Django基礎五之django模型層(二)多表操作

愛文飛翔發表於2019-07-25

Django基礎五之django模型層(二)多表操作

一 建立模型

表和表之間的關係

    一對一、多對一、多對多

# 作者表  比較常用的資訊放在這個表中
class Author(models.Model):
    name=models.CharField(max_length=32)
    age=models.IntegerField()
    authorDetail=models.OneToOneField(to='AuthorDetail')
    #與AuthorDetail建立一對一的關係,一對一的這個關係欄位寫在這兩個表的任意一個表裡面都可以,models.OneToOneField(to='AuthorDetail')就是foreignkey+unique,只不過不需要我們自己來寫引數了,並且orm會自動幫你給這個欄位名字拼上一個_id,資料庫中欄位名稱為authorDetail_id也可以寫成這樣# authorDetail = models.OneToOneField(to="AuthorDetail", to_field="id",on_delete=models.CASCADE)  on_delete=models.CASCADE級聯關係
    def __str__(self):
        return self.name
    
    
# 作者資訊表  不常用的作者資訊放這個表中
class AuthorDetail(models.Model):
    brithday=models.DateField()
    telephone=models.BigIntegerField()
    addr=models.CharField(max_length=64)
    def __str__(self):
        return self.addr
# 書籍出版社表
class Publish(models.Model):
    name=models.CharField(max_length=32)
    city=models.CharField(max_length=32)
    email=models.EmailField()
    def __str__(self):
        return self.name
    
# 書籍表
class Book(models.Model):
    title=models.CharField(max_length=32)
    publishDate=models.DateField()
    price=models.DecimalField(max_digits=5,decimal_palces=2)
    # 與Publish建立一對多的關係,外來鍵欄位建立在多的一方,欄位publish如果是外來鍵欄位,那麼它自動是int型別
    # foreignkey裡面可以加很多的引數,都是需要我們們學習的,慢慢來,to指向表,to_field指向你關聯的欄位,不寫這個,預設會自動關聯主鍵欄位,on_delete級聯刪除欄位名稱不需要寫成publish_id,orm在翻譯foreignkey的時候會自動給你這個欄位拼上一個_id,這個欄位名稱在資料庫裡面就自動變成了publish_id
    publish=models.Foreignkey(to='publish')
    
    # 與Author表建立多對多的關係,ManyToManyField可以建在兩個模型中的任意一個,自動建立第三張表,並且注意一點,你檢視book表的時候,你看不到這個欄位,因為這個欄位就是建立第三張表的意思,不是建立欄位的意思,所以只能說這個book類裡面有authors這個欄位屬性
    authors=models.ManyToManyField(to='Author')
    def __str__(self):
        return self.title
    
# 多對多的表關係,我們學mysql的時候是怎麼建立的,是不是手動建立一個第三張表,然後寫上兩個欄位,每個欄位外來鍵關聯到另外兩張多對多關係的表,orm的manytomany自動幫我們建立第三張表,兩種方式建立關係都可以,以後的學習我們暫時用orm自動建立的第三張表,因為手動建立的第三張表我們進行orm操作的時候,很多關於多對多關係的表之間的orm語句方法無法使用
# 如果你想刪除某張表,你只需要將這個表登出掉,然後執行那兩個資料庫同步指令就可以了,自動就刪除了。

#注意不管是一對多還是多對多,寫to這個引數的時候,最後後面的值是個字串,不然你就需要將你要關聯的那個表放到這個表的上面

多表間的增刪改查

新增表記錄---增

​ 操作前先簡單的錄入一些資料:還是create和save兩個方法,和單表的區別就是看看怎麼新增關聯欄位的資料

一對一
    方式一:
    new_author_detail=models.AuthorDetail.objects.create(
        birthday='1965-10-10',
        telephone='18335267641',
        addr='山西大同'
    )
    models.Author.objects.create(
        name='ll',
        age='53',
        authorDetail=new_author_detail,
    )

    方式二 常用
    obj=models.AuthorDetail.objects.filter(addr='山西大同').last()
    print(obj.id)
    models.Author.objects.create(
        name='mx',
        age='52',
        authorDetail_id=obj.id
    )

一對多
    obj=models.Publish.objects.get(id=3)
    models.Book.objects.create(
        title='故事會新編',
        publishDate='2019-9-10',
        price=30,
        # 方式一
        publish=obj
        # 方式二  常用
        publish_id=obj.id
    )

多對多
    方式一  常用
    book_obj=models.Book.objects.get(id=1)
    book_obj.authors.add(*[1,2])  #打散
    方式二
    author1=models.Author.objects.get(id=3)
    author2=models.Author.objects.get(id=4)
    book_obj=models.Book.objects.get(id=4)
    book_obj.authors.add(*[author1,author2])

刪除表記錄---刪

一對一
    models.AuthorDetail.objects.get(id=6).delete()
    models.Author.objects.get(id=5).delete()

一對多
    models.Publish.objects.get(id=1).delete()
    models.Book.objects.get(id=1).delete()

多對多
    book_obj = models.Book.objects.get(id=1)
    book_obj.authors.remove(*[1, 2]) #刪除
    book_obj.authors.clear()  #清空
    book_obj.authors.add(2,)  #新增
    book_obj.authors.set(['1','2']) #刪除然後更新

更新表記錄---改

一對一
    models.Author.objects.filter(id=1).update(
        name='安文',
        age=24,
    方式一
        # authorDetail_id=5,
    方式二
        authorDetail=models.AuthorDetail.objects.get(id=3)
    )

一對多
    models.Book.objects.filter(id=4).update(
    title='java核心',
    方式一
    publish_id=4,
    方式二
    publish=models.Publish.objects.get(id=2)
    )

多對多
    book_obj.authors.set(['1','2']) #刪除然後更新

查詢表記錄---查

基於物件的跨表查詢 -- 類似於子查詢

正向查詢和反向查詢

Django基礎五之django模型層(二)多表操作
| 關係屬性(欄位)寫在那個表中,從當前表(類)的資料去查詢它關聯表(類)中的資料叫正向查詢,反之叫反向查詢 |

#一對一
正向查詢
# 查詢yage的電話
author_obj=models.Author.objects.filter(name='yage').first()
print(author_obj.authorDetail)
print(author_obj.authorDetail.telepbone)
反向查詢
# 查詢這個電話 145523669874 是誰的
author_detail_obj=models.AuthorDertail.objects.get(telrphone=145523669872)
print(author_detail_obj.author)
print(author_detail_obj.author.name)
"""
        正向查詢Author_obj.authorDateil,物件.關聯屬性
    Author------------------------->AuthorDateil
    Author<-------------------------AuthorDateil
        反向查詢:AuthorDateil.author,物件.小寫類名

"""

#一對多
正向查詢
# 查詢某本書 java核心 的 出版社是哪個
book_obj=models.Book.objects.get(title='java核心')
print(book_obj.publish)
print(book_obj.publish.name)
反向查詢
#清華出版社出版的那些書
pub_pbj=models.Publish.objects.get(name='清華出版社')
print(pub_obj.book_set.all())

"""
        正向查詢 book_obj.publishs 物件.屬性
    Book------------------------------>>>Publish
    Book<<<------------------------------Publish
        反向查詢 pub_obj.book_set.all() 物件.表名小寫_set
    
"""

#多對多
正向查詢
# 查詢某本書 java核心 是誰出版的
book_obj=models.Book.objects.get(title='java核心')
print(book_obj.authors.all())
反向查詢
#查詢 yage 寫了哪些書
author_obj=models.Author.object.get(name='yage')
print(author_obj.book_set.all())
"""
        正向查詢 book_obj.authors.all() 物件.屬性
    book --------------->author
    book <---------------author
        反向查詢 author_obj.book_set.all() 物件.表名小寫_set
    
"""

基於雙下劃線的跨表查詢--連表查詢 join

Django 還提供了一種直觀而高效的方式在查詢(lookups)中表示關聯關係,它能自動確認 SQL JOIN 聯絡。要做跨關係查詢,就使用兩個下劃線來連結模型(model)間關聯欄位的名稱,直到最終連結到你想要的model 為止。

'''
    基於雙下劃線的查詢就一句話:正向查詢按欄位,反向查詢按表名小寫用來告訴ORM引擎join哪張表,一對一、一對多、多對多都是一個寫法,注意,我們寫orm查詢的時候,哪個表在前哪個表在後都沒問題,因為走的是join連表操作。
'''

# 一對一
#1. 查詢yage的電話
# 方式一 正向查詢
obj=models.Author.object.filter(name='yage').values('authorDetail__telepbone')
print(obj)
# 方式二 反向查詢
obj=models.AuthorDetail.objects.filter(author__name='yage').values('telephone')
print(obj)

#2. 誰的電話是 145523669874
obj=models.AuthorDetail.object.filter(telephon='145523669874').values('authors__name')
print(obj)
obj=models.Author.objects.filter(authorDetail__telephon='145523669874').values('name')
print(obj)

# 一對多
# 查詢某本書 java核心 的 出版社是哪個
obj=models.Book.objects.filter(name='java核心').values('publish__name')
obj=models.Publish.objects.filter(book__title='java核心').values('name')

# 清華出版社出版的那些書
obj=models.Publish.objects.filter(name='清華出版社').values('book__title')
print(obj)
obj=models.Book.object.filter(publish__name='清華出版社').values('title')
print(obj)


# 多對多
# 查詢某本書 java核心 是誰出版的
obj=models.Book.objects.filter(title='java核心').values('authors__name')
print(obj)
obj=models.Author.objects.filter(book__title='java核心').values('name')
print(obj)

# 查詢 yage 寫了哪些書
#方法一
obj=models.Author.objects.filter(name='yage').values('book__title')
print(obj)
#方法二
obj=models.Book.objects.filter(authors__name='yage').values('title')
print(obj)

#進階查詢一
#清華出版社 出版的書 以及作者姓名
#方法一
obj=models.Publish.objects.filter(name='清華出版社').values('book__title','book__authors__name')
print(obj)
#方法二
obj=models.Book.objects.filter(publish__name='清華出版社').values('title','authors__name')
print(obj)
#方法三
obj=models.Author.objects.filter(book__publish__name='清華出版社').values('name','book__title')
print(obj)


進階查詢二
#手機號以 14552 開頭的作者 出版過的所以書籍名稱 以及 出版社名稱
#方法一
obj=models.AuthorDetail.objects.filter(telephone__startswith='14552').values('author__book__title' , 'author__book__publish__name')
print(obj)
#方法二
obj=models.Author.objects.filter(authorDetail__telephone__startswith='14552').values('book__title','book__publish__name')
print(obj)
#方法三
obj=models.Book.objects.filter(authors__authorDetail__telephone__startswith='14552').values('authors__book__title','authors__book__publish__name')
print(obj)

反向查詢時,如果定義了related_name ,則用related_name替換 表名,例如:

publish = ForeignKey(Book, related_name='bookList')
# 練習: 查詢人民出版社出版過的所有書籍的名字與價格(一對多)
# 反向查詢 不再按表名:book,而是related_name:bookList
obj=models.Publish.objects.filter(name="清華出版社").values("bookList__title","bookList__price") 

聚合函式

from django.db.models import Avg,Min,Max ,Sum,Count

# 計算所有書籍的平均價格  圖書價格的最大值
#aggregate()是QuerySet 的一個終止子句,意思是說,它返回一個包含一些鍵值對的字典。鍵的名稱是聚合值的識別符號,值是計算出來的聚合值。鍵的名稱是按照欄位和聚合函式的名稱自動生成出來的。如果你想要為聚合值指定一個名稱,可以向聚合子句提供它。
obj=models.Book.objects.all().aggregate(a=Avg('price'),m=Max('price'))
    print(obj)  #{'a': 47.5, 'm': Decimal('78.00')}
    print(obj['m']-2)   #76.00

分組查詢

# 統計每個出版社出版的書籍的平均價格
ret=models.Book.objects.values('publish_id').annotate(a=Avg('price'))
 print(ret) #<QuerySet [{'publish_id': 2, 'a': 69.0}, {'publish_id': 3, 'a': 30.0}, {'publish_id': 4, 'a': 22.0}]>

ret=models.Publish.objects.annotate(a=Avg('book__price'))
print(ret)  #<QuerySet [<Publish: 山西出版社>, <Publish: 清華出版社>, <Publish: 江蘇出版社>]>

ret = models.Publish.objects.annotate(a=Avg('book__price')).values('name', 'a')
print(ret)  #<QuerySet [{'name': '山西出版社', 'a': 22.0}, {'name': '清華出版社', 'a': 69.0}, {'name': '江蘇出版社', 'a': 30.0}]>

F查詢

在上面所有的例子中,我們構造的過濾器都只是將欄位值與某個常量做比較。如果我們要對兩個欄位的值做比較,那該怎麼做呢?我們在book表裡面加上兩個欄位:評論數:comment,收藏數:good

#查詢點贊數大於評論數的書籍
ret=models.Book.objects.filter(good__gt=F('comment'))

# 查詢點贊數大於評論數+20的書籍     
#Django 支援 F() 物件之間以及 F() 物件和常數之間的加減乘除和取模的操作。
ret=models.Book.objects.filter(good__gt=F('comment')+20)
print(ret)  #<QuerySet [<Book: 今天是個好日子>, <Book: java核心>]>

#所有書籍的價格+20
models.Book.objects.all().update(price=F('price')+20)

#評論數大於100,和 ,點贊數大於100的
ret=models.Book.objects.filter(good__gt=100,comment__gt=100)
print(ret)  #<QuerySet [<Book: java核心>, <Book: 故事會新編>]>

Q查詢

Q 物件可以使用&(與)|(或)、~(非) 操作符組合起來。當一個操作符在兩個Q 物件上使用時,它產生一個新的Q 物件。

你可以組合&| 操作符以及使用括號進行分組來編寫任意複雜的Q 物件。同時,Q 物件可以使用~ 操作符取反,這允許組合正常的查詢和取反(NOT) 查詢:

#評論數大於100,或者 點贊數大於100的
ret=models.Book.objects.filter(Q(good__gt=100)|Q(comment__gt=100))
print(ret)  #<QuerySet [<Book: java核心>, <Book: 故事會新編>, <Book: LINUX學習>]>


#評論數大於100,或者 點贊數小於等於100的
ret = models.Book.objects.filter(~Q(good__gt=100) | Q(comment__gt=100))
    print(ret)

#評論數大於100,或者 點贊數大於100的 且 price='42'
#逗號連線的普通查詢條件放在最後
#查詢函式可以混合使用Q 物件和關鍵字引數。所有提供給查詢函式的引數(關鍵字引數或Q 物件)都將"AND”在一起。但是,如果出現Q 物件,它必須位於所有關鍵字引數的前面。例如:
ret = models.Book.objects.filter(Q(good__gt=100) | Q(comment__gt=100),price='42')
    print(ret)

#評論數大於100,或者 點贊數大於100的 且 price='42'
ret = models.Book.objects.filter(Q(good__gt=100) | Q(comment__gt=100) & Q(price='42'))  #&優先順序高

ret = models.Book.objects.filter(Q(Q(good__gt=100) | Q(comment__gt=100)) & Q(price='42'))   #|優先順序高

orm執行原生sql語句

在模型查詢API不夠用的情況下,我們還可以使用原始的SQL語句進行查詢。

  Django 提供兩種方法使用原始SQL進行查詢:一種是使用raw()方法,進行原始SQL查詢並返回模型例項;另一種是完全避開模型層,直接執行自定義的SQL語句。

  執行原生查詢

    raw()管理器方法用於原始的SQL查詢,並返回模型的例項:

    注意:raw()語法查詢必須包含主鍵

    這個方法執行原始的SQL查詢,並返回一個django.db.models.query.RawQuerySet 例項。 這個RawQuerySet 例項可以像一般的QuerySet那樣,通過迭代來提供物件例項。

ret=models.Publish.objects.raw('select * from app01_publish;')
print(ret)      #<RawQuerySet: select * from app01_publish;>
for i in ret:
   print(i.name)

# 直接執行自定義SQL
# django提供的介面中獲取資料庫連線,然後像使用pymysql模組一樣運算元據庫
from django.db import connection, connections
cursor = connection.cursor()  # cursor = connections['default'].cursor()
cursor.execute("""SELECT * from auth_user where id = %s""", [1])
ret = cursor.fetchone()

檢視orm生成原生sql語句

ret=models.Publish.objects.all()
print(ret)
from django.db.models import connection
print(connection.queries)

綜合查詢練習題

#1 查詢每個作者的姓名以及出版的書的最高價格
ret=models.Author.objects.values('name').annotate(max_price=Max('book__price'))
print(ret) #注意:values寫在annotate前面是作為分組依據用的,並且返回給你的值就是這個values裡面的欄位(name)和分組統計的結果欄位資料(max_price)
#ret=models.Author.objects.annotate(max_price=Max('book__price')).values('name','max_price')#這種寫法是按照Author表的id欄位進行分組,返回給你的是這個表的所有model物件,這個物件裡面包含著max_price這個屬性,後面寫values方法是獲取的這些物件的屬性的值,當然,可以加雙下劃線來連表獲取其他關聯表的資料,但是獲取的其他關聯表資料是你的這些model物件對應的資料,而關聯獲取的資料可能不是你想要的最大值對應的那些資料

# 2 查詢作者id大於2作者的姓名以及出版的書的最高價格
ret=models.Author.objects.filter(id__gt=2).annotate(max_price=Max('book__price')).values('name','max_price')#記著,這個values取得是前面呼叫這個方法的表的所有欄位值以及max_pirce的值,這也是為什麼我們取關聯資料的時候要加雙劃線的原因
print(ret)

#3 查詢作者id大於2或者作者年齡大於等於20歲的女作者的姓名以及出版的書的最高價格
ret=models.Author.objects.filter(Q(id__gt=2)|Q(age__gte=20),sex='female').annotate(max_price=Max('book__price')).values('name','max_price')

#4 查詢每個作者出版的書的最高價格 的平均值
ret=models.Author.objects.values('id').annotate(max_price=Max('book__price')).aggregate(Avg('max_price')) #{'max_price__avg': 555.0} 注意,aggregate是queryset的終止句,得到的是字典
ret=models.Author.objects.annotate(max_price=Max('book__price')).aggregate(Avg('max_price')) #{'max_price__avg': 555.0} 注意,aggregate是queryset的終止句,得到的是字典

補充

url.py 中的url(r'^admin/', admin.site.urls)路由操作admin.py

app應用中admin.py

from django.contrib import admin
from app01 import models
# Register your models here.
admin.site.register(models.Book)
admin.site.register(models.Author)
admin.site.register(models.AuthorDetail)
admin.site.register(models.Publish)

點選Tools 中run manage.py task 執行createsuperuser建立一個超級使用者(username和password),然後可以訪問http://127.0.0.1:8000/admin/ ,可以直接運算元據庫進行增刪改查。

Django基礎五之django模型層(二)多表操作
| 訪問http://127.0.0.1:8000/admin/ |

Django基礎五之django模型層(二)多表操作

相關文章