ORM相關操作
ORM之雙下劃線查詢
filter()我們都知道是ORM用來篩選資料的,像篩選指定欄位等於指定值的資料可以直接用欄位=值傳入,但如果想要篩選某個範圍的資料呢?
雙下劃線方法 | 作用 |
---|---|
__gt | 大於 |
__gte | 大於等於 |
__lt | 小於 |
__lte | 小於等於 |
__in | 成員運算 |
__range | 篩選範圍內資料,包含開頭和結尾 |
__contains | 篩選欄位包含值的資料,區分大小寫 |
__icontains | 篩選欄位包含值的資料,不區分大小寫 |
__startswith | 篩選欄位是否以某個值開始的,區分大小寫 |
__endswith | 篩選欄位是否以某個值結尾的,區分大小寫 |
__month | 按照月份篩選資料 |
__year | 按照年份篩選資料 |
案例:
# 查詢age欄位大於20的使用者 res = models.User.objects.filter(age__gt=20) # 查詢age欄位是18、22、25的使用者 res = models.User.objects.filter(age__in=[18, 22, 25]) # 查詢age欄位在18到26之間的使用者 res = models.User.objects.filter(age__range=[18, 26]) # 查詢name欄位中包含字母j的使用者 res = models.User.objects.filter(name__contains='j') # 查詢name欄位中以字母j開頭的使用者 res = models.User.objects.filter(name__startswith='j') # 查詢create_time欄位是5月的資料 res = models.User.objects.filter(create_time__month=5)
外來鍵欄位的建立
因為外來鍵關係有三種,所以在ORM也有三種建立外來鍵欄位的方法。
建立一對多關係:
models.ForeignKey(to=表(類)名)
建立一對一關係:
models.OneToOneField(to=表(類)名)
建立多對多關係:
models.ManyToManyField(to=表(類)名)
以上的建立外來鍵欄位都是預設與另外一個表的主鍵建立關係,如果想要指定欄位,除了新增給關鍵字to新增引數,還需要給關鍵字to_field或者to_fields新增引數。
注意事項:
ManyToManyField不會在表中建立實際的欄位,而是告訴django orm自動建立第三張關係表。
ForeignKey、OneToOneField會在欄位的後面自動新增_id字尾,所以不要自己加_id字尾。
外來鍵欄位操作
一對多、一對一外來鍵操作
一對多與一對一關係操作方式都一樣,與其他欄位操作方式都一樣,唯一特殊的地方就是它可以用物件新增值。
比如我用ORM建立了一個有外來鍵的表Student(uid[主鍵],name,clazz_id[外來鍵]),與表Clazz建立一對多關係。
# 普通新增資料
models.Student.objects.create(name='tom', clazz_id=1)
# 用獲取的物件新增資料
cla_obj = models.Clazz.objects.filter(clazz_id=1).first()
models.Student.objects.create(name='tom', clazz_id=cla_obj)
修改資料同理。
操作外來鍵欄位需要注意欄位名稱,不能用你在類裡面定義的外來鍵欄位名稱,因為ORM建立外來鍵欄位會自動新增_id字尾。
多對多外來鍵操作
多對多外來鍵操作需要用到新的方法,因為它是作用在第三張表的。
比如我用ORM建立了一個有外來鍵的表Student(uid[主鍵],name,course_id[外來鍵]),與表Course建立多對多關係。
新增資料:add(),括號內可以放主鍵值也可以放資料物件,並且都支援多個。
# 建立表Student中主鍵為1與Course中主鍵為1的關係
stu_obj = models.Student.objects.filter(pk=1).first()
stu_obj.course.add(1)
# 建立表Student中主鍵為2與Course中主鍵為1和2的關係
stu_obj = models.Student.objects.filter(pk=2).first()
stu_obj.course.add(1, 2)
"""資料物件同理"""
修改資料:set(),括號內必須是一個可迭代物件,元素同樣支援主鍵值或者資料物件。
# 修改表Student中主鍵為1只與Course中主鍵為2建立關係
stu_obj = models.Student.objects.filter(pk=1).first()
stu_obj.course.set([2,]) # 必須是可迭代物件
# 修改表Student中主鍵為2只與Course中主鍵為1,2,3建立關係
stu_obj = models.Student.objects.filter(pk=2).first()
stu_obj.course.set([1,2,3]) # 必須是可迭代物件
"""資料物件同理"""
刪除資料:remove(),括號內可以放主鍵值也可以放資料物件,並且都支援多個。
# 移除表Student中主鍵為1與Course中主鍵為1的關係
stu_obj = models.Student.objects.filter(pk=1).first()
stu_obj.course.remove(1)
# 移除表Student中主鍵為2與Course中主鍵為1,2的關係
stu_obj = models.Student.objects.filter(pk=2).first()
stu_obj.course.remove(1,2)
"""資料物件同理"""
清空指定資料:clear(),括號內無需傳值,直接清空當前表在第三張關係表中的繫結記錄。
# 清空表Student中主鍵為1與Course的所有關係
stu_obj = models.Student.objects.filter(pk=1).first()
stu_obj.course.clear()
多表查詢
ORM與MySQL多表查詢思路區別:
子查詢:
- MySQL:將SQL語句用括號括起來當做條件使用
- ORM:基於物件的跨表查詢
連表查詢:
- MySQL:使用inner join\left join\right join\union
- ORM:基於雙下劃線的跨表查詢
在瞭解ORM的多表查詢前,先了解是什麼正反向查詢。
正反向查詢的概念
正向查詢:使用含有外來鍵欄位的資料物件進行查詢。
反向查詢:使用不含有外來鍵欄位的資料物件進行查詢。
比如學生表與班級班建立一對多關係,外來鍵欄位在學生表中,通過學生查詢班級就是正向查詢,通過班級查詢學生就是反向查詢。
核心在於當前資料物件是否含有外來鍵欄位,有則是正向,沒有則是反向。
查詢口訣
正向查詢按類中的外來鍵欄位屬性名查詢,反向查詢按表名小寫加_set查詢。
基於物件的跨表查詢
基於物件的跨表查詢優先獲取物件,再通過物件去查詢。
如果獲取的物件結果可能有多個,那麼需要在呼叫all()才能獲取物件,比如一對一、一對多關係的正向查詢中,肯定都是隻獲取一個物件結果的,所以不需要再呼叫all(),而多對多關係的正向查詢都是可能會有多個物件結果,所以需要用到all()。
all()方法返回的是一個QuerySet物件。
現在有如下表:一對多關係
- Student(sid[主鍵],name,clazz_id[外來鍵])
- Clazz(cid[主鍵],name)
注意clazz_id欄位是ORM幫我取得,我自己的類中的外來鍵欄位名稱為clazz,ORM自動幫我添加了_id字尾,後續使用正向查詢的時候使用的都是clazz,而不是clazz_id。
正向查詢
查詢Student中name為'tom'的班級
# 1.先獲取name='tom'的資料物件
stu_obj = models.Student.objects.filter(name='tom').first()
# 2.獲取該物件的外來鍵物件
res = stu_obj.clazz # 返回一個Clazz表的物件
如果是多對多關係的正向查詢,需要在呼叫all()。
反向查詢
查詢Clazz中name為'一班'的所有學生
# 1.先獲取Clazz中name='一班'的資料物件
cla_obj = models.Clazz.objects.filter(name='一班').first()
# 2.通過表名加_set查詢
res = cla_obj.student_set # 返回None
因為一對多的反向查詢獲取的物件可能有多個,那麼需要在呼叫all()才能獲取物件。
# 1.先獲取Clazz中name='一班'的資料物件
cla_obj = models.Clazz.objects.filter(name='一班').first()
# 2.通過表名加_set查詢,並加上all()
res = cla_obj.student_set.all() # 返回QuerySet
針對一對一的反向查詢,不需要加_set。
基於雙下劃線的跨表查詢
基於雙下劃線的跨表查詢本質就是連表操作。如果是正向查詢,查詢時用外來鍵欄位屬性名加雙下劃線加欄位查詢;如果是反向查詢,查詢時用表名加雙下劃線加欄位查詢;現在看不懂啥意思沒關係,往下看就行了。
現在有如下表:一對多關係
- Student(sid[主鍵],name,clazz_id[外來鍵])
- Clazz(cid[主鍵],name)
我們都知道如果想要查詢Student中name='tom'的sid欄位和clazz_id欄位,可以這麼寫:
res = models.Student.objects.filter(name='tom').values('sid', 'clazz_id')
基於雙下劃線的跨表查詢也是類似這麼寫的。
正向查詢
查詢Student中sid為1的班級名稱
res = models.Student.objects.filter(sid=1).values('clazz__name')
程式碼中的clazz代表的是外來鍵欄位屬性名,而不是表名,name代表的是clazz外來鍵欄位所連線的表中的欄位。
反向查詢
查詢Clazz中cid為1的所有學生姓名
res = models.Clazz.objects.filter(cid=1).values('student__name')
程式碼中的student代表的是表名,name代表student表中的欄位。
基於雙下劃線的跨表查詢返回的都是QuerySet物件。
雙下劃線擴充套件
雙下劃線不僅可以用於查詢,也可以用於filter()篩選資料。
比如現在有如下表:一對多關係
- Student(sid[主鍵],name,clazz_id[外來鍵])
- Clazz(cid[主鍵],name)
現在只能通過操作Clazz表,並且要查詢出Student表中name='tom'的班級名稱。
res = models.Clazz.objects.filter(student__name='tom').values('name')
在filter()的student__name中,student代表表名,name代表student中的name欄位。
連續跨表操作
雙下劃線還可能連續跨表操作,跨表的前提,表與左右的表有關係。
比如:
res = models.Clazz.objects.filter(student__course__name='語文')
上述程式碼已表Clazz為起點,先到表Student,在從表Student到表Course中的name欄位。以上程式碼的前提是,clazz與student有關係,student與course有關係。
篩選如此,查詢同理。
檢視SQL語句
方式1:如果結果集物件是QuerySet,那麼可以直接.query檢視:
QuerySet.query
方式2:配置檔案固定配置,適用面更廣,只要執行了orm操作,都會列印內部SQL語句。在settings.py資料夾下新增:
LOGGING = {
'version': 1,
'disable_existing_loggers': False,
'handlers': {
'console':{
'level':'DEBUG',
'class':'logging.StreamHandler',
},
},
'loggers': {
'django.db.backends': {
'handlers': ['console'],
'propagate': True,
'level':'DEBUG',
},
}
}