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)