以前提到過Django自帶一個叫作sqlite3的小型數據庫,當咱們作本地測試時,能夠直接在sqlite3上測試。不過該數據庫是小型的,在有些細節可能體驗不大好,好比用ORM用雙下劃線查詢語法時,使用__contains和__icontains的結果是同樣的,由於sqlite3不管怎麼樣都不區分大小寫,並且它還會自動把日期格式的字段轉爲時間戳(該體驗賊差)。python
不過除此以外還好,目前也沒發現其餘問題,作一些數據的小測試仍是綽綽有餘的。mysql
1.1 項目settings.py文件git
鏈接其餘數據庫時,咱們須要修改settings.py裏面的相關配置,不過默認的配置就是sqlite3的,因此咱們不須要修改涉及數據庫的配置。sql
回憶一下,鏈接其餘數據庫時(以鏈接myqsl數據庫爲例),應該修改成如下配置:數據庫
1.2 修改應用下或項目下的的__init__.py文件django
跟鏈接其餘數據庫同樣,須要在__init__.py中加入替換操做數據庫的模塊的語句:併發
import pymysql pymysql.install_as_MySQLdb()
1.3 鏈接sqlite3數據庫app
選擇鏈接sqlite3數據庫:框架
安裝驅動,測試鏈接成功後點擊OK鏈接sqlite數據庫:less
1.3步驟執行完畢後,咱們能夠將下圖顯示的那個數據庫刪除(固然留着也沒事)。
1.4 在模型層中寫幾個映射數據庫中表的類
from django.db import models # Create your models here. class User(models.Model): name = models.CharField(max_length=32) age = models.IntegerField() register_time = models.DateField()
1.5 執行數據庫遷移命令(可使用Tools中的Run manage.py Task)
#pycharm下方點擊Terminal打開命令行窗口,執行如下兩句命令 python3 manage.py makemigrations python3 manage.py migrate
執行完數據庫遷移命令後,咱們會發現項目中出現了db.sqlite3數據庫,直接雙擊打開:
1.6 數據庫sqlite3鏈接完成
數據庫鏈接完畢後,咱們能夠選擇新建.py文件或者使用應用文件夾下自帶的tests.py來導入模型層models.py中的類來熟悉一下ORM語句,不過在這以前要作一些準備工做,否則當你經過ORM操做數據庫時會報錯。
咱們直接使用應用文件夾下自帶的tests.py文件測試ORM。首先輸入如下語句(前幾行能夠從manage.py中複製)。
from django.test import TestCase # Create your tests here. import os if __name__ == "__main__": os.environ.setdefault("DJANGO_SETTINGS_MODULE", "day58.settings") import django django.setup() # 以上語句固定寫法,接下來寫導入models模塊的語句 from app01 import models # 從這開始正常寫ORM操做數據庫的語句便可
當咱們操做數據庫拿回來的是QuerySet對象是,咱們能夠經過QuerySet對象.query查看內部的sql語句,那麼返回的不是QuerySet對象時該怎麼作呢?只須要在settings.py中找個地方將下面語句放着便可(運用日誌)。
配置文件配置參數查看全部orm操做內部的sql語句 LOGGING = { 'version': 1, 'disable_existing_loggers': False, 'handlers': { 'console':{ 'level':'DEBUG', 'class':'logging.StreamHandler', }, }, 'loggers': { 'django.db.backends': { 'handlers': ['console'], 'propagate': True, 'level':'DEBUG', }, } }
爲了更好的瞭解ORM對數據庫的操做,咱們選擇鏈接mysql數據庫,畢竟sqlite3仍是有點小瑕疵的。
from django.db import models # Create your models here. class Book(models.Model): title = models.CharField(max_length=32) price = models.DecimalField(max_digits=8, decimal_places=2) publish_time = models.DateField(auto_now_add=True) publish = models.ForeignKey(to='Publish') authors = models.ManyToManyField(to='Author') class Publish(models.Model): name = models.CharField(max_length=32) addr = models.CharField(max_length=32) email = models.EmailField() class Author(models.Model): name = models.CharField(max_length=32) age = models.IntegerField() author_detail = models.OneToOneField(to='AuthorDetail') class AuthorDetail(models.Model): phone = models.CharField(max_length=32) addr = models.CharField(max_length=32)
ORM有不少經常使用字段,其中字段DateField的參數有auto_now跟auto_now_add,若是咱們配置了這兩個參數中的一個,建立數據對象時DateField字段會自動建立,無需咱們傳值。配置auto_now_add=True,建立數據記錄的時候會把當前時間添加到數據庫,以後就不在變化。配置上auto_now=True,每次更新數據記錄的時候會更新該字段。友情提示,DateFIeld和DateTImeField能夠直接接收datetime對象。
ForeignKey有to_field參數,能夠指定你想跟一張表的什麼字段創建外鍵,不寫默認是id。
當使用數據對象進行數據的save()和delete()時,實際上找的是類Model中的方法(屬性與方法的查找順序),因此咱們能夠經過重寫save、delete方法來對數據對象保存和刪除的過程加以限制。
1.1 新增數據
# 第一種:有返回值,而且就是當前被建立的數據對象 modles.Publish.objects.create(name='',addr='',email='') # 第二種:先實例化產生對象,而後調用save方法保存 book_obj = models.Publish(name='',addr='',email='') book_obj.save()
1.2 修改數據
# 基於數據對象 user_obj = models.User.objects.filter(name='jason').first() user_obj.age = 17 user_obj.save() # 基於queryset對象 models.User.objects.filter(name='kevin').update(age=66)
1.3 刪除數據
# 基於數據對象 # user_obj = models.User.objects.filter(name='owen').first() # user_obj.delete() # 基於queryset對象 models.User.objects.filter(name='egon').delete()
1.4 查詢數據
以前經常使用的查詢是all(惰性查詢)查全部及filter條件查詢,其中filter條件查詢有多個條件時,各條件是and的關係。
models.User.objects.filter(name='egon', age=18) # name='egon' and 'age'=18
其實查詢數據能用到的方法一共有13個(objects對象和QuerySet對象方法),最好都記住。而後QuerySet對象可以無限制的點QuerySet方法(後面點方法可能會沒有提示,別慌,繼續手打):
# 在過濾獲得的QuerySet對象的基礎上再進行過濾,依次類推 models.User.objects.filter(過濾條件1).filter(過濾條件2).order_by(排序依據的字段名).reverse() # reverse方法使用的前提是QuerySet對象被排序過,order_by是排序(排序依據的字段名前加-號便是反向排序)
<1> all(): #查詢全部結果 <2> filter(**kwargs): #它包含了與所給篩選條件相匹配的對象 <3> get(**kwargs): #返回與所給篩選條件相匹配的對象,返回結果有且只有一個,若是符合篩選條件的對象超過一個或者沒有都會拋出錯誤。 <4> exclude(**kwargs): #它包含了與所給篩選條件不匹配的對象 <5> order_by(*field): #對查詢結果排序('-id')/('price') <6> reverse(): #對查詢結果反向排序 >>>前面要先有排序才能反向 <7> count(): #返回數據庫中匹配查詢(QuerySet)的對象數量。 <8> first(): #返回第一條記錄 <9> last(): #返回最後一條記錄 <10> exists(): #若是QuerySet包含數據,就返回True,不然返回False <11> values(*field): #返回一個ValueQuerySet——一個特殊的QuerySet,運行後獲得的並非一系列model的實例化對象,而是一個可迭代的字典序列 <12> values_list(*field): #它與values()很是類似,它返回的是一個元組序列,values返回的是一個字典序列 <13> distinct(): #從返回結果中剔除重複紀錄
着重記住values方法(普通QuerySet對象:<QuerySet [<Author: Author object>]>):
在上述13種方法中,有些方法執行完返回的不必定是QuerySet對象,多是其餘對象。
# 返回QuerySet對象的方法 all()、filter()、exclude()、order_by()、reverse()、distinct() # 特殊的QuerySet values() #返回一個可迭代的字典序列 values_list() #返回一個可迭代的元祖序列 # 返回具體數據對象的 get()、first()、last() # 返回布爾值的方法 exists() #判斷QuerySet對象是否爲空,空返回False,不然返回True # 返回數字的方法 count() #返回當前QuerySet對象中的數據對象個數
舒適提示:all()跟filter()是惰性查詢,即返回QuerySet對象,只有調用QuerySet對象時內部的sql語句纔會執行(這就是惰性的精髓)。objects是管理器對象(objects = Manage()),是Models和數據庫進行查詢的接口。Manage存在於models模塊中,因此咱們實際上是能夠自定義一個類,繼承models.Manage來定義本身的管理器對象的。
上述的查詢中,filter的查詢條件都是name='值',age=值之類的,但是咱們查詢時條件確定會有age>18,age<=18等狀況。這個時候就須要用的雙下劃線查詢。
2.1 大於 小於 大於等於 小於等於
filter(price__gt=90) # 大於 great than filter(price__lt=90) # 小於 less than filter(price__gte=90) # 大於等於 great than equal filter(price__lte=90) # 小於等與 less than equal
2.2 存在於某幾個條件中
filter(age__in=[11,22,33])
2.3 在某個範圍內
filter(age__range=[50,90])
2.4 模糊查詢
filter(title__contains='p') # 區分大小寫 filter(title__icontains='P') # 不區分大小寫
2.5 以什麼開頭,以什麼結尾
# 查詢名字以j開頭的用戶 res = models.User.objects.filter(name__startswith='j') print(res) # 查詢名字以n結尾的用戶 res = models.User.objects.filter(name__endswith='n') print(res)
2.6 按年查詢(針對DateField和針對DateTImeField)
filter(create_time__year='2017')
1.1 新增(主鍵用pk傳比較好,比較穩)
# 直接寫id models.Book.objects.create(title='紅樓夢',price=66.66,publish_id=1) # 傳數據對象 publish_obj = models.Publish.objects.filter(pk=2).first() models.Book.objects.create(title='三國演義',price=199.99,publish=publish_obj)
1.2 修改
# Queryset修改 models.Book.objects.filter(pk=1).update(publish_id=3) publish_obj = models.Publish.objects.filter(pk=2).first() models.Book.objects.filter(pk=1).update(publish=publish_obj) # 對象修改 book_obj = models.Book.objects.filter(pk=1).first() book_obj.publish_id = 3 # 點表中真實存在的字段名 book_obj.save() publish_obj = models.Publish.objects.filter(pk=2).first() book_obj.publish = publish_obj # 點orm中字段名 傳該字段對應的表的數據對象 book_obj.save()
1.3 刪除
# 使用QuerySet對象刪除 models.Book.objects.filter(pk=1).delete() models.Publish.objects.filter(pk=1).delete() # 使用數據對象刪除 book_obj = models.Book.objects.filter(pk=3).first() book_obj.delete()
2.1 添加(add)
# 拿到id=3的書籍數據對象 book_obj = models.Book.objects.filter(pk=3).first() # 數據對象.authors能夠直接跳到多對多那張表裏 # add傳值傳做者id,能夠傳多個 book_obj.authors.add(1) book_obj.authors.add(2,3) # add傳值支持傳對象,並且能夠傳多個 author_obj = models.Author.objects.filter(pk=1).first() author_obj1 = models.Author.objects.filter(pk=3).first() book_obj.authors.add(author_obj) book_obj.authors.add(author_obj,autor_obj1)
2.2 修改(set)
set接收的參數必須是可迭代對象!!!
# 拿到id=3的書籍數據對象 book_obj = models.Book.objects.filter(pk=3).first() # 數據對象.authors能夠直接跳到多對多那張表裏 # set傳值傳做者id,能夠傳多個 book_obj.authors.set((1,)) book_obj.authors.set((1,2,3)) # set傳值支持傳對象,並且能夠傳多個 author_list = models.Author.objects.all() book_obj = models.Book.objects.filter(pk=3).first() book_obj.authors.set(author_list)
2.3 刪除(remove)
# 拿到id=3的書籍數據對象 book_obj = models.Book.objects.filter(pk=3).first() # 數據對象.authors能夠直接跳到多對多那張表裏 # remove傳值支持傳值,並且能夠傳多個 book_obj.authors.remove(1) book_obj.authors.remove(2,3) # remove傳值支持傳對象,並且能夠傳多個 author_obj = models.Author.objects.all().first() author_list = models.Author.objects.all() book_obj.authors.remove(author_obj) book_obj.authors.remove(*author_list) #須要將Queryset對象打散
2.4 清空(clear)
清空的是你當前這個表記錄對應的綁定關係,不會影響其餘表。
book_obj = models.Book.objects.filter(pk=3).first()
book_obj.authors.clear()
正向反向查詢概念及方法:
# 一對一 # 正向:author---關聯字段在author表裏--->authordetail 按字段 # 反向:authordetail---關聯字段在author表裏--->author 按表名小寫 # 查詢jason做者的手機號 正向查詢 # 查詢地址是 :山東 的做者名字 反向查詢 # 一對多 # 正向:book---關聯字段在book表裏--->publish 按字段 # 反向:publish---關聯字段在book表裏--->book 按表名小寫_set.all() 由於一個出版社對應着多個圖書 # 多對多 # 正向:book---關聯字段在book表裏--->author 按字段 # 反向:author---關聯字段在book表裏--->book 按表名小寫_set.all() 由於一個做者對應着多個圖書 # 連續跨表 # 查詢圖書是三國演義的做者的手機號,先查書,再正向查到做者,在正向查手機號 # 總結:基於對象的查詢都是子查詢,這裏能夠用django配置文件自動打印sql語句的配置作演示
3.1 基於對象的表查詢
# 查詢書籍是三國演義的出版社郵箱 book_obj = models.Book.objects.filter(title='三國演義').first() print(book_obj.publish.email) # 查詢書籍是水滸傳的做者的姓名 book_obj = models.Book.objects.filter(title='水滸傳').first() print(book_obj.authors) # app01.Author.None print(book_obj.authors.all()) # 查詢做者爲jason電話號碼 user_obj = models.Author.objects.filter(name='jason').first() print(user_obj.authordetail.phone)
# 查詢出版社是東方出版社出版的書籍 一對多字段的反向查詢 publish_obj = models.Publish.objects.filter(name='東方出版社').first() print(publish_obj.book_set) # app01.Book.None print(publish_obj.book_set.all()) # 查詢做者jason寫過的全部的書 多對多字段的反向查詢 author_obj = models.Author.objects.filter(name='jason').first() print(author_obj.book_set) # app01.Book.None print(author_obj.book_set.all()) # 查詢做者電話號碼是110的做者姓名 一對一字段的反向查詢 authordetail_obj = models.AuthorDetail.objects.filter(phone=110).first() print(authordetail_obj.author.name)
3.2 基於雙下劃線的查詢
# 查詢書籍爲三國演義的出版社地址 res = models.Book.objects.filter(title='三國演義').values('publish__addr','title') print(res) # 查詢書籍爲水滸傳的做者的姓名 res = models.Book.objects.filter(title='水滸傳').values("authors__name",'title') print(res) # 查詢做者爲jason的家鄉 res = models.Author.objects.filter(name='jason').values('authordetail__addr') print(res)
# 查詢南方出版社出版的書名 res = models.Publish.objects.filter(name='南方出版社').values('book__title') print(res) # 查詢電話號碼爲120的做者姓名 res = models.AuthorDetail.objects.filter(phone=120).values('author__name') print(res) # 查詢做者爲jason的寫的書的名字 res = models.Author.objects.filter(name='jason').values('book__title') print(res) # 查詢書籍爲三國演義的做者的電話號碼 res = models.Book.objects.filter(title='三國演義').values('authors__authordetail__phone') print(res)
3.3 小練習
# 查詢jason做者的手機號 # 正向 res = models.Author.objects.filter(name='jason').values('authordetail__phone') print(res) # 反向 res = models.AuthorDetail.objects.filter(author__name='jason').values('phone') print(res) # 查詢出版社爲東方出版社的全部圖書的名字和價格 # 正向 res = models.Publish.objects.filter(name='東方出版社').values('book__title','book__price') # print(res) 反向 # res = models.Book.objects.filter(publish__name='東方出版社').values('title','price') print(res) # 查詢東方出版社出版的價格大於400的書 # 正向 res = models.Publish.objects.filter(name="東方出版社",book__price__gt=400).values('book__title','book__price') print(res) # 反向 res = models.Book.objects.filter(price__gt=400,publish__name='東方出版社').values('title','price') print(res)
在查詢的時候先把orm查詢語句寫出來,再看用到的條件是否在當前表內,在就直接獲取,不在就按照正向按字段反向按表名小寫來查便可。切忌一口吃成胖子。
須要先導入模塊:
from django.db.models import Max,Min,Count,Sum,Avg
# 查詢全部書籍的做者個數 res = models.Book.objects.filter(pk=3).aggregate(count_num=Count('authors')) print(res) # 查詢全部出版社出版的書的平均價格 res = models.Publish.objects.aggregate(avg_price=Avg('book__price')) print(res) # 4498.636 # 統計東方出版社出版的書籍的個數 res = models.Publish.objects.filter(name='東方出版社').aggregate(count_num=Count('book__id')) print(res)
# 統計每一個出版社出版的書的平均價格 res = models.Publish.objects.annotate(avg_price=Avg('book__price')).values('name','avg_price') print(res) # 統計每一本書的做者個數 res = models.Book.objects.annotate(count_num=Count('authors')).values('title','count_num') print(res) # 統計出每一個出版社賣的最便宜的書的價格 res = models.Publish.objects.annotate(min_price=Min('book__price')).values('name','min_price') print(res) # 查詢每一個做者出的書的總價格 res = models.Author.objects.annotate(sum_price=Sum('book__price')).values('name','sum_price') print(res)
目前爲止咱們filter的條件的值都是咱們本身給的,好比age=18。並且filter多個查詢條件時,它們是and的關係。當咱們查詢條件的值是來自表中某字段的值,或者咱們查詢條件是or的關係,咱們就要引入F、Q查詢。
首先F、Q使用前要先導入:
from django.db.models import F,Q
1.1 F查詢
假若有一張服裝店的銷售表(商品、價格、庫存、銷量),當咱們想要查詢銷量大於50的商品時很好實現:
res = models.Product.objects.filter(sell__gt=50) print(res)
當咱們要查詢銷量大於庫存的商品時,就要使用F查詢了:
from django.db.models import F,Q # F查詢 res = models.Product.objects.filter(sell__gt=F('stock')) print(res)
將全部商品的價格提升100:
models.Product.objects.update(price=F('price')+100)
將全部商品的名字後面都加一個爆款:
# 是否是本能會這麼想?注意,這是錯誤的寫法,由於ORM不支持+拼接字符串 models.Product.objects.update(price=F('name')+'爆款')
ORM不支持+拼接字符串,mysql中咱們拼接字符串要用concat,此處同理:
# 要進行字符串的拼接須要先導入Concat和Value模塊 from django.db.models.functions import Concat from django.db.models import Value models.Product.objects.update(name=Concat(F('name'), Value('爆款')))
1.2 Q查詢
Q查詢能夠將filter中的查詢條件變成or的關係,並且還能經過實例化對象的方法實現屬性能夠是字符串的形式(查詢時原本要寫name='xxx',Q查詢能夠實現'name'='xxx')。
注意:Q對象必須放在普通的過濾條件前面。
# Q查詢 res = models.Product.objects.filter(price=188.88, name='連衣裙爆款') print(res) from django.db.models import F, Q res = models.Product.objects.filter(Q(price=188.88), Q(name='連衣裙爆款')) # and res1 = models.Product.objects.filter(Q(price=188.88)|Q(name='連衣裙爆款')) # or res2 = models.Product.objects.filter(Q(price=188.88)|~Q(name='連衣裙爆款')) # not # 混合使用 須要注意的是Q對象必須放在普通的過濾條件前面 res3 = models.Product.objects.filter(~Q(name='連衣裙爆款'), price=188.88) # not
前面說Q查詢能夠實現查詢條件寫成'name'='xxx'的格式(好比說咱們寫一個函數,根據用戶輸入去數據庫中查詢,而用戶輸入都是字符串的形式)。也許你會進行如下嘗試:
user_input = 'name' res = models.Author.objects.filter(eval(user_input)='jason') print(res)
而後就會立刻迎來喜報:
要實現該需求,咱們須要使用Q實例化出的對象來實現(先來看一下Q的源碼):
實現的代碼以下:
from django.db.models import F, Q q = Q() # 先實例化出一個Q的對象 q.connector = 'or' # 經過這個參數能夠將Q對象默認的and關係變成or q.children.append(('price', 188.88)) q.children.append(('name', '高跟鞋爆款')) res = models.Product.objects.filter(q) # Q對象查詢默認是and,上方截圖爲證 print(res)
2.1 事務的四大特性:
Atomic(原子性):事務中包含的操做被看作一個邏輯單元,這個邏輯單元中的操做要麼所有成 功,要麼所有失敗。
Consistency(一致性):事務完成時,數據必須處於一致狀態,數據的完整性約束沒有被破壞,事務在執行過程當中發生錯誤,會被回滾(Rollback)到事務開始前的狀態,就像這個事務歷來沒 有執行過同樣。
Isolation(隔離性):事務容許多個用戶對同一個數據進行併發訪問,而不破壞數據的正確性 和完整性。同時,並行事務的修改必須與其餘並行事務的修改相互獨立。
Durability(持久性):事務結束後,事務處理的結果必須可以獲得固化。
2.2 事務的語法
在sql語句中使用事務時,咱們須要用start開啓事務,又要用end結束事務,這就須要咱們判斷事務什麼時候開始和結束比較好。而在Django的ORM中,直接使用上下文管理便可,省事又省心。
這裏直接拿上面提到過的銷售表作示例,好比一件商品賣出後,銷量要+1,對應的庫存要-1。
# 導入事務的模塊 from django.db import transaction from django.db.models import F with transaction.atomic(): # atomic原子性,要麼全成功,要麼全失敗 # 在with代碼塊兒寫你的事務操做 models.Product.objects.filter(id=1).update(stock=F('stock')-1) models.Product.objects.filter(id=1).update(sell=F('sell')+1) # 寫其餘代碼邏輯 print('支付寶到帳~~~')
only和defer是相反的兩個過程,有點相似filter()和exclude()的區別。咱們知道values返回的是列表套字典形式的QuerySet對象,而only和defer返回的是列表套對象的QuerySet對象。
以Product爲例,當使用only('name')時,返回的對象只攜帶Product對象的name屬性,若是用對象.未攜帶的屬性,那麼它會自動再去數據庫中Product映射的表中查找該屬性,有就帶回來返回給你。defer正好相反,defer('name')表示攜帶除name以外的全部屬性,當對象.name時,會自動去數據庫中Product映射的表中找name屬性帶回來給你。注意:only和defer自動找數據庫找屬性時會走sql語句。
# 輸出一下用values取出來的結果(列表套字典,鍵爲'name') test = models.Product.objects.values('name') print(test) # only取出來的是列表套對象,對象只有name屬性 res = models.Product.objects.only('name') for i in res: print(i.name) # defer取出來的是列表套對象,對象除了name屬性,其餘屬性都有 res1 = models.Product.objects.defer('name') for i in res: print(i.name)
Django支持自定義字段,須要繼承models.Field類。
# 自定義字段,對應數據庫中的Char類型 class MyCharField(models.Field): # 數據庫中Char類型須要指定長度,因此要傳max_length def __init__(self,max_length,*args,**kwargs): self.max_length = max_length # 調用父類init,必定要寫關鍵字傳參,由於Field的init參數不少,能夠看一下它的源碼 super().__init__(max_length=max_length,*args,**kwargs) # 該方法也不能少 def db_type(self, connection): return 'char(%s)'%self.max_length
父類models.Field中的db_type方法
提一下,當咱們給一個表新增一個字段時,若是表中已經有數據了,那麼咱們能夠將models.py中新增的字段名null設置默認值或者是設置爲空。
# 設置默認值 gender = models.IntegerField(default=2) # 容許爲空 gender = models.IntegerField(null=True)
choices屬性使用的場景也挺多,好比性別,在數據庫中咱們能夠存儲數字,好比1表明男,2表明女,而顯示給用戶看時,要轉變爲對應的男或女。
簡單示範一下:
class User(models.Model): name = models.CharField(max_length=32) choices = ((1,'男'),(2,'女'),(3,'其餘')) gender = models.IntegerField(choices=choices,default=2)
這樣數據庫裏存的就是數字了,那麼取出來怎麼變成對應的男、女呢:
res = models.User.objects.filter(id=1).first() print(res.gender) # 獲取編號對應的中文註釋,固定寫法get_字段名_display() print(res.get_gender_display()) # 建立對象是傳數字便可 models.User.objects.create(...gender=1)
Django大體分爲模型層(models)、模板層(templates)、視圖層(views)、路由層(urls)。由於Django中控制器接受用戶輸入的部分由框架自行處理,因此 Django 裏更關注的是模型models、模板templates和視圖views,稱爲 MTV模式。不過本質上也是屬於MVC框架(模型model,視圖view和控制器controller)。
以前建立測試ORM的表時,多對多關係的表是經過語句讓ORM自動幫咱們建立的,實際上建立多對多的表有三種方式。
第一種:Django主動咱們幫咱們建立
該方法很方便,可是第三張表字段都是固定的,沒法擴展
class Book(models.Model): name = models.CharField(max_length=32) # 該命令讓Django自動幫咱們建立多對多關係的第三張表 authors = models.ManyToManyField(to='Author') class Author(models.Model): name = models.CharField(max_length=32)
第二種:純手動建立第三張表
該方式能夠在第三張表中增長額外的字段,可是沒法像第一種方式建立的表同樣直接利用ORM正向反向查詢信息(好比book對象要查詢Author表中的內容,只能是反向查詢至Book2Author這張表,而後經過該表正向查詢Author表的內容)。
class Book(models.Model): name = models.CharField(max_length=32) class Author(models.Model): name = models.CharField(max_length=32) class Book2Author(models.Model): # 經過建立多對多關係的兩張表的外鍵來實現 book = models.ForeignKey(to='Book') author = models.ForeignKey(to='Author') info = models.CharField(max_length=32)
第三種:半自動建立
當一個項目中對數據庫擴展性有要求時,雖然第二種的確增長了擴展性,可是沒法充分利用ORM的多表查詢。接下來講的第三種方法結合了前兩種的優勢。建議使用該方式,擴展性高,若是使用第一種,後期須要在多對多關係的表增長字段時,須要改動的地方不少。
注意:使用第三種方式建立多對多關聯關係時,就沒法使用set、add、remove、clear方法來管理多對多的關係了,只能經過第三張表的model來管理多對多關係。當更改book對應的author信息時,能夠先將book2author中該book對象對應的信息所有刪除,而後再根據author信息從新建立數據,實際上ORM管理多對多表關係的set方法內部也是採用該方式。
class Book(models.Model): name = models.CharField(max_length=32) # 第三種建立表的方式 authors = models.ManyToManyField(to='Author',through='Book2Author',through_fields=('book','author')) class Author(models.Model): name = models.CharField(max_length=32) # 跟上面的ManyToManyField選一個寫便可 book = models.ManyToManyField(to='Book',through='Book2Author',through_fields=('author','book')) class Book2Author(models.Model): book = models.ForeignKey(to='Book') author = models.ForeignKey(to='Author') info = models.CharField(max_length=32)
小提示: