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']) #刪除然後更新
查詢表記錄---查
基於物件的跨表查詢 -- 類似於子查詢
正向查詢和反向查詢
| 關係屬性(欄位)寫在那個表中,從當前表(類)的資料去查詢它關聯表(類)中的資料叫正向查詢,反之叫反向查詢 |
#一對一
正向查詢
# 查詢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 ,則用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/ ,可以直接運算元據庫進行增刪改查。
| 訪問http://127.0.0.1:8000/admin/ |