溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

Django之模型層多表操作的實(shí)現(xiàn)

發(fā)布時(shí)間:2020-10-10 18:41:25 來源:腳本之家 閱讀:150 作者:W的一天 欄目:開發(fā)技術(shù)

一、創(chuàng)建模型

1,一對(duì)多關(guān)系

一本書只有一個(gè)出版社,一個(gè)出版社可以出版多本書,從而書與出版社之間就構(gòu)成一對(duì)多關(guān)系,書是‘多'的一方,出版社是‘一'的一方,我們?cè)诮⒛P偷臅r(shí)候,把外鍵寫在‘多'的一方,即我們要把外鍵寫在book類。

class Book(models.Model):
 name=models.CharField(max_length=15)
 price=models.IntegerField()
 publish=models.ForeignKey('Publish',on_delete=models.CASCADE) #這就是外鍵,其實(shí)是有三個(gè)參數(shù)的,第二參數(shù)是指向的字段,此處可以省略,他會(huì)自動(dòng)指向id字段

class Publish(models.Model):
 name=models.CharField(max_length=15)
 addr=models.CharField(max_length=15)
 phone=models.IntegerField()

在創(chuàng)建模型時(shí)不用創(chuàng)建id字段,在makemigrations命令輸入之后,它會(huì)在migrations文件夾下生產(chǎn)一個(gè)py文件記錄models.py里面所有的改動(dòng),在記錄的時(shí)候就會(huì)自動(dòng)給你加上自增長(zhǎng)的主鍵字段id。

2,多對(duì)多關(guān)系

一本書可以有多個(gè)作者,一個(gè)作者可以寫多本書,從而書和作者就構(gòu)成了多對(duì)多的關(guān)系,我們?cè)趧?chuàng)建模型的時(shí)候,把多對(duì)多關(guān)系寫在其中的任何一張表都可以。

class Book(models.Model):
 name=models.CharField(max_length=15)
 price=models.IntegerField()
 publish=models.CharField(max_length=15)
 author=models.ManyToManyField('Author',db_table='book_author')  這是創(chuàng)建關(guān)系表的代碼,由于是寫在book模型中的,所以第一個(gè)參數(shù)為另一張表Author,第二個(gè)參數(shù)為把關(guān)系表的名字改為‘book_author',如果不寫,
名字會(huì)是應(yīng)用名_本模型名的小寫_另一張模型名的小寫。如‘a(chǎn)pp_book_author'
 class Meta:  這是把表名改為‘book',如果不寫,表名為APP名_模型名,如'app_book'
  db_table='book'

class Author(models.Model):
 name=models.CharField(max_length=15)
 age=models.IntegerField()
 class Meta:
  db_table='author'
在創(chuàng)建第三張模型的時(shí)候也不用指定book的id和author的id,它會(huì)自動(dòng)把兩個(gè)模型的id字段寫進(jìn)去的

3,一對(duì)一關(guān)系

一個(gè)作者只能對(duì)應(yīng)一個(gè)作者詳細(xì)信息表,他們之間就是一對(duì)一關(guān)系,這和多對(duì)多一樣的,關(guān)系寫在哪張表都是可以的

class Author(models.Model):
 name=models.CharField(max_length=15)
 age=models.IntegerField()
 author_info=models.OneToOneField('Author_Info',on_delete=models.CASCADE)  這是一對(duì)一關(guān)系創(chuàng)建,第二參數(shù)是,自動(dòng)跟隨刪除,當(dāng)作者不在了,隨即作者的信息也會(huì)刪除
 class Meta:
  db_table='author'
  
class Author_Info(models.Model):
 gf_name=models.CharField(max_length=10)
 telephone=models.IntegerField()
 ShenFenZheng=models.IntegerField()

4,在此處我們可以使用Django的database:db.sqlite3

步驟如下:

Django之模型層多表操作的實(shí)現(xiàn)

Django之模型層多表操作的實(shí)現(xiàn)

5,數(shù)據(jù)庫(kù)遷移

Django之模型層多表操作的實(shí)現(xiàn)

由于Django默認(rèn)就是db.sqlite,所以我們不用去settings配置,也不需要在項(xiàng)目的__init__.py里寫代碼,現(xiàn)在只需要輸入兩條數(shù)據(jù)庫(kù)遷移指令就行了

Django之模型層多表操作的實(shí)現(xiàn)

點(diǎn)擊這里之后進(jìn)入:

Django之模型層多表操作的實(shí)現(xiàn)

在這里數(shù)輸入指令,就不需要寫python manage.py了,因?yàn)橐呀?jīng)進(jìn)入到manage.py

現(xiàn)在輸入makemigrations指令 #記錄models.py文件發(fā)生的改變,然后在migrations文件夾下生產(chǎn)一個(gè)py文件,里面記錄發(fā)生的變化

再輸入migrate指令 #執(zhí)行migrations文件下新變動(dòng)的遷移文件,去更新數(shù)據(jù)庫(kù)

到此,表就創(chuàng)建成功了。

二、添加表記錄

1,一對(duì)多關(guān)系

之前我們創(chuàng)建了Book表和Publish表,兩者就是一對(duì)多的關(guān)系,Book表是‘多'的一方,所以外鍵字段在Book表,Book表添加和之前的不一樣,而‘一'的Publish表就是一張單表,和之前的一樣,所以我們只要學(xué)習(xí)‘多'的一張Book表的添加就行了。添加表記錄有兩種方式。

1.1 按models.py里面Book類的屬性來添加

pub=Publish.objects.all().filter(id=1).first()  #首先找到id為1的Publish對(duì)象
book=Book.objects.create(name=name,price=price,publish=pub,pub_date=pub_date) #然后把這一對(duì)象賦值給Book類的publish屬性

1.2 按數(shù)據(jù)庫(kù)里面Book表的字段來添加

book=Book.objects.create(name=name,price=price,publish_id=1,pub_date=pub_date) 
#直接把Publish的id賦值給book表的publish_id就行了

2,多對(duì)多關(guān)系

之前我們創(chuàng)建了Book表和Author表,兩者就是多對(duì)多關(guān)系,我是把多對(duì)多關(guān)系寫在book表中的,所以從book去添加關(guān)聯(lián)關(guān)系是正向的。

# 當(dāng)前生成的書籍對(duì)象
book_obj=Book.objects.create(title="追風(fēng)箏的人",price=200,publishDate="2012-11-12",publish_id=1)
# 為書籍綁定的作者對(duì)象
a1=Author.objects.filter(id=2).first() # 在Author表中主鍵為2的紀(jì)錄
a2=Author.objects.filter(id=1).first() # 在Author表中主鍵為1的紀(jì)錄


# 綁定多對(duì)多關(guān)系,即向關(guān)系表book_authors中添加紀(jì)錄,正向用屬性,反向用表名_set

第一種,以Book為基表,因?yàn)槎鄬?duì)多關(guān)系是寫在Book中的,所以現(xiàn)在屬于正向關(guān)聯(lián),用屬性
book_obj.author.add(author1,author2) #這是給book_obj對(duì)象綁定上author1和author2兩個(gè)對(duì)象。這里的author不是Author小寫,而是Book類的一個(gè)屬性
第二種,以Author為基表,因?yàn)槎鄬?duì)多關(guān)系不是寫在Author表,所以屬于反向關(guān)聯(lián),用表名小寫_set
author_obj.book_set.add(book1,book2) #這是給author_obj對(duì)象綁定上book1和book2兩個(gè)對(duì)象,但是這里book可不是Author類的屬性,而且也沒有這個(gè)屬性,它是Book表小寫后得到的
關(guān)系表的方法:
1,add()方法
參數(shù)可以是可以是n個(gè)模型類對(duì)象,如上面的寫法
也可以是一個(gè)queryset集合,如author_list=Author.objects.filter(id__gt=2),這是找出id大于2的作者集合
book_obj.author.add(*author_list)
還可以是一個(gè)主鍵列表,如下面的寫法
book_obj.author.add(*[1,3,4])

2,remove()方法,移出關(guān)系方法
現(xiàn)在book1關(guān)聯(lián)著author1和author2兩個(gè)作者
book1.author.remove(author1) #此時(shí)book1就關(guān)聯(lián)author2一個(gè)作者
反向也行author2.book_set.remove(book2) #把a(bǔ)uthor2的關(guān)聯(lián)書籍book2給移出

3,clear()方法,清空關(guān)系方法
book1.author.clear()  #把book1的所有關(guān)聯(lián)關(guān)系給刪除,現(xiàn)在book1就沒有關(guān)聯(lián)作者了
author1.book_set.clear() 一樣的,把a(bǔ)uthor1的所有關(guān)聯(lián)書籍的關(guān)聯(lián)關(guān)系刪除

4,set()方法,先把關(guān)聯(lián)關(guān)系清空,再添加關(guān)聯(lián)關(guān)系
假如book1關(guān)聯(lián)著author1
book1.author.set(author2) 先把與author1的關(guān)聯(lián)關(guān)系刪除,然后再建立與author2的關(guān)聯(lián)關(guān)系
假如author3關(guān)聯(lián)著book1
author3.book_set.set(book2) 先把關(guān)聯(lián)關(guān)系清空,再建立與book2的關(guān)聯(lián)關(guān)系

5,=方法,賦值一個(gè)可迭代對(duì)象,關(guān)聯(lián)關(guān)系會(huì)被整體替換
假如book1關(guān)聯(lián)author1
new_list=[author2,author3]
book1.author=new_list  這也會(huì)先把關(guān)聯(lián)關(guān)系清空,然后把列表里的對(duì)象與book1建立關(guān)聯(lián)關(guān)系

3,一對(duì)一關(guān)系

之前創(chuàng)建的Author表和Author_Info表之間就是一對(duì)一關(guān)系,我把關(guān)聯(lián)字段寫在了Author表中。

給Author類的屬性賦值
info=Author_Info.objects.create(gf_name=gf_name,telephone=telephone,ShenFenZheng=ShenFenZheng) #這是創(chuàng)建了一條Author_Info記錄,info就是一個(gè)Author_info對(duì)象
Author.objects.create(name=name,age=age,author_info=info)  把創(chuàng)建的info對(duì)象賦值給author_info屬性

和一對(duì)多一樣,也可以使用Author表的字段賦值
Author.objects.create(name=name,age=age,author_info_id=2)

三、基于對(duì)象的跨表查詢(就是子查詢)

1,一對(duì)多查詢(Book與Publish)

1.1 正向查詢(按屬性:publish)

# 查詢主鍵為1的書籍的出版社所在的城市
book_obj=Book.objects.filter(pk=1).first()
# book_obj.publish 是主鍵為1的書籍對(duì)象關(guān)聯(lián)的出版社對(duì)象
print(book_obj.publish.city)  

1.2 反向查詢(按表名小寫_set:book_set)

publish=Publish.objects.get(name="蘋果出版社")
#publish.book_set.all() : 與蘋果出版社關(guān)聯(lián)的所有書籍對(duì)象集合
book_list=publish.book_set.all() 
for book_obj in book_list:
  print(book_obj.title)

2,一對(duì)一查詢(Author與Author_Info)

2.1 正向查詢(按屬性:author_info)

egon=Author.objects.filter(name="egon").first()
print(egon.authorDetail.telephone)

2.2 反向查詢(按表名小寫:author)

# 查詢所有住址在北京的作者的姓名
 
authorDetail_list=AuthorDetail.objects.filter(addr="beijing")
for obj in authorDetail_list:
  print(obj.author.name)

3,多對(duì)多查詢(Author與Book)

3.1 正向查詢(按屬性:author)

# 金瓶眉所有作者的名字以及手機(jī)號(hào)
 
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)

3.2 反向查詢(按表名小寫_set:book_set)

# 查詢egon出過的所有書籍的名字
 
 author_obj=Author.objects.get(name="egon")
 book_list=author_obj.book_set.all()  #與egon作者相關(guān)的所有書籍
 for book_obj in book_list:
  print(book_obj.title)

4,related_name設(shè)置

可以通過Foreignkey和MangToMangField的定義中設(shè)置related_name的值來復(fù)寫foo_set的名稱。
publish=ForeignKey('Publish',related_name='booklist') #這樣之后,反向就不用表名_set,就用booklist
# 查詢 人民出版社出版過的所有書籍
publish=Publish.objects.get(name="人民出版社")
book_list=publish.bookList.all() # 與人民出版社關(guān)聯(lián)的所有書籍對(duì)象集合

四、基于雙下劃線的跨表查詢

Django還提供了一種直觀而高效的方式在查詢中表示關(guān)聯(lián)關(guān)系,它能自動(dòng)確認(rèn)sql join聯(lián)系。要做跨關(guān)系查詢,就使用兩個(gè)下劃線來鏈接模型間關(guān)聯(lián)字段的名稱,直到最終連接到想要的model為止。

正向查詢按屬性,反向查詢按表名小寫

1,一對(duì)多查詢

# 練習(xí): 查詢蘋果出版社出版過的所有書籍的名字與價(jià)格(一對(duì)多)

 # 正向查詢 按字段:publish

 queryResult=Book.objects
            .filter(publish__name="蘋果出版社")
            .values_list("title","price")

 # 反向查詢 按表名:book

 queryResult=Publish.objects
              .filter(name="蘋果出版社")
              .values_list("book__title","book__price")

2,多對(duì)多查詢

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

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

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

3,一對(duì)一關(guān)系

# 查詢alex的手機(jī)號(hào)
 
 # 正向查詢
 ret=Author.objects.filter(name="alex").values("authordetail__telephone")

 # 反向查詢
 ret=AuthorDetail.objects.filter(author__name="alex").values("telephone")

4,進(jìn)階練習(xí)

# 練習(xí): 查詢?nèi)嗣癯霭嫔绯霭孢^的所有書籍的名字以及作者的姓名


 # 正向查詢
 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")


# 練習(xí): 手機(jī)號(hào)以151開頭的作者出版過的所有書籍名稱以及出版社名稱


 # 方式1:
 queryResult=Book.objects
            .filter(authors__authorDetail__telephone__regex="151")
            .values_list("title","publish__name")
 # 方式2: 
 ret=Author.objects
    .filter(authordetail__telephone__startswith="151")
    .values("book__title","book__publish__name")

五、聚合查詢與分組查詢

1,聚合

aggregate(*args,**kwargs)是Queryset的一個(gè)終止子句,意思是說,它返回一個(gè)包含一些鍵值對(duì)的字典。鍵的名稱是按照字段和聚合函數(shù)的名稱自動(dòng)生成出來的
計(jì)算所有圖書的平均價(jià)格
from django.db.models import Avg
Book.objects.all().aggregate(Avg('price'))
結(jié)果:{'price__avg': 34.35}
如果你想要為聚合值指定一個(gè)名稱,可以向聚合函數(shù)前面用一個(gè)變量名來接收,此時(shí),鍵的名稱就變?yōu)榻邮盏淖兞棵?Book.objects.aggregate(average_price=Avg('price'))
{'average_price': 34.35}
在終止子句里面可以放多個(gè)聚合函數(shù),得到結(jié)果就是有多個(gè)鍵值對(duì)
from django.db.models import Avg, Max, Min
Book.objects.aggregate(Avg('price'), Max('price'), Min('price'))
{'price__avg': 34.35, 'price__max': Decimal('81.20'), 'price__min': Decimal('12.99')}
aggregate()只能對(duì)一個(gè)分組有用,對(duì)于按某字段分完組后的n個(gè)組,此時(shí)aggregate()就不能循環(huán)對(duì)每個(gè)分組作用,它只會(huì)得到第一組的結(jié)果

2,分組

2.1 單表分組查詢

查詢每一個(gè)部門名稱以及對(duì)應(yīng)的員工數(shù)
emp:
id name age salary dep
 alex 12 2000  銷售部
 egon 22 3000  人事部
 wen 22 5000  人事部
emp.objects.values('dep').annotate(c=Count('*'))
values(‘dep')就是按‘dep'進(jìn)行分組
annotate()對(duì)每個(gè)分組的進(jìn)行操作

2.2 多表分組查詢

每一個(gè)出版社的名稱和出版過的書籍個(gè)數(shù)
Publish.objects.values('name').annotate(c=Count('book'))  #首先讀整個(gè)語(yǔ)句,當(dāng)讀到‘book'時(shí),就會(huì)把兩個(gè)表連起來,然后在按Publish.name分組
跨表分組查詢本質(zhì)就是將關(guān)聯(lián)表join成一張表,然后再按單表的思路進(jìn)行分組查詢

還有一種寫法:
publishlist=Publish.objects.annotate(c=Count('book')) 這相當(dāng)于給Publish表添加了一個(gè)‘c'字段。首先也是把兩張表連起來,以Publish分組,計(jì)算每個(gè)Publish的書籍?dāng)?shù)量
publishlist是一個(gè)queryset對(duì)象集合,里面放的是publish模型類對(duì)象,只是現(xiàn)在的對(duì)象比之前多了一個(gè)‘c'字段
for publish in publishlist:
  print(publish.name,publish.c) 利用for循環(huán)就可以遍歷出每個(gè)模型類對(duì)象,然后用句點(diǎn)符‘.'就可以取得任何字段的值
我們也可以不用for循環(huán),直接用values_list()就可以實(shí)現(xiàn),如上面的for循環(huán)可以寫成:values_list('name','c')

統(tǒng)計(jì)每一本書的作者個(gè)數(shù)
Book.objects.annotate(c=Count('author')).values_list('name','c')

filter()放在annotate()前面就是相當(dāng)于where
統(tǒng)計(jì)每一本以py開頭的書籍的作者的個(gè)數(shù):
Book.objects.filter(name__startswith='py').annotate(c=Count('author')).values_list('name','c')

filter()放在annotate()后面就相當(dāng)于having
統(tǒng)計(jì)作者個(gè)數(shù)大于1的書籍:
Book.objects.annotate(c=Count('author')).filter(c__gt=1).value_list('name','c')

根據(jù)書籍的作者數(shù)來排序:
Book.objects.annotate(c=Count('author')).orderby('c')

六、F查詢與Q查詢

1,F(xiàn)查詢

在之前,對(duì)象的字段只能放在比較符的前面,比如filter(id__gt=2),但現(xiàn)在,有一個(gè)表,有生物成績(jī)ss字段和物理成績(jī)ws字段,統(tǒng)計(jì)物理成績(jī)高于生物成績(jī)的學(xué)生:
student.objects.filter(ws__gt=ss) 這樣寫肯定是報(bào)錯(cuò)的,因?yàn)樽侄螌懺诹吮容^符后面,但此時(shí)我們借助F查詢就可以不報(bào)錯(cuò)了,正確寫法如下:
student.objcts.filter(ws__gt=F('ss')) F('ss')此時(shí)就是把ss字段的值取出來,就相當(dāng)于一個(gè)純數(shù)字了,可以進(jìn)行加減乘除操作

查詢物理成績(jī)大于生物成績(jī)兩倍的學(xué)生
student.objects.filter(ws__gt=F('ss')*2)

把每個(gè)學(xué)生的物理成績(jī)加上10分:
student.objects.all().update(ws=F('ws')+10)

2,Q查詢

之前我們?cè)谟胒ilter()時(shí),可以用‘,'表示與關(guān)系,但沒有或關(guān)系,現(xiàn)在我們用Q查詢就可以實(shí)現(xiàn)或關(guān)系
Book.objects.filter(Q(id__gt=2)|Q(title__startswith='p')) 過濾出id大于2或者以‘p'開頭的
Book.objects.filter(Q(id__gt=2)&Q(title__startswith='p')) 過濾出id大于2且以‘p'開頭的
Book.objects.filter(Q(id__gt=2)|~Q(title__startswith='p')) 過濾出id大于2或不以‘p'開頭的

Q查詢可以和關(guān)鍵字參數(shù)混用,但Q()在前面
Book.objects.filter(Q(pub_date__year=2017)|Q(pub_date__year=2016),pub_date__month=2)過濾出2017年2月份或2016年2月份的書籍

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持億速云。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI