聚合查询,分组查询,F查询与Q查询,ORM查询优化,ORM字段类型和choice参数,ORM事务,ORM执行原生SQL,多对多创建第三张表

数据库:
#书籍
class Book(models.Model):
    title = models.CharField(max_length=32)
    price = models.DecimalField(max_digits=8,decimal_places=2)
    # 一对多
    publish = models.ForeignKey(to='Publish')
    # 多对多
    authors = models.ManyToManyField(to='Author')  # 自动创建书籍和作者的第三张关系表

#出版
class Publish(models.Model):
    title = models.CharField(max_length=32)

#作者
class Author(models.Model):
    name = models.CharField(max_length=32)
    # 一对一
    author_detail = models.OneToOneField(to='AuthorDetail')

#作者信息
class AuthorDetail(models.Model):
    addr = models.CharField(max_length=32)
    phone = models.BigIntegerField()

聚合查询(aggregate)

聚合查询返回值的数据类型是字典。
返回的字典中:键的名称默认是(属性名称加上__聚合函数名),值是计算出来的聚合值。
MySQL聚合函数:max\min\sum\count\avg
如果要自定义返回字典的键的名称,可以起别名:
  aggregate(别名 = 聚合函数名("属性名称"))

使用时需要导入对应模块
from django.db.models import Max, Min, Sum, Avg, Count

查找书籍中最便宜书籍的价格
    from django.db.models import Max, Min, Sum, Avg, Count
    res = models.Book.objects.aggregate(price_min = Min('price'))
    print(res) # {'price_min': Decimal('11111.00')}
'''没有分组也可以使用聚合函数 默认整体就是一组'''

分组查询(annotate)

MySQL分组操作:group by
ORM执行分组操作 如果报错 可能需要去修改sql_mode 移除only_full_group_by

返回值:
  分组后,用 values 取值,则返回值是 QuerySet 数据类型里面为一个个字典;
  分组后,用 values_list 取值,则返回值是 QuerySet 数据类型里面为一个个元组。

annotate 里面放聚合函数。
    values 或者 values_list 放在 annotate 前面:values 或者 values_list 是声明以什么字
  段分组,annotate 执行分组。
    values 或者 values_list 放在annotate后面: annotate 表示直接以当前表的pk执行分组,
  values 或者 values_list 表示查询哪些字段, 并且要将 annotate 里的聚合函数起别名,在 
  values 或者 values_list 里写其别名。

1.统计每本书的作者个数
    from django.db.models import Min,Min,Avg,Sum,Count
    res = models.Book.objects.annotate(munb = Count('authors__pk')).values('title','munb')
    print(res) 
    # <QuerySet [{'title': 'Python编程', 'munb': 2}, {'title': 'JAVA', 'munb': 1}, {'title': 'JS', 'munb': 2}]>

2.统计每个出版社卖的最便宜的书的价格
    from django.db.models import Min
    res = models.Publish.objects.annotate(book_min = Min('book__price')).values('title','book_min')
    print(res)
    # <QuerySet [{'title': '上海出版社', 'book_min': Decimal('100000.00')}, {'title': '浙江出版社', 'book_min': Decimal('11111.00')}]>

3.统计不止一个作者的图书
    from django.db.models import Count
    res = models.Book.objects.annotate(count = Count('authors__id')).filter(count__gt=1).values('title')
    print(res) # <QuerySet [{'title': 'Python编程'}, {'title': 'JS'}]>

4.统计每个出版社出版的书籍个数
    from django.db.models import Count
    res = models.Publish.objects.annotate(count = Count('book__id')).values('title','count')
    print(res) 
    # <QuerySet [{'title': '上海出版社', 'count': 2}, {'title': '浙江出版社', 'count': 1}]>

当表中已有数据的情况下,为表重新添加字段

为书籍表添加2个字段,销量和库存字段
#书籍
class Book(models.Model):
    title = models.CharField(max_length=32)
    price = models.DecimalField(max_digits=8,decimal_places=2)
    #销量
    sales = models.IntegerField(verbose_name='销量')
    #库存
    inventory = models.IntegerField(verbose_name='库存')
    # 一对多
    publish = models.ForeignKey(to='Publish')
    # 多对多
    authors = models.ManyToManyField(to='Author')  # 自动创建书籍和作者的第三张关系表

修改了模型代码后,一定要进行数据迁移。
执行迁移命令后会发现不成功
它给了两个选项:
  1)立即提供一次性默认值(在所有现有行上都设置为空值)
  2)退出,让我在Models.py中添加一个默认值。

解决方法:
  当表中已经有数据的情况下 添加额外的字段 需要指定默认值或者可以为null
	方式1
		IntegerField(verbose_name='销量',default=1000)
	方式2
		IntegerField(verbose_name='销量',null=True)
	方式3
		在迁移命令提示中直接给默认值

F查询和Q查询

F查询

如果想要对同一张表中的两个字段的值做比较,就需要用到 F()。
F() 的实例可以在查询中引用字段,来比较同一个 model 实例中两个不同字段的值。
用法:
  F("字段名称")

F 动态获取对象字段的值,可以进行运算。
Django 支持 F() 对象之间以及 F() 对象和常数之间的加减乘除和取余的操作。
修改操作(update)也可以使用 F() 函数。

首先需要导入模块
from django.db.models import F

1.查询库存大于销量的书籍
    from django.db.models import F
    res = models.Book.objects.filter(inventory__gt=F('sales'))
    print(res) # <QuerySet [<Book: Book object>, <Book: Book object>]>
    print(res[0].title,res[1].title,end='   ') # Python编程 JS  

2.将所有书的价格提升1000from django.db.models import F
    res = models.Book.objects.update(price = F('price') + 1000)
    print(res) # 3 返回受影响的行数

在已有的char数据类型的值后面添加内容

'''如果要修改char字段咋办(千万不能用上面对数值类型的操作!!!) 需要使用下列两个方法'''
from django.db.models.functions import Concat
from django.db.models import Value
res = models.Book.objects.update(title=Concat(F('title'), Value('爆款')))

Q查询

用法:
  Q(条件判断)

之前构造的过滤器里的多个条件的关系都是 and,如果需要执行更复杂的查询(例如 or 语句),就可以使用 Q 。
Q 对象可以使用 & | ~ (与 或 非)操作符进行组合。
优先级从高到低:~ & |。

可以混合使用 Q 对象和关键字参数,Q 对象和关键字参数是用"and"拼在一起的(即将逗号看成 
and ),但是 Q 对象必须位于所有关键字参数的前面。
使用时需要导入模块
  from django.db.models import Q

1.查询价格销量大于900或者名称以P开头的书籍的名称和价格。
    from django.db.models import Q
    res = models.Book.objects.filter(Q(sales__gt=900) | Q(title__startswith='P')).values('title','price')
    print(res) 
    # <QuerySet [{'title': 'Python编程', 'price': Decimal('102000.00')}, {'title': 'JAVA', 'price': Decimal('125456.00')}]>

Q对象的第二种用法

    from django.db.models import Q
    q = Q()  # 创建一个Q对象q
    q.connector = 'OR'  # 默认是and 可以改为or
    # 传入条件进行查询:
    q.children.append(('price__lt', 100000))
    q.children.append(('sales__gt', 600))
    res = models.Book.objects.filter(q) 
    # 等于models.Book.objects.filter(Q(price__lt=100000) | Q(sales__gt=600))
    print(res) # <QuerySet [<Book: Book object>, <Book: Book object>, <Book: Book object>]>

ORM查询优化

# 惰性查询:如果只是书写了orm语句,在后面根本没有用到该语句所查询出来的参数,那么orm会自动识别出来,直接不执行。

# 举例:
    res = models.Book.objects.all()  # 这时orm是不会走数据库的
    print(res)   # 只有当要用到的上述orm语句的结果时,才回去数据库查询。

only

res = models.Book.objects.only('title')     # 括号内查询的字段可以有多个
print(res)        # 查询一次,打印一条sql查询语句
for i in res:
    print(i.title)  # 查询一次,打印一条sql查询语句
    print(i.price)  # 有几个对象,就查询几次,打印几条sql查询语句

  only会把括号内字段对应的值,封装到查询返回的对象中,通过对象点括号字段,不需要再走数据库
  查询,直接返回结果,一旦你点了不是括号内的字段 就会频繁的去走数据库查询
  '''
      only英语的意思就是只有,那我们可以理解为通过only查询出来的对象内只会有小括号内的字
      段的值,如果想要获取其他字段的值,就还得在查一次数据库或者说是在执行一次SQL语句。
  '''

defer

res = models.Book.objects.defer('title')
# print(res)
for i in res:
    print(i.price) # 只会执行一次SQL语句
    print(i.title) # 每查询一次执行一次SQL语句

  和 only相反,defer会将括号内的字段排除之外将其他字段对应的值, 直接封装到返回给你的对象
  中, 点其他字段 不需要再走数据库查询,一旦你点了括号内的字段就会有多少值,就会查询几次

  '''
      defer刚好和only相反,defer小括号里写了啥,它反倒不给你,它会给你除了括号内的所有
      字段值。当你获取小括号内的字段值时,会去查询,获取不在小括号内的字段值时反倒还不用去查
      询。
  '''
    res = models.Book.objects.select_related('publish')
    res = models.Book.objects.select_related('publish')
    print(res) # <QuerySet [<Book: Book object>, <Book: Book object>, <Book: Book object>]>
    print(res[0].title)  # Python编程
    print(res[0].publish.title) # 上海出版社

  select_related括号内放外键字段,并且外键字段的类型只能是一对一和一对多,不能是多对多,

  内部自动做联表操作,会将括号内外键字段所关联的表与当前表自动拼接成一张表,然后将表中的
  数据一个个查询出来封装成一个个的对象。 这样做 就不会重复的走数据库,减轻数据库的压力。

  select_related括号内可以放多个外键字段,用逗号隔开,会将多个外键字段关联的表拼接成一张大表
  
  后续对象通过正反向查询跨表 内部不会再走数据库查询
  res = models.Book.objects.prefetch_related('publish','authors')
  for i in res:
      print(i.publish)

  prefetch_related内部是子查询,会自动按照步骤查询多张表,然后将查询的结果封装到对象中,这样给用户的感觉还是联表操作。

  括号内支持传多个外键字段,并且没有类型限制。

  每放一个外键字段,就会多走一条sql语句,多查询一张表

 """
    将多次查询之后的结果封装到数据对象中 后续对象通过正反向查询跨表 内部不会再走数据库查询
 """

ORM常见字段

属性名 = models.字段类型,定义属性时需要指定字段类型, 通过字段类型的参数指定选项

属性名:
  不允许使用python的保留关键字
  不允许使用mysql的保留关键字
  不允许使用连续的下划线,因为Django的查询语法就是连续的下划线

AutoField:自动增长的IntegerField, 不指定时Django会自动创建属性名为id的自动增长属性

BooleanField:布尔字段,值为TrueFalse

NullBooleanField:支持Null、TrueFalse三种值

CharField(max_length=20):字符串
参数max_length表示最大字符个数

TextFiled:大文本字段,一般超过4000个字符时使用

IntegerField:整数

DecimalField(max_digits=None, decimal_places=None):可以指定精度的十进制浮点数
参数max_digits表示总位数
参数decimal_places表示小数位数

FloatField():浮点数 

DateField[auto_now=False, auto_now_add=False]):日期
参数auto_now表示每次保存对象时,自动设置该字段为当前时间,用于"最后一次修改"的时间戳,它
总是使用当前日期,默认为false
参数auto_now_add表示当对象第一次被创建时自动设置当前时间,用于创建的时间戳,它总是使用当
前日期,默认为false
参数auto_now_add和auto_now是相互排斥的,组合将会发生错误

TimeField:参数和DateField一样

DateTimeField:日期时间,参数同DateField

FileField:上传文件字段,以二进制的形式

ImageField:继承于FileField,对上传的内容进行校验,确保是有效的图片

自定义字段类型

class FixedCharField(models.Field):
    """
    自定义的char类型的字段类    固定长度
    """
    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):
        """
        限定生成数据库表的字段类型为char,长度为max_length指定的值
        """
        return 'char(%s)' % self.max_length

使用:
  class Class(models.Model):
    id = models.AutoField(primary_key=True)
    title = models.CharField(max_length=25)
    # 使用上面自定义的char类型的字段
    cname = FixedCharField(max_length=25)

choice参数

用户表	
	性别
	学历
	工作经验
	是否结婚
	是否生子
	客户来源
	...
针对某个可以列举完全的可能性字段,我们应该如何存储

只要某个字段的可能性是可以列举完全的,那么一般情况下都会采用choices参数

class User(models.Model):
    username = models.CharField(max_length=32)
    age = models.IntegerField()
	
    # 性别
    gender_choices = (
        (1,'男'),
        (2,'女'),
        (3,'其他'),
    )
    gender = models.IntegerField(choices=gender_choices)
    
    # 分数
    score_choices = (
        ('A','优秀'),
        ('B','良好'),
        ('C','及格'),
        ('D','不合格'),
    )
    # 保证字段类型跟列举出来的元祖第一个数据类型一致即可
    score = models.CharField(choices=score_choices,null=True)


添加数据:
  models.User.objects.create(username='111',age=18,gender=1)
  models.User.objects.create(username='222',age=85,gender=2)
  models.User.objects.create(username='333',age=40,gender=3)
  models.User.objects.create(username='444',age=45,gender=4)
'''
  添加数据时,如果添加了没有列举出来的数字也能存(范围是按照字段类型决定)
'''

取值:
  只要是choices参数的字段 如果你想要获取对应信息 固定写法 get_字段名_display()
  user_obj = models.User.objects.filter(pk=1).first()
  print(user_obj.get_gender_display())
  '''
    有对应关系就拿对应的内容 没有还是本身
  '''

ORM事务

在Django中可以通过django.db.transaction模块提供的atomic来定义一个事务,atomic提供两种用法
1.装饰器方法:
from django.db import transaction

@transaction.atomic
def viewfunc(request):
    # 这些代码会在一个事务中执行
    ...   

2.with 语句:
from django.db import transaction

def viewfunc(request):
    # 这部分代码不在事务中,会被Django自动提交
    ...

    with transaction.atomic():
        # 这部分代码会在事务中执行
    # 创建回滚点
    save_id = transaction.savepoint() 
    #一旦异常,则回滚代码
    transaction.savepoint_rollback(save_id)
        ...

from django.db import transaction
    try:
        with transaction.atomic():
            pass
    except Exception:
        pass

ORM执行原生SQL

1from django.db import connection, connections

cursor = connection.cursor()  
cursor = connections['default'].cursor()
cursor.execute("原生SQL语句",[1]) # []中的内容会按位置传值给%s

# 查询结果
row = cursor.fetchone()
'''类似于pycharm操作'''2)
models.UserInfo.objects.extra(
                    select={'newid':'select count(1) from app01_usertype where id>%s'},
                    select_params=[1,], # 按位置传值给select中的%s
                    where = ['age>%s'],
                    params=[18,], # 按位置传值给where中的%s
                    order_by=['-age'],# 指定按这个字段降序,升序把"-"号去掉即可
                    tables=['app01_usertype'] # 指定表
                )

多对多三种创建方式

# 全自动(常见)
	orm自动创建第三张表 但是无法扩展第三张表的字段
	authors = models.ManyToManyField(to='Author')
# 全手动(使用频率最低)
	优势在于第三张表完全自定义扩展性高 劣势在于无法使用外键方法和正反向
	class Book(models.Model):
          title = models.CharField(max_length=32)
        class Author(models.Model):
          name = models.CharField(max_length=32)
        class Book2Author(models.Model):
          book_id = models.ForeignKey(to='Book')
          author_id = models.ForeignKey(to='Author')
# 半自动(常见)
	正反向还可以使用 并且第三张表可以扩展 唯一的缺陷是不能用
  add\set\remove\clear四个方法
  
	class Book(models.Model):
            title = models.CharField(max_length=32)
            authors = models.ManyToManyField(
              					to='Author',
            					through='Book2Author',  # 指定表
              					through_fields=('book','author')  # 指定字段
            )
        class Author(models.Model):
            name = models.CharField(max_length=32)
            '''多对多建在任意一方都可以 如果建在作者表 字段顺序互换即可'''
            books = models.ManyToManyField(
              					to='Author',
            					through='Book2Author',  # 指定表
              					through_fields=('author','book')  # 指定字段
            )
        #第三张表
        class Book2Author(models.Model):
            book = models.ForeignKey(to='Book')
            author = models.ForeignKey(to='Author')

本文作者:春游去动物园

本文链接:https://www.cnblogs.com/chunyouqudongwuyuan/p/16285499.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   春游去动物园  阅读(43)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
展开
  1. 1 生分 川青
生分 - 川青
00:00 / 00:00
An audio error has occurred.

生分 - 川青

词:莎子

曲:邵翼天

编曲:林亦

混音:罗杨轩

吉他:林亦

制作人:谢宇伦

监制:曾炜超/陈显

策划:+7

统筹:黄染染

出品:漫吞吞文化

『酷狗音乐人 • 星曜计划』

全方位推广,见证星力量!

「版权所有未经许可 不得商业翻唱或使用」

我们怎么变得那么生分

用了几年也没解开疑问

有些事你不提我也不问

在陌生与熟悉间找平衡

有些话一开口会伤人

有些话一开口会伤人

所以我选择默不作声

所以我选择默不作声

爱一个人

若甘愿陪衬

甘愿牺牲

也许换个名分

也不是没可能

我不怕在爱里做个蠢人

我不怕在爱里做个蠢人

也不怕爱过之后再分

也不怕爱过之后再分

爱一个人

有万种身份

万种可能

只是没想到

我们最后友人相称

我们怎么变得那么生分

我们怎么变得那么生分

连说话都要掌握好分寸

怕不注意流言

见缝插针

怕不小心我们

成陌生人

我们怎么变得那么生分

用了几年也没解开疑问

有些事你不提我也不问

在陌生与熟悉间找平衡

有些话一开口会伤人

有些话一开口会伤人

所以我选择默不作声

所以我选择默不作声

爱一个人

若甘愿陪衬

甘愿牺牲

也许换个名分

也不是没可能

我不怕在爱里做个蠢人

我不怕在爱里做个蠢人

也不怕爱过之后再分

也不怕爱过之后再分

爱一个人

有万种身份

万种可能

只是没想到我们最后

友人相称

我们怎么变得那么生分

连说话都要掌握好分寸

怕不注意流言见缝插针

怕不小心我们成陌生人

我们怎么变得那么生分

用了几年也没解开疑问

有些事你不提我也不问

在陌生与熟悉间找平衡

我们怎么变得那么生分

我们怎么变得那么生分

连说话都要掌握好分寸

怕不注意流言见缝插针

怕不小心我们成陌生人

我们怎么变得那么生分

用了几年也没解开疑问

有些事你不提我也不问

在陌生与熟悉间找平衡