必知必会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)