正反向查询与ORM操作补充
正反向进阶操作
1.查询主键为1数据对应的出版社名称及书名
res = models.Publish.objects.filter(book__pk=1).values('name', 'book__title')
print(res) # <QuerySet [{'name': '中信书院出版社', 'book__title': '富婆追求指南'}]>
2.查询主键为3的书籍对应的坐着姓名及书名
res = models.Author.objects.filter(book_pk=3).values('name','book__title')
print(res) # <QuerySet [{'name': zey, 'book__title': '被讨厌的勇气'}]>
3.查询joseph坐着的电话号码和地址
res = models.AuthorDetail.objects.filter(author__name='joseph').values('phone', 'addr')
print(res) # <QuerySet [{'phone': 999, 'addr': '北京'}]>
4.查询中信书院出版社的书籍名称和价格
res = models.Book.objects.filter(publish__name='中信书院出版社').values('title', 'author__author_detail__addr')
print(res) # <QuerySet [{'title': '富婆追求指南', 'author__author_detail__addr': '北京'}
5.查询joseph写过的书的名称和日期
res = models.Book.objects.filter(author__name='joseph').values('title','publish_time')
print(res) # <QuerySet [{'title': '富婆追求指南', 'publish_time': datetime.datetime(2022, 9, 6, 8, 54, 4, 451528, tzinfo=<UTC>)}]>
6.查询电话是999的坐着姓名和年龄
res = models.Author.objects.filter(author_detail__phone=999).values('name','age')
print(res) # <QuerySet [{'name': 'joseph', 'age': 21}]>
7.查询主键为1的书籍对应的坐着电话号码
res = models.AuthorDetail.objects.filter(book__pk=1).values('author_detail__phone')
print(res) # <QuerySet [{'author_detail__phone':999}]>
聚合查询
1.聚合函数
max(最大值)、Min(最小值)、Sum(求和)、avg(平均值)、Count(次数)
2.聚合查询
没有分组之前如果需要使用聚合函数,那么就需要使用关键字aggregate
from django.db.models import Max, Min, Sum, Avg, Count
res = models.Book.objects.aggregate(Max('perice'), Min('price'),Sum('price'),Avg('price'),Coun('pk'))
print(res)
分组查询
1.在分组中有一个特征默认只能够直接获取分组的字段,其他字段需要使用方法才可以获得,我们需要到cmd中调用mysql修改配置,将sql_mode中的only_full_froup_by配置移除即可
2.按照整条数据分组
models.Book.objects.annotate()按照一条条书籍记录分组
3.按照表中某个字段分组()
models.Book.object.values('title').annotate() 按照annotate之前values括号中指定的字段分组进行分组
查看配置环境:show variables like '%mode%';
修改配置:set global sql_mode = 'STRICT_TRANS_TABLES';
统计每一本书的坐着个数
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) # <QuerySet [{'publish_id': 1, 'book_num': 8}, {'publish_id': 2, 'book_num': 4}, {'publish_id': 3, 'book_num': 4}]>
统计出每个出版社卖出最便宜的书的价格
res = models.Publish.objects.annotate((min_price=Min('book__price')).values('name','min_price'))
print(res)
统计:统计不知一个作者的图书(filter在annotate前面表示的是sql中的where,如果在annotate后面则是having)
res = models.Book.objects.annotate(author_num=Count('authors_pk')).filter(author_num__gt=1).values('title', 'author_num')
print(res)
查询各个作者出的书的总价格
res = models.Author.object.annotate(book_sum_price = Sum('book__price').values('name','book_sum_price'))
print(res)
F查询
1.当表中已经有数据的情况下,添加新的字段需要指定一些参数
1.设置字段值允许为空 null=None
2.设置字段默认值 default=520
3.在终端中直接给出默认值
2.F查询:查询条件不是自定义的而是来自于表中其他的字段
from django.db.models import F
1.查询库存数大于卖出数的书籍
res = models.Book.objects.filter(storage_num__gt=F('sale_num'))
print(res) # <QuerySet [<Book: 书籍对象:富婆追求指南>, <Book: 书籍对象:做一个别人>, <Book: 书籍对象:如何与前半生和解>,
2.将所有书籍价格上涨520元
models.Book.objects.filter(price=F('price')+520)
3.将所有书籍名称加上爆款后缀
models.Book.objects.update(title=F('title')+'爆款') # 针对字符串无法直接拼接,只有数字可以直接拼接
from django.db.models.functions import Concat
from django.db.models import Value
res = models.Book.objects.filter(pk=5).update(title=Concat(F('title'),Value('爆款')))
Q查询
1.在F查询中默认连接为and,然而filter不支持or或者not查询,那么就需要使用Q查询
2.Q查询可以改变filter括号内多个条件之间的逻辑运算符,还可以将查询条件的字段改变为字符串形式
3.Q查询的or关系是管道符,not关系是波浪号
from django.db.models import Q
res = models.Book.objects.filter(Q(pk=1) | Q(publish_id=3))
4.Q查询将查询条件字段改为字符串形式
q_obj = Q()
q_obj.connector = 'or' # q对象默认的多个条件也是and关系,考科一修改成or关系
q_obj.children.ppend(('pk',1))
q.obj.children.append(('publish_id', 3))
res = models.Book.objects.filter(q_obj)
print(res)
ORM查询优化
1.ORM特性
django orm默认都是惰性查询
当orm的语句在后续的代码中真正需要使用的时候才会执行
django orm自带limit分页特性
用以减轻数据库端以及服务器的压力
2.四种查询方式
only only会将括号内填写的字段封装成一个个数据对象对象在点击的时候不会再走数据库查询,但是对象也可以点击括号内没有的字段,只不过每次都会走数据库
res = models.Book.objects.only('title','price')
for obj in res:
print(obj.title)
print(obj.price)
print(obj.publish_time)
defer 与only正好相反数据对象点击括号内出现的字段每次都会走数据库查询,数据对象点击括号内没有的字段那么就不会走数据库查询
res = models.Book.objects.defer('title', 'price')
for obj in res:
# print(obj.title)
# print(obj.price)
print(obj.publish_time)
select_related select_related括号内只能接受外键字段(一对多,一对一)自动连表得出的数据对象在点击表中数据的时候都不会再走数据库查询
res = models.Book.objects.all()
for obj in res:
print(obj.publish.name) # 频繁走数据库查询
res = models.Book.objects.select_related('authors')
prefetch_related prefetch_related底层其实是子查询,将查询之后的结果也是一次性封装到数据对象中,用户在使用的时候是感觉不出来的
for obj in res:
print(obj.publish.name)
res = models.Book.objects.prefetch_related('publish')
for obj in res:
print(obj.publish.name)
事务相关操作
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)
ORM常见字段类型
字段 |
功能 |
AutoField() |
自动增值的id字段 |
CharDield() |
字符串字段 |
IntegerField() |
整形 |
BigIntegerField() |
长整形 |
DateField() |
日期类型date |
DateTimeField() |
日期时间类型 |
DecimalField() |
十进制小数类型 |
EmailField() |
一个带有检查Email合法性的CHarField |
BooleanField() |
布尔类型 |
TextField() |
文本类型 |
FileField() |
文件类型 |
ForeignKey() |
主键 |
OneToOneField() |
一对一 |
ManyToManyField() |
多对多 |
1.自定义字段
class MyCharField(models.Field):
def __init__(self, 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 |
字段长度 |
verboses_name |
admin中字段的显示名称,如果不设置该参数时,则与属性名 |
auto_now |
自动创建,无论添加或修改都是当前操作时间 |
auto_now_add |
自动创建,永远是闯进时的时间 |
null |
如果是True,django会在数据库此字段置为null,默认为False |
default |
该字段设置默认值 |
max_digits |
最大位数 |
decimal_places |
显示数字的小数位数 |
unique=True |
不允许重复 |
db_index |
数据库索引 |
choices |
是一个二维的元组用作choices,如果这样定义,Django会select box代替普普通通的文本框,并且限定choice的值是元组中的值 |
当字段数据的可能性是可以完全列举出来的时候 应该考虑使用该参数
性别
学历
成绩
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 |
设置要关联的字段 |
on_delect |
当删除关联表中数据 |
models.CASCADE |
删除关联数据,与之关联也删除 |
db-constraint |
是否在数据库中创建外键约束,默认为True |
related_name |
相关对象名称 |
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")
优点:扩展性强并且支持正反向查询
缺点:无法使用多对多四个方法