1. 程式人生 > 其它 >ORM相關操作

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',
        },
    }
}