Django数据库操作详解:分组聚合、ORM字段属性及外键关联处理
本文深入解析Django数据库操作核心技巧。涵盖分组聚合查询(aggregate与annotate)的语法及场景,详解Model字段属性配置(null, blank, choices等)。重点讲解如何通过db_constraint=False断开外键关联以保留ORM查询能力,以及手动创建多对多关系表的方法与注意事项,帮助开发者优化数据库设计。
Django多表查询基础:增删改与对象操作
多表查询
1. 增删改
一对多关系处理:遵循先创建主表(一)再创建从表(多)的原则。外键字段可以存储关联对象实例,也可以直接存储依赖表的主键ID(例如publish与book的关系)。
publish = Publish.objects.create()
Book.objects.create(....publish=publish|publish_id=publish.id)
删除操作:默认情况下,Django ORM会执行级联删除,即删除主表记录时自动删除关联的从表记录。
修改操作:当修改book的外键字段时,必须确保外键指向的记录在数据库中真实存在。
多对多关系:
获取关系表数据:通过主表对象访问关联对象,例如book.author。
增加关联:使用book.author.add()方法,参数可以是作者对象列表或作者主键ID列表。
删除关联:使用clear()方法清除所有关联,或使用remove()方法删除单个指定作者。
修改关联:使用set([作者对象们|主键们])方法,直接替换当前的所有关联关系。
2. 查
基于对象的查询:正向查询通过属性访问,反向查询通过类名小写获取;若反向查询返回多条记录,则通过类名小写加_set访问。
book.publish.first().name (book 一定是对象,不是queryset)
publish.book_set.first().name
基于双下划线的查询:
Book.objects.filter(id=1).values('publish__name')[0] (values 查出的也是queryset)
publish.values('book__name')
Django分组聚合查询实战:aggregate与annotate详解
今日内容
1. 分组查询:利用聚合结果进行group_by操作。
2. 聚合函数:包括Max, Min, Sum, Avg, Count等。
3. 字段:涉及模型字段的详细配置。
分组查询分为单独聚合查询和分组聚合查询,底层基于MySQL语法实现。
Book模型包含字段:id, name, price, publish_date, publish。
聚合函数可以单独使用,此时整张表被视为一个大的分组。
SQL示例:select max(price) from book。
聚合函数在分组场景下使用,用于统计各组数据。
select max(price) as high_price from book group by publish having high_price > 50;
聚合查询基于ORM实现,无需直接编写SQL。
聚合函数的使用场景主要分为两类:
单独使用:不指定分组,仅查询整体的聚合结果。
分组使用:按指定字段分组,同时查询分组字段和聚合结果。
导入聚合函数:
from django.db.models import Avg,Max,Min,Count,Sum
单独聚合查询:使用aggregate方法(意为聚集、合集),不进行分组操作。
# 语法
# 聚合函数: Max, Min,Sum, Avg, Count
aggregate(别名=聚合函数('字段‘))
规则:
1. 支持同时对多个字段进行聚合处理,语法为aggregate(name1=..., name2=...)。
2. aggregate是QuerySet对象的方法,可链式调用在all()或filter()之后。
3. 返回值类型为字典(dict)。
4. 在aggregate之前的values()操作无效,会被忽略。
例:查询所有书中最贵的书的价格。
dic = Book.objects.all().aggregate(high_price=max('price),low_price=min('price'))
分组聚合查询:使用annotate方法(意为注释、做注解),用于分组操作。
# 语法 values('分组字段').annotate(别名=聚合函数(‘字段')).filter(聚合别名条件).values('取分组字段','取聚合字段别名')
规则:
1. values与annotate组合使用,values控制分组的字段,annotate控制聚合计算的字段。
2. values支持按多个字段分组,例如values('字段1', '字段2')。
3. 支持同时对多个字段进行聚合处理,例如annotate(别名1=max('price'), 别名2=min('price'))。
4. 分组后的filter代表having判断,仅对聚合字段进行条件筛选;若参数为非聚合或非分组字段,则代表where判断。
5. 取字段值时,values()省略参数默认返回所有分组字段和聚合字段;也可自定义返回字段(若指定非分组或非聚合字段,该字段会自动变为分组字段)。
# 案例:每个出版社出版的最贵的书的价格高于50元的出版社名与最高价格
# 思路:按出版社分组(从book出发),计算high_price=max('price'),筛选条件filter(high_price__gt=50)。
# 每个组的价格最贵的
Book.objects.all().values('publish__name').annotate(high_price=max('price').filter(high_price__gl=50).values('publish__name','high_price'))
Django Model字段属性配置指南
字段属性
1. null:默认False(表示数据库字段不能为NULL),True表示字段允许存储NULL值。 2. blank:默认False,True表示表单验证时字段可以为空。 3. choice:限制该选项字段的值必须是指定的choice元组中的一个,格式为元组套元组。 示例:sex = models.SmallIntegerField(choice=((1,'man'),(2,'female'))) 获取显示值:obj.get_sex_display()
有choices字段的:要取得'女‘或'男‘,调用get_字段名_display()方法(若值超出choice范围则失效)。 4. db_column:自定义数据库中的字段名。 示例:db_column='gender',将模型中的sex字段在数据库中别名为gender。 5. db_index:True表示为该字段设置数据库索引。 6. default:设置字段的默认值。 7. editable:默认为True,若设为False,则该字段不在admin管理界面显示。 8. primary_key:TRUE表示该字段为主键。 9. unique:true表示字段值不可重复。
字段
1. AutoField():默认自增主键(通常配合primary_key=True使用)。 2. BooleanField():布尔字段,对应数据库tinyint类型。 3. CharField():字符类型(默认不为空)。 示例:max_length=20, null=True表示可以为空。 4. DateField():年月日。 auto_now = True表示每次数据更新时自动更新时间。 auto_now_add = True表示数据第一次创建时自动设置时间。 5. DateTimeField():年月日时分秒。 auto_now = True表示每次数据更新时自动更新时间。 auto_now_add = True表示数据第一次创建时自动设置时间。 6. DecimalField():混合精度小数类型。 max_digits = 5表示含小数在内的最大位数。 decimal_places = 2表示小数位数。 7. IntegerField():整型。
不常用字段
关系字段
1. ForeignKey():外键字段。 to= 关联模型类(用于一对多关系)。 to_field = 关联字段,省略时默认关联主键。 on_delete (外键关联数据被删除时的操作策略): models.CASCADE:级联删除。 models.PROTECT:抛出异常阻止删除。 models.SET_NULL:设置外键为空(需null=True)。 models.SET_DEFAULT:设置默认值。 models.SET(value):自定义值。 related_name:自定义反向查询的字段名。 db_constraint=False:取消数据库层面的关联约束,但仍可使用ORM链表查询。 总结示例:models.ForeignKey(to='related class name', null=True, on_delete=models.SET_NULL, db_constraint=False, related_name='本类名小写')。 2. OneToOneField():一对一字段。 用法同外键。 3. ManyToManyField():多对多关系。 to = 关联模型类。 through=关联关系类。 through_fields:指定关联关系表中(本身字段,关联字段)。
断开外键关联:保留ORM查询能力的技巧
断开外键关联的ForeignKey使用(一对多,一对一)
# 一对多查询 ----(publish and book) # 方式一 : 不使用外键,在book 中添加 publish_id 属性 # 不在支持Django ORM 链表查询语法
# class Book(models.Model): # name = models.CharField(max_length=20) # publish_id = models.IntegerField(null=True) # # class Publish(models.Model): # name = models.CharField(max_length=20) # # # 查询方式: # # 通过第一本书book 找出版社 # # id = Book.objects.first().publish_id # # publish = Publish.objects.filter(id=id)[0].name # # print(publish)
方式二:使用外键,设置db_constraint=False断开数据库层面的连接。 # 可以使用Django ORM连表查询语法 class Book(models.Model): name = models.CharField(max_length=20) publish = models.ForeignKey(to='Publish',db_constraint=False,null=True,on_delete=models.SET_NULL) # to_field='id' 不写会自动添加
class Publish(models.Model): name = models.CharField(max_length=20)
# 书的出版社 (外键方式) # print(Book.objects.first().publish.name) # print(Book.objects.filter(pk=1).values('publish__name'))
多对多关系处理:自动与手动创建关系表
断开关联--- 多对多自动创建关系表
# 断开关联(db_constraint属性)的多对多自动创建关系表 (book(外键) and author) # 断开后依然支持Django ORMlianiao 查询语法 # 当新表中无需新加额外字段时, 可以自动创建 class MyBook(models.Model): name = models.CharField(max_length=20) # 这里会产生第三张表 book_author = models.ManyToManyField(to='MyAuthor',db_constraint=False)
class MyAuthor(models.Model): name = models.CharField(max_length=20)
# # 查询方法 # # 多对多(自动创建第三张表): 书的作者 # b1 = MyBook.objects.first() # # b1.book_author 这个是关系表 # for author in b1.book_author.all(): # print(author.name)
# print(MyBook.objects.filter(pk=1).values('book_author__name'))
断开关联 --- 多对多手动创建关系表
# 手动创建关系表的原因: 可以拥有自身字段,可以通过关系表类名直接获取第三张表
# 手动创建关系表可以让关系表可以拥有更多的自身的字段,同时通过关系表类名可以直接获取第三张表 ''' # **** # 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才不会自动建立关系表,没有关联关系后就不能再使用db_constraint字段属性 author = models.ManyToManyField(to='Author', through='Book_Author', through_fields=('book_id', 'author_id')) 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(因为关系表拥有自己的字段,这些方法无法直接操作这些字段)
到此这篇关于Django分组聚合查询实例分享的文章就介绍到这了,更多相关Django分组聚合查询内容请搜索本站以前的文章或继续浏览下面的相关文章希望大家以后多多支持本站!
本站声明
本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。













