今日内容回顾(Django之查询相关操作)

今日内容回顾

正反向查询进阶操作

 # 1.查询主键为2的书籍对应的出版社名称及书名
        res = models.Publish.objects.filter(book__pk=2).values('name','book__title')
        print(res)  # <QuerySet [{'name': '南方出版社', 'book__title': 'python'}]>


 # 2.查询主键为3的书籍对应的作者姓名及书名
        res = models.Author.objects.filter(book__pk=3).values('name','book__title')
        print(res)
        """
        <QuerySet [{'name': 'tom', 'book__title': 'golang'},
        {'name': 'egon', 'book__title': 'golang'},
        {'name': 'jason1号', 'book__title': 'golang'}]>
        """

聚合查询

聚合查询通常情况下都是配合分组一起使用的,只要是跟数据库相关的模块

基本上都在django.db.models或django.db里面

# 通过一些聚合函数配合分组使用得到所需要的数据,没有分组之前也可以通过aggregate关键字配合使用。
	# 聚合函数:max、min、sum、avg、count

    
# 使用前需要先导入 
from django.db.models import Max,Min,Sum,Avg,Count


res = models.Book.objects.aggregate(Count('pk'),Avg('price'),Max('price'))
    print(res)  # {'pk__count': 10, 'price__avg': Decimal('32533.326000'), 'price__max': Decimal('38888.88')}


分组查询

分组有一个特性,默认只能够直接获取分组的字段,其它字段需要使用方法。

我们也可以忽略该特性,在sql_mode中only_full_group_by欸之移除即可

#分组查询 需要使用到分组关键字 	annotate

"""
	1.按照整条数据分组
		models.Book.objects.annotate()
		根据表名,将该表内的一条条数据进行分组
		
	2. 按照表中某个字段分组
		models.Book.objects.values('字段名').annotate()
		按照annotate前面的values括号内填写的指定字段分组

"""

# 按照整条数据分组
	# 示例1,统计每一本书的作者个数
    
    res = models.Book.objects.annotate(A_C= Count('authors__PK')).values('A_C')
    print(res)

	# 示例2,统计出每个出版社卖的最便宜的书

    res = models.Publish.objects.annotate(P_P = Min('book__price')).values('name','P_P')
    print(res)
    """
    <QuerySet [{'name': '南方出版社', 'P_P': Decimal('28888.88')},
     {'name': '北方出版社', 'P_P': Decimal('18888.88')}, 
     {'name': '西方出版社', 'P_P': Decimal('26666.66')}
    , {'name': '东方出版社', 'P_P': Decimal('36666.66')},
     {'name': '中原出版社', 'P_P': Decimal('38888.88')}]>
    
    """

# 按照表中某个字段分组
    # 统计每个出版设出了几本书
    res = models.Book.objects.values('publish_id').annotate(数量=Count('pk')).values('数量','publish__name')
    print(res)  # <QuerySet [{'publish__name': '南方出版社', '数量': 1}, 
    # {'publish__name': '北方出版社', '数量': 5}, 
    # {'publish__name': '西方出版社', '数量': 2},
    # {'publish__name': '东方出版社', '数量': 1}, 
    # {'publish__name': '中原出版社', '数量': 1}]>
    

F与Q查询

  • F查询

    查询条件不是自定义的而是来至表中其它字段

    # F查询
        # 1.查询卖出数大于库存数的书籍
        """
        能够直接获取到表中某个字段对应的数据
        """
        from django.db.models import F
        # res = models.Book.objects.filter(stock__gt=F('sales_volume'))
        # print(res)  # <QuerySet [<Book: 对象:golang>, <Book: 对象:linux>, <Book: 对象:c>, <Book: 对象:java>, <Book: 对象:javascript>, <Book: 对象:django>]>
    
        # 2.将所有书籍的价格提升500块
        models.Book.objects.update(price=F('price')+500)
    
        # 3.将所有书的名称后面加上爆款两个字
    models.Book.objects.filter(pk=5).update(title=F('title') + '爆款') 
        """
        在操作字符类型的数据的时候 F不能够直接做到字符串的拼接
        """
        from django.db.models.functions import Concat
        from django.db.models import Value
        # 需要使用Concat及Value
        models.Book.objects.update(title=Concat(F('title'),Value("爆款")))
        
    
  • Q查询

    可以改变filter括号内多个条件之间的逻辑运算符,还可以将查询条件的字段改为字符串形式

    # Q查询 可以改变filter括号内多个条件之间的逻辑运算符
        # 1.查询卖出数大于3000或者价格大于38000的书籍
        	res = models.Book.objects.filter(sales_volume__gt=3000,price__gt=38000)
        	print(res)  # <QuerySet []>
        # filter括号内的参数是and关系
        # 导入Q用Q来操作
        from django.db.models import Q
        # | 管道符or 关系
        # res = models.Book.objects.filter(Q(sales_volume__gt=3000)|Q(price__gt=38000))
        # print(res)  # <QuerySet [<Book: 对象:c爆款>, <Book: 对象:mysql爆款>]>
        # ~ 波浪号为not关系
        res = models.Book.objects.filter(~Q(sales_volume__gt=3000) | Q(price__gt=38000))
        print(res)
    
    # Q查询 还可以将查询条件的字段改为字符串形式
    	q = Q()
        q.connector = 'or'
        q.children.append(('sales_volume__gt', 3000))
        q.children.append(('price__gt', 38000))
        res = models.Book.objects.filter(q)  # 默认还是and关系
        print(res)  # <QuerySet [<Book: 对象:c爆款>, <Book: 对象:mysql爆款>]>
    
    

ORM查询优化

django orm自带limit分页,便于减轻数据库以及服务端的压力

django orm默认都是惰性查询,当orm的语句在后续的代码中真正需要使用的时候才会执行。

# ORM查询优化之only与defer

# 1. only
    book_obj = models.Book.objects.only('title','price')
    for obj in book_obj:
        print(obj.title)
        print(obj.price)
        print(obj.publish_time)
        
   """
    在使用only关键字时,only会将括号内填写的字段封装成一个个数据对象
    对象点击的时后不会再去数据库查询
    
    如果点击没有在括号内的字段,则每次都会重新去数据库查询找该字段对应的数据
    
    可以简单的理解为,筛选出括号内的字段数据并保存起来,便于以后调用
    不在括号内的字段数据每次调用则需要重新查询数据库
    """

# 2. defer
    book_obj = models.Book.objects.defer('title','price')
    for obj in book_obj:
        print(obj.title)
        print(obj.price)
        print(obj.publish_time)
        
        """
        defer与only刚好相反
            数据对象点击括号内出现的字段,每次都会走数据库查询
            数据对象点击括号内没有的字典,不会走数据库查询
    
        可以简单的理解为,筛选出括号内的其它字段数据并保存起来,便于以后调用
        在括号内的数据每次调用则需要重新查询数据库
    
    	"""
        
# ORM查询优化之select_related与prefetch_related
    res = models.Book.objects.all()
    for obj in res:
         print(obj.publish.name)  # 频繁走数据库查询

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

# 4. prefetch_rlated
	res = models.Book.objects.prefetch_related('publish')
    for i in res:
        print(i.publish.name)
        
    """
    	prefetch_related底层其实是子查询
    	将查询之后的结果也一次性封装到数据对象中,用户在使用的时候是感觉不出来的。
    """


事务操作

事务:ACID

原子性:不可分割的最小单位

一致性:跟原子性是相辅向成

隔离性:事务之间互相不干扰

持久性:事务一旦确人永久性

开启事务transaction	回滚rollback	确认commit	返回上一次savepoint 

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



模型层常见字段

AutoField------主键字段 (primary_key=True)
	- int 自增列 必须填入(primary_key=True)
    
    
BigAutoField(AutoField)
	- bigint自增列,必须填入参数 (primary_key=True)

CharField---varchar
	verbose_name---字段的注释
    max_length---长度
    - 字符类型,必须提供max_length参数表示字符长度
    
IntegerField---int
	- 整数列(有负号),-2147483648 ~ 2147483647

BigIntegerField---bigint
	- 长整型(有负号的) -9223372036854775808 ~ 9223372036854775807

DecimalField
	- 10进制小数
    - 参数
	max_digits=8	小数总长度,表示八位
    decimal_places=2	小数位长度,表示俩位
    
    
EmailField---varchar(254)
	- 字符串类型,Django Admin以及ModelForm中提供验证机制

DateField---date
DateTimeField---datetime
	auto_now:每次修改数据的时候都会自动更新当前时间
	auto_now_add:只在创建数据的时候记录创建时间后续不会自动修改了

BooleanField---布尔值类型
	该字段传布尔值(Flase/True)		数据库里面存0/1

TextField---文本类型
	该字段可以用来存大段内容(文章、博客...)没有字数限制
    
FileField---字符类型
	upload_to = "/data"
    给该字段传一个文件对象,会自动将文件保存到/data目录下然后将文件路径保存到数据库中---/data/a.txt
    
    
# django 除了提供很多字段类型之外,还支持自定义字段
class MyCharField(models.Field):
    def __init__(self,max_length,*args,**kwargs):
        self.max_length = max_length
        # 重新调用父类的init方法
        super().__init__(max_length=max_length,*args,**kwargs)  #一定要是关键字的形式传入
        
     def db_type(self,connection)
     	# 返回真正的数据类型及各种约束条件
        return 'char(%s)'%self.max_length

# 自定义字段使用
myfield = MyCharField(max_length=32,null=True)

模型层常见参数

max_length
	max_length=32
	-- 设置字段字符的长度

verboses_name
	verboses_name="注释"
	-- 添加字段的注释

auto_now
	auto_now=True
	-- 每次修改数据的时候都会自动更新当前时间

auto_now_add
	auto_now_add=True
	-- 只在创建数据的时候记录创建时间后续不会自动修改了

null
	null=True
	-- 用于表示某个字段可以为空

defaulf
	defaulf="默认值"
	-- 为该字段设置默认值

max_digits

decimal_places

unique=True
	-- 如果等于True则表示该字段在此表中是唯一的

db_index=True
	如果db_index=True 则代表该字段设置索引

choices
	当字段数据的可能性是可以完全列举出来的时候,可以考虑使用该参数
    如:性别、学历、成绩...
    
    如下:
    	class UserInfo(models.Model):
            username= models.CharField(max_length=32)
            gender_choice=((1,男),(2,女))
    	    gender = models.IntegerField(choices=gender_choice)
            
            	userinfo_obj.get_gender_display()
    

to
	设置要关联的表 如 to="表名"

to_field
	设置要关联的表的字段,默认不写关联的就是另一张表的主键字段

related_name

on_delete
	当删除关联表中的数据时,当前表与其关联的行的行为
    django1.x中默认级联更新级联删除,
    django2.x、3.x、4.x,需要自己手动指定定外键字段的级联更新级联删除。
	# 示例:on_delete = models.CASCADE
	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
    什么都不做,一切都看数据库几倍的约束,注意数据库级别的默约束为RESTTRICT
    这个约束与django中的models.PROTECT相似


多对多的三种创建方式

  • 自动创建

    authors = models.ManyToManyField(to="Author")
    # 优:第三张表自动创建,无需自己手动创建		缺:第三张表扩展性差
    
  • 手动创建

    class Book(models.Model):
        pass
    class Author(models.Model):
        pass
    class Book2Author(models.Model):
        book_id = models.Foreignkey(to="Book")
        author_id = models.ForeginKey(to="Author")
    # 优:第三张表扩展性强
    # 缺:无法使用正反向查询及多对多的四个方法 add、set、remove、clear
    
  • 半自动创建

    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")
    
    # 优:扩展性强并支持正反向查询
    # 缺:无法使用多对多四个方法 add、set、remove、clear
    
posted @ 2022-09-06 20:40  瓮小辉  阅读(74)  评论(0)    收藏  举报