Django 06
今日内容详细
一、正反向查询进阶操作
正反向查询进阶操作
1.查询主键为1的书籍对应的出版社名称及书名
res = models.Publish.objects.filter(book__pk=1).values('name','book__title')
print res
2.查询主键为3的书籍对应的作者姓名及书名
res = models.Author.objects.filter(book__pk=3).values('name','book__title')
print(res)
3.查询jason作者的电话号码和地址
res = models.AuthorDetail.objects.filter(author__name='jason').values('phone','addr')
print(res)
4.查询南方出版社出版的书籍名称和价格
res = models.Book.objects.filter(publish__name='南方出版社').values('title','price')
print(res)
5.查询jason写过的书的名称和日期
res = models.Book.objects.filter(author__name='jason').values('title','publish_time')
print(res)
6.查询电话是110的作者姓名和年龄
res = models.Author.objects.filter(author_detil__phone=110).values('name','age')
print(res)
7.查询主键为1的书籍对应的作者电话号码
res = models.Author.objects.filter(book__pk=1).values('author_detail__phone')
res = models.Author_detail.objects.filter(author__book__pk=1).values('phone')
二、聚合查询
聚合函数:max、min、sum、avg、count
''' 聚合查询 ''' (先导入模块才可以使用)
from django.db.models import Max、Min、Sum、Avg、Count
没有分组之前,如果想要单纯的使用聚合函数,需要关键字 aggregate
res = models.Book.objects.aggregate(Max('price'),Min('price'),Sum('price'),Avg('price'),Count('price'))
print(res)
三、分组查询
"""
分组有一个特性,默认只能够直接获取分组的字段,其他字段需要使用方法
我们也可以忽略掉该特性,将sql_mode中only_full_group_by配置移除即可
"""

‘’分组查询‘’
1.示例一:统计每一本书的作者个数
①代码:
res =models.Book.objects.annotate(author_num=Count('authors__pk')).values('title','author_num')
print(res)
res1 = models.Book.objects.values('publish_id').annotate(book_num=Count('pk')).values('publish_id','book_num')
print(res1)
②解析:
"""
1.按照整条数据分组
models.Book.objects.annotate() 按照一条条书籍记录分组
2.按照表中某个字段分组()
models.Book.objects.values('title').annotate() 按照annotate之前values括号中指定的字段分组
"""
2.示例二:统计出每个出版社卖的最便宜的书的价格
①代码:
res = models.Publish.objects.annotate(min_price=Min('book_price')).values('name','min_price')
print(res)
②效果展示:

3.示例三:统计不止一个作者的图书
‘’filter在annotate前面则是where(先筛选后分组),在annotate后面则是having(先分组后筛选)‘’
res = models.Book.objects.annotate(author_num=Count('author__pk')).filter(author_num__gt=1).values('title','author_num')
print(res)
4.示例四:查询各个作者出的书的总价格
res = modles.Author.objects.annotate(book_sum_price=Sum('book__price')).values('name','book_sum_price')
print(res)
四、F与Q查询【非常重要】
'''当表中已经有数据的情况下,添加新的字段需要指定一些参数
1.设置字段值允许为空 null=True
2.设置字段默认值 default=1000
3.在终端中直接给出默认值 1 provide a default value
'''
strong_num = models.IntegerField(verbose_name='库存数',null=True)
sale_num = models.IntegerField(verbose_name='卖出数',default=1000)
'''F查询:查询条件不是自定义的而是来自于表中其他字段'''
from django.db.models import F
# 1.查询库存数大于卖出数的书籍
res = models.Book.objects.filter(storage_num__gt=F('sale_num'))
print(res)
# 2.将所有的书籍价格上涨1000块
models.Book.objects.update(price=F('price'+1000))
# 3.将所有的书籍名称加上爆款后缀
models.Book.objects.filter(pk=5).update(title=F('tilte')+'爆款') 针对字符串数据无法直接拼接

这才是正确答案>>>:
from django.db.models.functions import Concat
from django.db.models import Value
ret3 = models.Book.objects.filter(pk=5).update(title=Concat(F('title'),Value('爆款')))
# 这里F有一个限制,如果你操作的不是数字类型,而是字符串类型,这样的情况需要再次导入一个模块
'''Q查询:可以改变filter括号内多个条件之间的逻辑运算符,还可以将查询条件的字段改为字符串形式'''
res = models.Book.objects.filter(pk=1,publish_id=3)
print(res)
返回的结果是 QureySet[]
因为,filter默认只有一个and关系,所以上面filter括号内的两个条件都要满足,如果想要用到‘或’这个方法,就需要用到‘Q’
改变filter括号内多个条件之间的逻辑运算符>>>:
res = models.Book.objects.filter(Q(pk=1),Q(publish_id=3))
# 像这样逗号隔开的依旧是and关系
res = models.Book.objects.filter(Q(pk=1)|Q(publish_id=3))
# 管道符分割才是or的关系
res = models.Book.objects.filter(~Q(pk=1)|Q(publish_id=3))
# 波浪号是not关系
print(res.query)
将查询条件的字段改为字符串形式'''>>>:
q_obj = Q()
q_obj.connector = 'or' # q对象默认的多个条件也是and关系,可以修改为or
q_obj.children.append('pk',1)
q_obj.children.append(('publish_id',3))
res = models.Book.objects.filter(q_obj)
print(res)
五、ORM查询优化
django orm默认都是惰性查询
当orm的语句在后续的代码中真正需要使用的时候才会执行
django orm自带limit分页
减轻数据库端以及服务端压力(肉眼是看不见的,在底层操作)
''' ORM查询优化之only与defer '''
1.only
①only的用法
res = models.Book.objects.only('title','price')
for obj in res:
print(obj.title) # only后面括号内含有的字段名
print(obj.price) # only后面括号内含有的字段名
print(obj.publish_time) # only后面括号内没含有的字段名
②only的作用
only会将括号内填写的字段封装成一个个数据对象,对象在点击的时候不会再走数据库查询
但是对象也可以点击括号内没有的字段,只不过每次都会走数据库查询
2.defer
defer与only刚好相反
数据对象点击括号内出现的字段,每次都会走数据库查询
数据对象点击括号内没有出现的字段,不会走数据库查询
''' ORM查询优化之select_related与prefetch_related '''
①select_related
res = models.Book.objects.all()
for obj in res:
print(obj.publish.name) # 频繁走数据库查询

res = models.Book.objects.select_related('publish')
"""
select_related括号内只能接收外键字段(一对多 一对一) 自动连表 得出的数据对象在点击表中数据的时候都不会再走数据库查询
"""
for obj in res:
print(obj.publish.name)

②prefetch_related
res = models.Book.objects.prefetch_related('publish')
for obj in res:
print(obj.publish.name)
''' prefetch_related 底层其实是子查询 ,将查询之后的结果也一次性封装到数据对象中,用户在使用的时候是感觉不出来的 '''
六、事务相关操作❓
1.事务:ACID
2.事务隔离级别: 脏读 幻读 不可重复读 ...
3.原生SQL:start transaction\rollback\commit\savepoint
from django.db import transaction
try:
with transaction.atomic():
pass # 多余ORM语句
except Exception as e:
print(e)
七、模型层常见字段
| AutoField() | int自增列,必须填入参数 primary_key=True。当model中如果没有自增列,则自动会创建一个列名为id的列 |
| CharField() | 字符类型,必须提供max_length参数, max_length表示字符长度 |
| IntegerField() | 整数类型,范围在 -2147483648 to 2147483647 |
| BigIntegerField() | 长整型(有符号的),范围在 -9223372036854775808 ~ 9223372036854775807 |
| DateField() | 日期字段,日期格式 YYYY-MM-DD,相当于Python中的datetime.date()实例 |
| DateTimeField() | 日期时间字段,格式 YYYY-MM-DD HH:MM[:ss[.uuuuuu]][TZ],相当于Python中的datetime.datetime()实例 |
| DecimalField() | 10进制小数 |
| EmailField() | 字符串类型,Django Admin以及ModelForm中提供验证机制 |
| BooleanField() | 传布尔值,将传入的True存为1,传入的False存为0 |
| TextField() | 存储大段文本 |
| FileField() | 存储文件数据,自动找指定位置存储,字段存具体路径 |
| ForeignKey() | 外键类型在ORM中用来表示外键关联关系 |
| OneToOneField() | 通常一对一字段用来扩展已有字段 |
| ManyToManyField() | "关联管理器"是在一对多或者多对多的关联上下文中使用的管理器。它存在于两种情况:1.外键关系的反向查询,2.多对多关联关系 |
ORM还支持自定义字段
class MyharField(models.Field):
def __init__(slef,max_length,*args,**kwargs):
self.max_length = max_length
super().__init__(max_length=max_length,*args,**kwargs)
def db_type(self,connection):
return 'char(%s)' % self.max_length
info = MyCharField(max_length=32)

这里要用关键字参数传参
八、orm常见字段参数
| 参数 | 描述 |
|---|---|
| max_length | CharField 字段类型长度 |
| verboses_name | 给字段设置别名 |
| auto_now | 配置上auto_now=True,每次更新数据记录的时候会更新该字段 |
| auto_now_add | 配置auto_now_add=True,创建数据记录的时候会把当前时间添加到数据库 |
| null | 用于表示某个字段可以为空 |
| default | 为该字段设置默认值 |
| max_digits | 作用于 DecimalField 字段类型,表示小数总位数 |
| decimal_places | 作用于 DecimalField 字段类型,表示小数后的位数 |
| unique=True | 如果设置为unique=True 则该字段在此表中必须是唯一的 |
| db_index=True | 如果db_index=True 则代表着为此字段设置索引 |
| to | 设置要关联的表 |
| to_field | 设置要关联的表的字段 |
| related_name | 改变正反查询时的名字 |
| on_delete | 当删除关联表中的数据时,当前表与其关联的行的行为 |
当字段数据的可能性是可以完全列举出来的时候,应该考虑使用该参数
性别、学历、成绩
class UserInfo(models.Model):
username = models.CharField(max_length=32)
gender_choice = (
(1,'男性'),
(2,'女性'),
(3,'其他')
)
gender = models.IntegerField(choices=gender_choice)
userinfo_obj.get_gender_display()
to
to_field
related_name
on_delete
1、models.CASCADE
级联操作,当主表中被连接的一条数据删除时,从表中所有与之关联的数据同时被删除
2、models.SET_NULL
当主表中的一行数据删除时,从表中所有与之关联的数据的相关字段设置为null,此时注意定义外键时,这个字段必须可以允许为空
3、models.PROTECT
当主表中的一行数据删除时,由于从表中相关字段是受保护的外键,所以都不允许删除
4、models.SET_DEFAULT
当主表中的一行数据删除时,从表中所有相关的数据的关联字段设置为默认值,此时注意定义外键时,这个外键字段应该有一个默认值
5、models.SET()
当主表中的一条数据删除时,从表中所有的关联数据字段设置为SET()中设置的值,与models.SET_DEFAULT相似,只不过此时从表中的相关字段不需要设置default参数
6、models.DO_NOTHING
什么都不做,一切都看数据库级别的约束,注数据库级别的默认约束为RESTRICT,这个约束与django中的models.PROTECT相似
九、多对多三种创建方式
1.自动创建
authors = models.ManyToManyField(to='Author')
优点:第三张表自动创建
缺点:第三张表扩展性差
2.手动创建
class Book(models.Model):
pass
class Author(models.Model):
pass
class Book2Author(models.Model):
book_id = models.ForeignKey(to="Book")
author_id = models.ForeignKey(to="Author")
优点:第三张表扩展性强
缺点:无法使用正反向查询以及多对多四个方法
3.半自动创建
class Book(models.Model):
authors = models.ManyToManyField(to='Author',
through='Book2Author'
through_fields=('book_id','author_id')
)
class Author(models.Model):
pass
class Book2Author(models.Model):
book_id = models.ForeignKey(to="Book")
author_id = models.ForeignKey(to="Author")
优点:扩展性强并且支持正反向查询
缺点:无法使用多对多四个方法

浙公网安备 33010602011771号