1. 程式人生 > 實用技巧 >Django模型層:單表、多表操作、F與Q查詢

Django模型層:單表、多表操作、F與Q查詢

DJango模型層

單表操作

使用有幾步:

  1. settings.py中配置連線資料庫的地址,埠
  2. 在init.py中使用pymysql
  3. 在models.py裡寫類,一個類對應一個表,寫屬性
  4. 資料庫遷移命令,第一個命令是紀錄,第二個命令才是真正的資料庫同步
  5. 匯入models到views,開始使用
# models.py
# 建立單表
from django.db import models
class User(models.Model):
	# 類名就是表名,必須繼承models.Model
	name = models.CharField(max_length=32)
	# charfield必須指定最大長度,否則報錯
	age = models.IntegerField()
	# 建立數字欄位
	register_time = models.DateField(auto_now=True)
	# 建立日期物件,這個欄位有兩個重要的引數	
	# auto_now 每次操作資料的時候自動更新該欄位為
	# auto_now_add,建立的時候紀錄時間,只要不人為修改,就會一直不變

當沒有建立主鍵欄位(primary_key=True)的時候,orm會自動幫你建立一個名為id的主鍵欄位

執行兩條資料庫遷移命令,接下來就可以在檢視中或測試指令碼中使用了

# views.py
from app01 import models
# 先從應用中匯入模型
...

增加資料

# 第一種
res = models.User.objects.create(name='aaa',age=21,register_time='2020-02-22')
# models.表名.objects.create() 括號裡面填欄位,日期欄位可以直接寫日期也可以填一個日期物件
# 這個方法有一個返回值,就是被建立的物件本身

# 第二種
user_obj = models.User(name='aaa',age=21,register_time='2020-02-22')
user_obj.save()

刪除資料

res = models.User.objects.filter(pk=2).delete()
# 查詢當前表主鍵為2的欄位,刪除
user_obj = models.User.objects.filter(pk=2).first()
user_obj.delete()

修改資料

user_obj = models.User.objects.filter(pk=3).update(name='bbb')

orm其他的api

<1> all():                  查詢所有結果
  
<2> filter(**kwargs):       它包含了與所給篩選條件相匹配的物件
  
<3> get(**kwargs):          返回與所給篩選條件相匹配的物件,返回結果有且只有一個,如果符合篩選條件的物件超過一個或者沒有都會丟擲錯誤。
  
<4> exclude(**kwargs):      它包含了與所給篩選條件不匹配的物件
 
<5> order_by(*field):       對查詢結果排序('-id')
  
<6> reverse():              對查詢結果反向排序
  
<8> count():                返回資料庫中匹配查詢(QuerySet)的物件數量。
  
<9> first():                返回第一條記錄
  
<10> last():                返回最後一條記錄
  
<11> exists():              如果QuerySet包含資料,就返回True,否則返回False
 
<12> values(*field):        返回一個ValueQuerySet——一個特殊的QuerySet,執行後得到的並不是一系列
                            model的例項化物件,而是一個可迭代的字典序列
<13> values_list(*field):   它與values()非常相似,它返回的是一個元組序列,values返回的是一個字典序列
 
<14> distinct():            從返回結果中剔除重複紀錄

基於雙下劃線的模糊查詢

Book.objects.filter(price__in=[100,200,300])
Book.objects.filter(price__gt=100)  # 大於
Book.objects.filter(price__lt=100)  # 小於
Book.objects.filter(price__gte=100)  # 大於等於
Book.objects.filter(price__lte=100)  # 小於等於
Book.objects.filter(price__range=[100,200]) 
Book.objects.filter(title__contains="python")
Book.objects.filter(title__icontains="python")
Book.objects.filter(title__startswith="py")
Book.objects.filter(pub_date__year=2012)

多表操作

表與表之間的關係有一對一,一對多,多對多

以圖書,出版社,作者舉例

一本圖書只能有一個出版社,一個出版社出版多本書

  • 書----出版社 :一對多

一本書有多個作者,一個作者可以寫多本書

  • 書----作者 : 多對多關係

作者與作者資訊是一對一關係

建立外來鍵

一對多

models.ForeignKey()

# 書與出版社一對多外來鍵
publish = models.ForeignKey(to='Publish',to_field='nid',on_delete=models.CASCADE)
# 建立外來鍵欄位publish,其中
# to:關聯的表名
# to_field:要關聯的表的欄位名稱,與publish表中的nid欄位關聯
# on_delete,刪除表中的資料時,當前表與其關聯的資料的行為
	# models.CASCADE,刪除關聯資料,與之關聯也刪除
    # models.SET_NULL,刪除關聯資料,與之關聯的值設定為null(前提FK欄位需要設定為可空)
    # models.SET_DEFAULT,刪除關聯資料,與之關聯的值設定為預設值(前提FK欄位需要設定預設值)

一對一

models.OneToOneField()

# 作者資訊與作者表,一對一關係
author_detail = models.OneToOneField(to='AuthorDatail',to_field='nid',unique=True,on_delete=models.CASCADE)
# 關聯的表明:AuthorDetail,關聯的欄位nid,該欄位唯一,刪除表中的資料,authordetail中對應的author也刪除

多對多

authors=models.ManyToManyField(to='Author')

orm多對多關係並沒有真正的關聯,宣告一個ManyToMany欄位後,orm會幫我們建立一張中間表

多表新增表紀錄

一對多

方式1:
publish_obj=Publish.objects.get(nid=1)
book_obj=Book.objects.create(title="",publishDate="2012-12-12",price=100,publish=publish_obj)
  
方式2:
book_obj=Book.objects.create(title="",publishDate="2012-12-12",price=100,publish_id=1)

多對多

# 當前生成的書籍物件
book_obj=Book.objects.create(title="追風箏的人",price=200,publishDate="2012-11-12",publish_id=1)
# 為書籍繫結的做作者物件
yuan=Author.objects.filter(name="yuan").first()
# 在Author表中主鍵為2的紀錄
egon=Author.objects.filter(name="alex").first()
# 在Author表中主鍵為1的紀錄

# 繫結多對多關係,即向關係表book_authors中新增紀錄
book_obj.authors.add(yuan,egon)
#  將某些特定的 model 物件新增到被關聯物件集合中。=======book_obj.authors.add(*[])

多對多關係常用api

book_obj.authors.remove()      # 將某個特定的物件從被關聯物件集合中去除
# book_obj.authors.remove(*[])
book_obj.authors.clear()       #清空被關聯物件集合
book_obj.authors.set()         #先清空再設定 

基於物件的跨表查詢

查詢兩次,先拿到一個物件,再點再查一次得到結果

一對多

一對多的關係,外來鍵欄位建在多的一方,正向查詢就是從有外來鍵欄位的這邊查另一邊:書表查出版社表

# 查詢主鍵為1的書籍的出版社所在的城市
book_obj=Book.objects.filter(pk=1).first()
# book_obj.publish 是主鍵為1的書籍物件關聯的出版社物件
print(book_obj.publish.city)

正向查詢,直接使用點 . 即可

publish=Publish.objects.get(name="蘋果出版社")
#publish.book_set.all() : 與蘋果出版社關聯的所有書籍物件集合
book_list=publish.book_set.all()    
for book_obj in book_list:
	print(book_obj.title)

反向查詢,使用 要查詢的表_set() 查詢

一對一查詢

正向,反向都用點方法

# 正向查詢
egon=Author.objects.filter(name="egon").first()
print(egon.authorDetail.telephone)
# 通過作者表查詢作者資訊表的電話號碼
# 反向查詢
# 查詢所有住址在北京的作者的姓名
authorDetail_list=AuthorDetail.objects.filter(addr="beijing")
for obj in authorDetail_list:
     print(obj.author.name)

多對多查詢

# 眉所有作者的名字以及手機號
book_obj=Book.objects.filter(title="眉").first()
authors=book_obj.authors.all()
for author_obj in authors:
     print(author_obj.name,author_obj.authorDetail.telephone)
# 查詢egon出過的所有書籍的名字
author_obj=Author.objects.get(name="egon")
book_list=author_obj.book_set.all()
#與egon作者相關的所有書籍
for book_obj in book_list:
    print(book_obj.title)

基於雙下劃線的跨表查詢

使用兩個下劃線來連結模型(model)間關聯欄位的名稱,直到最終連結到你想要的model 為止

一對多

表名__欄位名

# 練習:  查詢蘋果出版社出版過的所有書籍的名字與價格(一對多)
# 正向查詢 按欄位:publish
queryResult=Book.objects.filter(publish__name="蘋果出版社").values_list("title","price")
# 出版社表__出版社名

# 反向查詢 按表名:book
queryResult=Publish.objects.filter(name="蘋果出版社").values_list("book__title","book__price")
# 查詢的本質一樣,就是select from的表不一樣

多對多

# 練習: 查詢alex出過的所有書籍的名字(多對多)

# 正向查詢 按欄位:authors:
queryResult=Book.objects.filter(authors__name="yuan").values_list("title")

# 反向查詢 按表名:book
queryResult=Author.objects.filter(name="yuan").values_list("book__title","book__price")

連續跨表案例

# 查詢人民出版社出版過的所有書籍的名字以及作者的姓名

# 正向查詢
queryResult=Book.objects.filter(publish__name="人民出版社").values_list("title","authors__name")
# 反向查詢
queryResult=Publish.objects.filter(name="人民出版社").values_list("book__title","book__authors__age","book__authors__name")

F與Q查詢

F查詢

F() 的例項可以在查詢中引用欄位,來比較同一個 model 例項中兩個不同欄位的值

from django.db.models import F
# 需要先匯入F
Book.objects.filter(commnetNum__lt=F('keepNum'))
# book表中,commentNum<keepNum的資料

Django 支援F()物件之間以及F()物件和常數之間的加減乘除和取模的操作

# 查詢評論數大於收藏數2倍的書籍
Book.objects.filter(commnetNum__lt=F('keepNum')*2)

修改操作也可以使用F函式,比如將每一本書的價格提高30元:

Book.objects.all().update(price=F("price")+30) 

Q查詢

filter() 等方法中的關鍵字引數查詢都是一起進行“AND” 的。 如果你需要執行更復雜的查詢(例如OR 語句),你可以使用Q 物件

from django.db.models import Q
Q(title__startswith='Py')

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

bookList=Book.objects.filter(Q(authors__name="yuan")|Q(authors__name="egon"))

等同於下面的SQL WHERE 子句:

WHERE name ="yuan" OR name ="egon"

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

查詢函式可以混合使用Q 物件和關鍵字引數。所有提供給查詢函式的引數(關鍵字引數或Q 物件)都將“AND”在一起。但是,如果出現Q 物件,它必須位於所有關鍵字引數的前面。例如:

bookList=Book.objects.filter(Q(publishDate__year=2016) | Q(publishDate__year=2017), title__icontains="python")

一些例子

 # 查詢評論數大於閱讀數的書籍
 from django.db.models import F,Q
 ret=Book.objects.filter(commit_num__gt=F('reat_num'))
 print(ret)
 # 把所有書籍的價格加10
 Book.objects.all().update(price=F('price')+10)

 # ----Q函式,描述一個與,或,非的關係
 # 查詢名字叫紅樓夢或者價格大於100的書
 ret=Book.objects.filter(Q(name='紅樓夢')|Q(price__gt=100))
 print(ret)
 # 查詢名字叫紅樓夢和價格大於100的書
 ret = Book.objects.filter(Q(name='紅樓夢') & Q(price__gt=100))
 print(ret)
 # # 等同於
 ret2=Book.objects.filter(name='紅樓夢',price__gt=100)
 print(ret2)
 # 也可以Q套Q
 # 查詢名字叫紅樓夢和價格大於100  或者 nid大於2
 ret=Book.objects.filter((Q(name='紅樓夢') & Q(price__gt=100))|Q(nid__gt=2))
 print(ret)
 # ----非
 ret=Book.objects.filter(~Q(name='紅樓夢'))
 print(ret)
 # Q和鍵值對聯合使用,但是鍵值對必須放在Q的後面(描述的是一個且的關係)
 # 查詢名字不是紅樓夢,並且價格大於100的書
 ret=Book.objects.filter(~Q(name='紅樓夢'),price__gt=100)
 print(ret)