Djiango orm 单表查询

一、分组查询

1、概念

# Book: id  name  price  publish_date  publish

1. 聚合函数可以单独使用: 将整张表作为一个大的分组,查询字段只能是聚合结果
select max(price), group_concat(name) from book where id < 10;

2. 聚合函数在分组下使用
select publish_id, max(price) as high_price from book group by publish_id having max(price) > 50

2、使用场景

 1)聚合函数的使用场景:
    a、单独使用:不分组,只查聚合结果(aggregate);
    b、分组使用:按字段分组,可查分组字段与聚合结果 (annotate)。

 2)聚合函数使用前需要先进行导入:

# 导入聚合函数
from django.db.models import Avg, Max, Min, Count, Sum

3、单独聚合查询 aggregate

 1)语法:

aggregate(别名=聚合函数('字段'))

 2)规则:
  1.可以同时对多个字段进行聚合处理:aggregate(别名1=聚合函数1('字段1'), ..., 别名n=聚合函数n('字段n'));
  2.查询结果是QuerySet对象方法;
  3.方法返回值为dict类型。

 3)案例:

# 案例:所有书中最贵的书的价格
Book.objects.all().aggregate(high_price=Max('price'))
print(dic)

4、组聚合查询 annotate

 1)语法:

values('分组字段').annotate(别名=聚合函数('字段')).filter(聚合字段别名条件).values('取分组字段', '取聚合字段别名')

 2)规则:

  1.values(...).annotate(...)为分组组合,values控制分组字段,annotate控制聚合字段;

res = UserInfo.objects.values('province', 'city').annotate(high_age=Max('age'))

  2.values可按多个字段分组values('分组字段1', ..., '分组字段n'),取字段的values中出现了非分组或非聚合字段,该字段自动成为分组字段(这样会使分组变得无意义,因此取字段的values中不应出现非分组或非聚合字段);

res = UserInfo.objects.values('province', 'city').annotate(high_age=Max('age')).values('city', 'high_age', 'name')
# 取值values中出现name字段,django的orm在分组是为了避免出现报错会将‘name’字段加到第一个values中作为一个分组依据。

res = UserInfo.objects.values('province', 'city').annotate(high_age=Max('age')).filter(city__contains="济南").values('city', 'high_age')
print(res)

  3.可以同时对多个字段进行聚合处理annotate(别名1=聚合函数1('字段1'), ..., 别名n=聚合函数n('字段n'));
  4.分组后的的filter代表having判断,只对聚合字段进行条件判断,可以省略(对非聚合字段或分组字段进行条件判断代表where判断);

# 案例:每个出版社出版的最贵的书的价格高于50元的出版社名与最高价格
res = Book.objects.values('publish__name')\
    .annotate(high_price=Max('price'))\
    .filter(high_price__gt=50)\
    .values('publish__name', 'high_price')
print(res)
#  filter中出现了聚合函数(annotate)中的参数时相当于sql查询语句中的having

res = Publish.objects.values('name')\
    .annotate(high_price=Max('book__price'))\
    .filter(name__icontains='n')\
    .values('name', 'high_price')
print(res)
#  filter中出现非聚合函数(annotate)中的参数时,django的orm会将其有优化为where条件放到分组前面进行过滤

  5.取字段值values(...)省略默认取所有分组字段与聚合字段,也可以自主取个别分组字段及聚合字段(取字段的values中出现了非分组或非聚合字段,该字段自动成为分组字段)。

res = UserInfo.objects.values('province', 'city').annotate(high_age=Max('age'))

res = UserInfo.objects.values('province', 'city').annotate(high_age=Max('age')).filter(city__contains="济南").values('province','city', 'high_age')

# 上面两个查询语句所得到的查询结果相同
print(res)

二、常用共有字段属性

#1. null:默认为False,True表示字段可为null,与数据库相关
    a1 = models.CharField(max_length=20)  # 插入数据时该字段必须赋值
    a2 = models.CharField(max_length=20, null=True)  # 插入数据时可以为空

#2. blank:默认为False,True表示字段可为空, 与表单验证相关
#3. choice:可选的,限制了该选项的字段值必须是所指定的choice中的一个:
    -- sex = models.SmallIntegerField(choices=((1, ''), (2, "")))
    -- obj.get_sex_display()

#4. db_column:自定义字段名
sex = models.SmallIntegerField(choices=choices, default=0, db_column='gender')
# 他只是将数据库中的字段变为gender,在django中若要查询该字段的值仍需要输入sex,因此不常用,不建议使用。

#5. db_index:如果为True的话,设置索引
#6. default:字段默认值
#7. editable:默认为True,若为False,则不会在/admin/界面显示
#8. primary_key:若设置为True,则表示将该字段设置为主键。一般情况下django默认会设置一个自增长的id主键。
#9. unique:若设置为True,该字段值不可重复

三、常用字段

# 1. AutoField():默认自增主键(primary_key=True),django会默认建立id字段主键
# 2. BooleanField():布尔字段,对应数据库tinyint类型  数据长度只有1位 内部只接收0/1或者True/Flase
# 3. CharField():字符类型
    -- 字段属性max_length=64,数据长度,必须明确
# 4. DateField():年月日时间类型
    -- 字段属性auto_now=True,数据被更新就会更新时间
    -- 字段属性auto_now_add=True,数据第一次参数时产生
# 5. DateTimeField():年月日小时分钟秒时间类型
    -- 字段属性auto_now=True,数据被更新就会更新时间
    -- 字段属性auto_now_add=True,数据第一次参数时产生
# 6. DecimalField():混合精度的小数类型
    -- 字段属性max_digits=3,限定数字的最大位数(包含小数位)
    -- 字段属性decimal_places=2,限制小数的最大位数
# 7. IntegerField():整型

四、不常用字段

# 1. BigAutoField():大整型自增
# 2. BigIntegerField():长整型
# 3. EmailField():邮箱字段,拥有/admin/验证
# 4. FloatField():浮点型小数
# 5. SmallIntegerField():小整型
# 6. TextField():大文本类型
# 7. FileField():文件字段

五、关系字段

# 1. ForeignKey():外键字段
    #-- 字段属性to关联模型类
    #-- 字段属性to_field关联字段,省略默认关联主键
    #-- 字段属性on_delete (外键关联数据被删除时的操作)
        #-- models.CASCADE 级联删除
class Book(models.Model):
     publish = models.ForeignKey(to='Publish', to_field='id', on_delete=models.CASCADE)
        #-- modles.PROTECT 抛出异常
        #-- models.SET_NULL 设置空值
        #-- models.SET_DEFAULT 设置默认值
        #-- models.SET(value)自定义值
    #-- 字段属性related_name自定义反向查询的字段名,自定义后方向查询自定义的名字会将默认的类名下划线set覆盖。

    #-- 字段属性db_constraint=False取消关联关系,但还可以使用连表查询
#总结:models.ForeignKey(to='关联的类名', null=True, on_delete=models.SET_NULL, db_constraint=False, related_name="本类名小写")

class Test2(models.Model):
    name = models.CharField(max_length=20)
    test1 = models.ForeignKey(to='Test1', null=True, on_delete=models.SET_NULL, db_constraint=False, related_name="tt")

#2、OneToOneField():一对一外键字段
#    -- 字段同外键
#3、ManyToManyField():多对多关系字段
    #-- 字段属性to关联模型类
    #-- 字段属性through关联关系类
    #-- 字段属性through_fields关联关系表中(本身类名小写字段, 关联表类名小写字段)    
class MyBook(models.Model):
       my_author = models.ManyToManyField(to='MyAuthor', through='MyBook_MyAuthor', through_fields=('mybook', 'myauthor'))

class MyAuthor(models.Model):
    name = models.CharField(max_length=20)

class MyBook_MyAuthor(models.Model):
    mybook = models.ForeignKey(to="MyBook", null=True, on_delete=models.SET_NULL, db_constraint=False)
    myauthor = models.ForeignKey(to='MyAuthor', null=True, on_delete=models.SET_NULL, db_constraint=False)
    time = models.DateField()

六、断开外键关联的ForeignKey使用

# 1、不使用ForeignKey方式断开关联(即建表的时候就不让其拥有自动关联关系,而是手动的靠创建数据时的数据录入建立关联关系),查询的时候不再支持Django ORM连表查询语法
class Publish(models.Model):
    name = models.CharField(max_length=20)
class Book(models.Model):
    name = models.CharField(max_length=20)
    # 字段需要写_id来表示相关表的字段信息
    publish_id = models.IntegerField()

# *****
# 2、使用ForeignKey方式用db_constraint=False字段属性断开关联,依然支持Django ORM连表查询语法,建议使用
class Publish(models.Model):
    name = models.CharField(max_length=20)
class Book(models.Model):
    name = models.CharField(max_length=20)
    # 字段不需要写_id来表示相关表的字段信息,ORM会自动添加
    publish = models.ForeignKey(to='Publish', null=True, on_delete=models.SET_NULL, db_constraint=False)

七、断开关联的多对多自动创建关系表

# 使用ManyToManyField方式用db_constraint=False字段属性断开关联,依然支持Django ORM连表查询语法,建议使用
class MyBook(models.Model):
    name = models.CharField(max_length=20)
    my_author = models.ManyToManyField(to='MyAuthor', db_constraint=False)
class MyAuthor(models.Model):
    name = models.CharField(max_length=20)

八、断开关联的多对多手动创建关系表

  手动创建关系表的好处:手动创建关系表可以让关系表可以拥有更多的自身的字段,同时通过关系表类名可以直接获取第三张表

  1、和自动建立关系表类似,依然支持Django ORM连表查询语法(多对多借助关系表连表查询)

class Book(models.Model):
    name = models.CharField(max_length=20)
    
class Author(models.Model):
    name = models.CharField(max_length=20)
    
class Book_Author(models.Model):
    book = models.ForeignKey(to="Book", null=True, on_delete=models.SET_NULL, db_constraint=False)
    author = models.ForeignKey(to='Author', null=True, on_delete=models.SET_NULL, db_constraint=False)
    time = models.DateField()

  2、手动创建关系表,在关系表中用ForeignKey方式支持基于外键关系表的ORM连表查询,同时明确ManyToManyField字段,所以也支持ORM正向方向连表查询
       -- db_constraint=False断开关联可以在ForeignKey或ManyToManyField任意一方完成

class Book(models.Model):
    name = models.CharField(max_length=20)
    # 明确through与through_fields,ManyToManyField才不会自动建立关系表
    author = models.ManyToManyField(to='Author', through='Book_Author')
    
class Author(models.Model):
    name = models.CharField(max_length=20)
    
class Book_Author(models.Model):
    book = models.ForeignKey(to="Book", null=True, on_delete=models.SET_NULL, db_constraint=False)
    author = models.ForeignKey(to='Author', null=True, on_delete=models.SET_NULL, db_constraint=False)
    time = models.DateField()

  总结:手动创建第三张表,第三张表的增删改就采用关系表类名衍生的create|delete|update,就不再拥有add|clear|remove|set(因为关系表拥有自己的字段,这些方法无法直接操作这些字段)

 

posted @ 2019-03-07 20:11  zhao_peng  阅读(625)  评论(0编辑  收藏  举报