django入门菜鸟教程:ORM其他查询操作补充与查询优化

2022-09-1811:06:55后端程序开发Comments752 views字数 6859阅读模式

正反向查询进阶操作

我们之前的查询都是通过点已知条件来查询结果,这次我们学习怎样通过点所求结果来查询。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/27824.html

# 1.查询主键为1的书籍对应的出版社名称及书名
方式1:models.条件所在的表.objects.filter
res = models.Book.objects.filter(pk=1).values('publish__name', 'title')

方式2:models.结果所在的表.objects.filter
res = models.Publish.objects.filter(book__pk=1).values('name', 'book__title')
# 2.查询主键为3的书籍对应的作者姓名及书名
res = models.Author.objects.filter(book__pk=2).values('name', 'book__title')
# 3.查询jason的作者的电话号码和地址
res = models.AuthorDetail.objects.filter(author__name='jason').values('phone', 'address')
# 4.查询南方出版社出版的书籍名称和价格
res = models.Book.objects.filter(publish__name='南方出版社').values('title', 'price')
# 5.查询jason写过的书的名称和日期
res = models.Book.objects.filter(authors__name='jason').values('title', 'join_time')
# 6.查询电话是110的作者姓名和年龄
res = models.Author.objects.filter(author_detail__phone=110).values('name', 'age')
# 7.查询主键为1的书籍对应的作者电话号码
res1 = models.Book.objects.filter(pk=1).values('authors__author_detail__phone')
res2 = models.Author.objects.filter(book__pk=1).values('author_detail__phone')
res3 = models.AuthorDetail.objects.filter(author__book__pk=1).values('phone')

聚合查询

聚合函数:Max, Min, Sum, Avg, Count文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/27824.html

使用前先导模块
from django.db.models import Max, Min, Count, Sum, Avg

'没有分组之前使用聚合函数,需要关键字aggregate'

# 查询书籍表中价格最大、最小、平均、总和、计数。
res = models.Book.objects.aggregate(Max('price'), Min('price'), Avg('price'),Count('pk'), Sum('price'))
print(res)

分组查询

分组查询有一个特性:默认只能直接查询参与分组的字段,例如: selecct age from user group by age 想要查询其他字段需要使用concat方法。我们可以在配置文件中修改参数来忽略这个特性:去my.ini文件中的sql_mode参数中,去掉only_full_group_by即可文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/27824.html

ORM分组查询:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/27824.html

分组方式:
1.按照整条数据(一条记录)分组	'models.后面点什么,就是按照什么分组'
	models.Book.objects.annotate()  # 按照一条条书籍记录分组
2.按照表中某个字段分组	'values如果在annotate前面,那么就是按照values括号内的字段分组'
	models.Book.objects.values('title').annotate()	# 按照annotate之前values括号中的字段分组

    
# 示例1:统计每一本书的作者个数
res = models.Book.objects.annotate(author_num=Count('authors__pk')).values('title', 'author_num')
# 示例2:统计出每个出版社卖的最便宜的书的价格
res = models.Publish.objects.annotate(min_price=Min('book__price')).values('name', 'min_price')
# 示例3:统计不止一个作者的图书
res = models.Book.objects.values('authors').annotate(author_num=Count('authors__pk'))\
.filter(author_num__gte=1).values('title', 'author_num')
print(res)
# 示例4:查询各个作者出的书的总价格
res = models.Author.objects.annotate(book_sum_num=Sum('book__price')).values('name', 'book_sum_num')

F与Q查询

补充知识:给已经有数据的表新增字段

方式1:允许新增字段结果为空 方式2:通过参数设置默认值 方式3:通过终端提示设置默认值文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/27824.html

eg:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/27824.html

class Book(models.Model):   
	# 新增字段:库存
    kucun = models.IntegerField(default=1000)
    # 新增字段:卖出
    maichu = models.IntegerField(null=True)

F查询:

查询条件不是自定义的而是来源于其他字段。F() 的实例可以在查询中引用字段,来比较同一个 model 实例中两个不同字段的值。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/27824.html

老规矩:使用前先导入
from django.db.models import F
先给书籍表中添加两个字段:库存与售出
# 1.查询库存数大于卖出数的书籍
res = models.Book.objects.filter(kucun__gt=F('maichu'))
# 2.将所有书籍的价格上涨1000块
models.Book.objects.update(price=F('price')+1000)
# 3.将id为5的书籍书籍名称加上爆款后缀
错误写法:
models.Book.objects.filter(pk=5).update(title=F('title') + '爆款')  # 针对字符串数据无法直接拼接
正确写法:
from django.db.models.functions import Concat
from django.db.models import Value
models.Book.objects.filter(pk=5).update(title=Concat(F('title'), Value('爆款')))

Q查询:

filter后面各个查询条件之间逗号分隔,默认是and关系,Q查询可以将其变为或、非文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/27824.html

res = models.Book.objects.filter(price__gt=2000, maichu__gt=1000)	# and
res = models.Book.objects.filter(Q(price__gt=2000), Q(maichu__gt=1000))	  # and
res = models.Book.objects.filter(Q(price__gt=2000) | Q(maichu__gt=1000))  # or
res = models.Book.objects.filter(~Q(price__gt=2000), ~Q(maichu__gt=1000))  # not

# 当条件是字符串时
q = Q()
q.connector = 'or'   # 通过这个参数可以将Q对象默认的and关系变成or
q.children.append(('price', 188.88))   # children和append是两个方法,就是将条件加进去(price=188.88)
q.children.append(('name', '裤子爆款'))
res = models.Product.objects.filter(q)   # 原先Q对象查询默认是and,但是现在改为or了,也就是在Product表中price=188.88或者name=裤子爆款的所有数据;
print(res)

ORM查询优化

django ORM查询特性:

1.ORM查询默认都是惰性查询,当orm查询语句的结果在后续代码中使用到才会去执行 2.ORM自带limit分页,目的是减轻数据库以及服务端的压力文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/27824.html

only与defer

res = models.Book.objects.all()  
# all返回的结果是列表套对象,但是all会查询表中所有的数据,数据量过大时,容易搞崩数据库
res = models.Book.objects.values('title','price')  
# filter查询结果是列表套字典,不能用句点符取值,

1.only
res = models.Book.objects.only('title','price')  # 查询结果后续用到,会走一次数据库
# only查询结果是列表套对象,可以用句点符。
for obj in res:
     print(obj.title)  # obj中有该属性,直接拿就好,不会走数据库查询
     print(obj.price)  # obj中有该属性,直接拿就好,不会走数据库查询
     print(obj.publish_time)  # obj中没有该属性,每查询一次,都会走一遍数据库
'only会将根据括号内的字段查询到的结果封装成一个数据对象,该对象在句点符点括号内包含的字段的时候不会走数据库查询。也可以点括号内没有的字段,不过每次点都会走数据库查询'
2.defer
res = models.Book.objects.defer('title', 'price')
for obj in res:
    print(obj.title)  # 括号内有该字段,会走数据库查询
    print(obj.price)  # 括号内有该字段,会走数据库查询
    print(obj.publish_time)  # 括号内没有有该字段,不会走数据库查询
'defer与only刚好相反:数据对象点击括号内出现的字段,每次都会走数据库查询;数据对象点击括号内没有的字典,不会走数据库查询.'

select_related与prefetch_related

res = models.Book.objects.all()
for obj in res:
     print(obj.publish.name)  # 频繁走数据库查询

res = models.Book.objects.select_related('authors')
# 这句话相当于select_related会将Book表与外键authors所关联的表 连接起来,把这张大表封装到书籍对象里边去。本质是连表操作,只走一次查询。
"""
    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)

ORM事务操作

事务:ACID
事务隔离级别:脏读 幻读 不可重复读 ...
原生SQL: start transaction\rollback\commit\savepoint 

from django.db import transaction
try:
    with transaction.atomic():
        pass  # 多条ORM语句
except Exception as e:
        print(e)

模型层常见字段

AutoField()
CharField()
IntegerField()
BigIntegerField()
DateField()
DateTimeField()
DecimalField()
EmailField()
BooleanField()
	传布尔值存数字01
TextField()
	存储大段文本
FileField()
	存储文件数据  自动找指定位置存储 字段存具体路径
ForeignKey()
OneToOneField()
ManyToManyField()
    
ORM还支持自定义字段
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)

模型层字段的常见参数

max_length
verboses_name
auto_now
auto_now_add
null
default
max_digits
decimal_places
unique=True
db_index=True
choices
	当字段数据的可能性是可以完全列举出来的时候 应该考虑使用该参数
    	性别
    	学历
    	成绩  
        
	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")
   	优点:扩展性强并且支持正反向查询
  	 缺点:无法使用多对多四个方法

作者:爱新觉罗吉星
来源:稀土掘金文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/27824.html

  • 本站内容整理自互联网,仅提供信息存储空间服务,以方便学习之用。如对文章、图片、字体等版权有疑问,请在下方留言,管理员看到后,将第一时间进行处理。
  • 转载请务必保留本文链接:https://www.cainiaoxueyuan.com/bc/27824.html

Comment

匿名网友 填写信息

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定