1. 程式人生 > 實用技巧 >Django中orm相關操作

Django中orm相關操作

必知必會13條
示例model
單表雙下劃線
外來鍵相關
多對多相關
聚合和分組
F、Q
orm效能相關
事務

必知必會13條
import os
import django

os.environ.setdefault("DJANGO_SETTINGS_MODULE", "day50_orm.settings")
django.setup()
from app01 import models

一、all 查詢所有的資料,返回的是一個物件列表(QuerySet)

ret = models.Person.objects.all()
print('all',ret)

二、get 獲取一個存在且唯一的資料,返回的是物件,沒有或者是多個就報錯

ret = models.Person.objects.get(pid=4)
print('get',ret.name)

三、filter 篩選出符合條件的,返回的是物件列表

ret = models.Person.objects.filter(age=18)
print('filter',ret)

四、exclude 獲取不滿足條件的資料,返回的是物件列表

ret = models.Person.objects.exclude(name='alex')
print('exclude',ret)
for i in ret:
print(i.name)

五、order_by 排序,預設為升序,欄位前加-為降序

ret = models.Person.objects.all().order_by('pid')
print('order_by',ret)
for i in ret:
print(i.pid)

六、reverse 對排完序的物件列表進行反轉

ret = models.Person.objects.all().order_by('pid').reverse()
print('reverse',ret)
for i in ret:
print(i.pid)

七、values 不指定欄位,獲取資料所有的欄位和值,返回的是物件列表(QuerySet)[{},{}]

ret = models.Person.objects.all().values()
print('values',ret)

指定欄位,獲取到資料指定的欄位名和值,返回的是物件列表(QuerySet)[{},{}]

ret = models.Person.objects.all().values('name', 'age')
print('values',ret)

八、values_list 不指定欄位,獲取所有資料欄位的值,返回的是物件列表(QuerySet)[{},{}]

ret = models.Person.objects.all().values_list()
print('values_list',ret)

指定欄位,獲取到資料指定的欄位的值,返回的是物件列表(QuerySet)[{},{}]

ret = models.Person.objects.all().values_list('name', 'age')
print('values_list',ret)

九、distinct 對獲取到的資料進行去重,返回的是物件列表(QuerySet)[{},{}]

ret = models.Person.objects.values('age').distinct()
print('distinct',ret)

十、count 對獲取到的資料進行計數

ret = models.Person.objects.filter(age=18).count()
print('count',ret)

十一、first 獲取第一個元素

ret = models.Person.objects.all().first()
print('first',ret.pid)

十二、last 獲取最後一個元素

ret = models.Person.objects.all().last()
print('last',ret.pid)

十三、exists 判斷資料是否存在,返回的是物件列表,如果物件不存在,返回的是空物件列表

ret = models.Person.objects.filter(pid=1)
print('exists',ret)
返回物件列表
all
filter
exclude
order_by
reverse
values [{},{}]
values_list [(),()]
distinct [{},{}]
返回物件
get
first
last
返回數字
count
返回布林值
exists
示例model模型
from django.db import models

class MyCharField(models.Field):
"""
自定義的char型別的欄位類
"""

  def __init__(self, max_length, *args, **kwargs):
      self.max_length = max_length
      super(MyCharField, self).__init__(max_length=max_length, *args, **kwargs)

  def db_type(self, connection):
      """
      限定生成資料庫表的欄位型別為char,長度為max_length指定的值
      """
      # return 'char(%s)' % self.max_length
      return f'char{self.max_length}'

class Person(models.Model):
pid = models.AutoField(primary_key=True)
name = models.CharField(db_column='nick', max_length=32, )
age = models.IntegerField(null=True, default=18)
phone = MyCharField(verbose_name='電話', max_length=11, blank=True, unique=True)
birth = models.DateTimeField(auto_now_add=True)
sex = models.BooleanField(choices=((True, '男'), (False, '女')))
gender = models.IntegerField(choices=((1, '男'), (2, '女'), (3, '不詳')))

  def __str__(self):
      return f'{self.pid}-{self.name}-{self.age}'
  
  class Meta:
      db_table = 'person'  # 資料庫中表的名字
      verbose_name = '個人資訊'  # admin中的表名稱
      verbose_name_plural = '所有使用者資訊'
      index_together = [
          ('name', 'age')
      ]  # 聯合索引,查詢兩個存在的欄位,全部查詢速度快,單查左邊速度快
      unique_together = ('name', 'age')  # 聯合唯一約束,查詢兩個存在的欄位

class Publisher(models.Model):
name = models.CharField(max_length=32)

  def __str__(self):
      return f'<Publisher object>:{self.id}-{self.name}'

class Book(models.Model):
name = models.CharField(max_length=32)
pub = models.ForeignKey('Publisher', on_delete=models.CASCADE, related_name='book')

  def __str__(self):
      return f'<Book object>:{self.id}-{self.name}-{self.pub.name}'

class Author(models.Model):
name = models.CharField(max_length=32, verbose_name='作者')
books = models.ManyToManyField('Book')

  def __str__(self):
      return f'<Author object>:{self.id}-{self.name}'

單表查詢之神奇的下劃線
import os, django
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "day50_orm.settings")
django.setup()
from app01 import models

ret = models.Person.objects.filter(pid__lt=5) # less than
ret = models.Person.objects.filter(pid__lte=5) # less than equal

ret = models.Person.objects.filter(pid__gt=5) # greater than
ret = models.Person.objects.filter(pid__gte=5) # greater than equal

ret = models.Person.objects.filter(pid__in=[5, 6, 11]) # 判斷在列表裡面
ret = models.Person.objects.exclude(pid__in=[5, 6, 11])
ret = models.Person.objects.filter(pid__range=[1, 10]) # 判斷範圍在1-10,包括10.

ret = models.Person.objects.filter(name__contains='a') # 判斷name屬性裡包不包含a
ret = models.Person.objects.filter(name__icontains='a') # 大小寫不敏感

ret = models.Person.objects.filter(name__startswith='s') # 判斷name是否已a開頭
ret = models.Person.objects.filter(name__istartswith='s') # 大小寫不敏感

ret = models.Person.objects.filter(name__endswith='t') # 判斷name是否以t結尾
ret = models.Person.objects.filter(name__iendswith='t') # 大小寫不敏感

ret = models.Person.objects.filter(birth__year='2020') # 判斷年是不是2020
ret = models.Person.objects.filter(birth__contains='-04-') # 判斷月份是不是4月

ret = models.Person.objects.filter(birth__month='02')

ret = models.Person.objects.filter(birth__day='21')

ret = models.Person.objects.filter(age__isnull=True) # 判斷age這個欄位是否為空

print(ret)
外來鍵(ForeignKey)查詢的相關操作
import os, django
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "day50_orm.settings")
django.setup()
from app01 import models

基於物件的查詢

正向查詢

book_obj = models.Book.objects.get(id=1)  # 獲取對應的book物件
print(book_obj.pub)  # 關聯的出版社物件
print(book_obj.pub_id)  # 關聯的出版社id

反向查詢

pub_obj = models.Publisher.objects.get(id=1)
print(pub_obj)  # 出版社物件
# 不指定related_name,使用類名_set獲取到關係關聯物件,在通過.all()獲取到對應的所有物件
print(pub_obj.book_set.all())  # pub_obj.book_set關係關聯物件,類名_set.all()得到出版社所有的書籍
print(type(pub_obj.book_set))  # pub_obj.book_set關係關聯物件
# 指定related_name,使用related_name的值獲取到關係關聯物件,在通過.all()獲取到對應的所有物件
print(pub_obj.book.all())  # pub_obj.book_set關係關聯物件

基於欄位的查詢

ret = models.Book.objects.filter(pub__name__contains='曉龍出版社')
# 不指定related_name,直接類名小寫__name
ret = models.Publisher.objects.filter(book__name='少年阿兵')
# 指定related_name,使用related_name的值 + __name
ret = models.Publisher.objects.filter(book__name='少年阿兵')
# 指定了related_query_name,使用related_query_name的值
# 不指定related_query_name,使用related_name的值
print(ret)

################### 外來鍵 ###################

pub_obj = models.Publisher.objects.get(id=1)

set add create [id,id]

pub_obj.books.set(models.Book.objects.filter(id__in=[1,2,3,4] ))
pub_obj.books.add(
models.Book.objects.all())

remove clear 外來鍵欄位引數 null=True 才有這兩個方法

pub_obj.books.clear()
多對多(ManyToManyField)查詢的相關操作
import os
import django
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "day50_orm.settings")
django.setup()
from app01 import models

author_obj = models.Author.objects.get(id=8) # 獲取id=8的作者物件
print(author_obj.books.all()) # 獲取到關係關聯物件的所有資料

關係關聯物件的方法

all 獲取所有的資料

set 設定多對多的關係,[id,id]/[物件,物件]

author_obj.books.set([1,4]) # 通過關係關聯物件給多對多表裡author_id=8設定book_id
author_obj.books.set(*models.Book.objects.filter(id__in=[2,4]))

add 新增多對多關係,[id,id]/[物件,物件]

author_obj.books.add(4,3,2) # 通過關係關聯物件給多對多表裡author_id=8新增book_id
author_obj.books.add(*models.Book.objects.filter(id__in=[1,2,3,4]))

remove 刪除多對多關係,[id,id]/[物件,物件]

author_obj.books.remove(1)
author_obj.books.remove(*models.Book.objects.filter(id__in=[4]))

clear 清空多對多關係

author_obj.books.clear()

create 新建一個book物件與當前的物件建立關係,在book表新建一個物件,並且和當前的作者進行關聯

author_obj.books.create(name='alexdsb',pub_id=2)

book_obj = models.Book.objects.get(id=1) # 獲取到id=1的book物件
print(book_obj.author_set.all()) # 通過反向查詢到對應的作者物件
book_obj.author_set.set([8,9,10]) # 通過作者物件給多對多表裡book_id=1的設定author_id
聚合與分組查詢
import os
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "day50_orm.settings")
import django
django.setup()
from app01 import models
from django.db.models import Max, Min, Avg, Count, Sum # 匯入聚合函式

聚合 aggregate,終止子句
ret = models.Book.objects.aggregate(Count('price')) # 統計有多少本書
print(ret)

統計出book_id大於3的書裡的最大價格和最低價格

ret = models.Book.objects.filter(id__gt=3).aggregate(max=Max('price'),min=Min('price'))

統計所有書裡最大價格和最低價格

ret = models.Book.objects.all().aggregate(max=Max('price'),min=Min('price'))
print(ret)
分組 annotate 註釋,給聚合之後的結果新增一個欄位

1.統計每一本書的作者個數

ret = models.Book.objects.annotate(Count('author__name')).values()
for i in ret:
print(i)

2.統計出每個出版社賣的最便宜的書

方法一、

ret = models.Publisher.objects.annotate(Min('book__price')).values()
for i in ret:
print(i)

方法二、

ret = models.Book.objects.values('pub','pub__name').annotate(Min('price'))
for i in ret:
print(i)

3.統計不止一個作者的圖書

ret = models.Book.objects.annotate(count=Count('author__name')).filter(count__gt=1)
print(ret)

4.根據一本圖書作者的數量的多少對查詢集 進行排序

方式一

ret = models.Book.objects.annotate(count=Count('author__name')).order_by('-count')
.values()

方式二

ret = models.Author.objects.values('books','books__name').annotate(count=Count('name'))
.order_by('-count')
for i in ret:
print(i)

5.查詢各個作者出的書的總價格

方式一

ret = models.Author.objects.annotate(sum=Sum('books__price')).values()

方式二

ret = models.Book.objects.values('author','author__name').annotate(sum=Sum('price'))
for i in ret:
print(i)
F和Q查詢
import os
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "day50_orm.settings")
import django
django.setup()
from app01 import models
from django.db.models import F, Q

F操作 - 用於欄位與欄位之間的比較
ret = models.Book.objects.filter(stock__gt=50) # 統計庫存大於50的書籍
for i in ret:
print(i)
ret = models.Book.objects.filter(sales__gt=F('stock')) # 統計銷量大於庫存的書籍
for i in ret:
print(i)

ret = models.Book.objects.all().update(stock=F('stock')+10) # 給所有書籍的庫存加10

ret = models.Book.objects.all().update(stock=F('stock')-10) # 給所有書籍的庫存減10

Q操作

與 &

或 |

非 ~

獲取id小於3或者id大於4的書籍

方式一、用exclude排除

ret = models.Book.objects.exclude(id__gte=3,id__lte=4)
print(ret)

方式二、用Q操作

ret = models.Book.objects.filter(Q(id__lt=3)|Q(id__gt=4))
print(ret)

獲取id小於3或者id大於4,且書名以‘少年’開頭的書籍

ret = models.Book.objects.filter(Q(id__lt=3) | Q(id__gt=4) & Q(name__startswith='少年'))
print(ret)

獲取id小於3=,且書名以‘少年’開頭的書籍

ret = models.Book.objects.filter(Q(id__lt=3) & Q(name__startswith='少年'))
print(ret)

獲取id小於3或者id大於4,且書名不以‘少年’開頭的書籍

ret = models.Book.objects.filter(Q(id__lt=3) | Q(id__gt=4) & ~Q(name__startswith='少年'))
print(ret)

獲取id小於3=,且書名不以‘少年’開頭的書籍

ret = models.Book.objects.filter(Q(id__lt=3) & ~Q(name__startswith='少年'))
print(ret)
orm效能相關
Debug_tool_bar

1 能用values的儘量不用物件.的形式來獲取資料
students = models.Student.objects.all().values('classes__name') #連結串列查詢,查詢一次 queryset[{'classes__name':'27期'},{'classes__name':'27期'},{'classes__name':'27期'}]
for s in students:
# print(s.classes.name) #查詢多次,每次點都進行一次sql查詢,效率低
print(s['classes__name'])

2 通過select_related直接進行連表查詢 針對一對一和一對多
students = models.Student.objects.all().select_related('classes')
for s in students:
print(s.classes.name)

3 通過prefetch_related子查詢來完成
students = models.Student.objects.all().prefetch_related('classes')
for s in students:
print(s.classes.name)

4 only和defer

	當我們進行orm查詢的時候,你通過翻譯出來的sql語句可以看到,每次查詢都是查詢了每個欄位的資料,所以我們通過only和defer,可以指定查詢哪些欄位資料
all_students = models.Student.objects.all().only('name')#只要這個欄位資料
all_students = models.Student.objects.all().defer('name')#排除,除了這個欄位其他欄位資料都要

事務
import os
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "day50_orm.settings")
import django
django.setup()
from app01 import models
from django.db.models import F
from django.db import transaction

try:
with transaction.atomic():
models.Book.objects.all().update(stock=F('stock') + 20)
models.Book.objects.all().update(sales=F('sales') - 20)
except Exception as e:
print(e)