Django 模型层之多表操作
- 2020 年 1 月 19 日
- 筆記
一.创建模型
实例:
作者表: 拥有字段:姓名(name),性别(sex),该表与书籍表之间为多对多的关系 作者详情表: 拥有字段:地址(addr),手机号(phone),该表与作者表之间为一对一的关系 出版社表: 拥有字段:名字(name),地址(addr),该表与书籍表之间为一对多的关系 书籍表: 拥有字段:书名(name),单价(price) 分析:一本书可以拥有多个作者,一个作者可以写多本书,所有作者与书籍之间为多对多的关联关系(Many-To-Many);一本书只有一个出版社,但是一个出版社可以出版多种书籍,所以出版社和书籍之间是一对多的关联关系(One-To-Many);作者与作者详情就是一对一关联关系(One-To-One)
所以在Models.py下创建模型如下
class Publish(models.Model): name = models.CharField(max_length=255) addr = models.CharField(max_length=255) def __str__(self): return self.name class Author(models.Model): id = models.AutoField(primary_key=True) name = models.CharField(max_length=255) sex = models.IntegerField() authordetail = models.OneToOneField(to='AuthorDetail') def __str__(self): return self.name class AuthorDetail(models.Model): id = models.AutoField(primary_key=True) addr = models.CharField(max_length=255) phone = models.CharField(max_length=255) class Book(models.Model): id = models.AutoField(primary_key=True) name = models.CharField(max_length=255) price = models.DecimalField(max_digits=5, decimal_places=2) # 外键,关联关系写在一对多中多的那一方 publish = models.ForeignKey(to=Publish) authors = models.ManyToManyField(to=Author) def __str__(self): return self.name
注意事项:
1.id字段不写的话会自动添加 2.对于外键字段,Django会在字段名上添加"_id"来创建数据库中的列名 3.外键字段ForeignKey有一个null=True的设置,你可以赋给它空值None
二.添加表记录
一对一:
# 方式一: detail_obj = models.AuthorDetail.objects.filter(id=2).first() models.Author.objects.create(name='Yven',sex=1,authordetail=detail_obj) # 方式二: models.Author.objects.create(name='hwt',sex=2,authordetail_id=1)
一对多:
# 方式一: publish_obj = models.Publish.objects.filter(id=2).first() models.Book.objects.create(name='水浒传',price=24.5,publish=publish_obj) # 方式二: models.Book.objects.create(name='西游记',price=24.5,publish_id=2)
多对多:
# 获得书籍对象 book = models.Book.objects.create(name='水浒传',price=24.5,publish_id=3) # 获得作者对象 yven = models.Author.objects.get(name='Yven') hwt = models.Author.objects.get(name='hwt') # 绑定多对多关系 # add中可以传递对象,可以传递id,也可以传递*列表如:(*[yven,hwt]) book.authors.add(yven,hwt) #多对多关系其他常用API: # 将某个特定的对象从被关联对象集合中去除,可以传递对象,可以传递id,也可以传递*列表如:(*[yven,hwt]) book.authors.remove() # 清空被关联对象集合,无需传参 book.authors.clear() # 先清空再设置,传递的参数必须是可迭代对象,一般为列表,列表内可以是对象,也可以是id book.authors.set()
三.基于对象的跨表查询
一对一查询
正向查询(按字段:authordetail)
# 查询作者姓名为Yven的手机号 yven = models.Author.objects.get(name='Yven') phone = yven.authordetail.phone
反向查询(按表名小写:author)
# 查询手机号为12221321的作者名 authordetail = models.Author.objects.filter(phont='12221321').first() name = authordetail.author.name
一对多查询
正向查询(按字段:publish)
# 查询西游记的出版社名 book = models.Book.objects.filter(name='西游记').first() name = book.publish.name
反向查询(按表名小写_set.all():book_set.all())
# 查询pk为2的出版社出版书籍的单价 publish = models.Publish.objects.filter(pk='2').first() booklist = publish.book_set.all() #QuerySet对象 for obj in booklist: print(obj.price)
多对多查询
正向查询(按字段.all():publish.all())
# 查询水浒传的所有作者 book = models.Book.objects.get(name='水浒传') author = book.authors.all() for i in author: print(i.name)
反向查询(按表名小写_set.all():book_set.all())
# 查询Yven写的多少有书籍的单价 author = models.Author.objects.get(name='Yven') book = author.book_set.all() for i in book: print(i.price)
四.基于双下划线的跨表查询
Django还提供了一种直观而搞笑的方式在查询(lookups)种表示关联关系,它能自动确认SQL JOIN联系。要做跨关系查询,就使用两个下划线来连接模型(model)间关联字段的名称,知道最终链接到你想要的model为止。
一对一查询
正向查询(按字段):authordetail
# 查询Yven的地址 models.Author.objects.filter(name='Yven').values('authordetail__addr')
反向查询(按表名):author
# 查询Yven的地址 models.AuthorDetail.objects.filter(author__name='Yven').values('addr')
一对多查询
正向查询(按字段):publish
# 查询西游记的出版社名 models.Book.objects.filter(name='西游记').values('publish__name')
反向查询(按表名):book
# 查询西游记的出版社名 models.Publish.objects.filter(book__name='西游记').values('name')
多对多查询
正向查询(按字段):authors
# 查询水浒传的作者性别 models.Book.objects.filter(name='水浒传').values('authors__sex')
反向查询(按表名):book
# 查询水浒传的作者性别 models.Author.objects.filter(book__name='水浒传').values('sex')
五.聚合查询与分组查询
聚合查询:aggregate(*args,**kwargs)
# 需要导入包 from django.db.models import Avg # 查询所有图书的平均价格 models.Book.objects.all().aggregate(Avg('price'))
aggregate()是QuerySet的一个终止子句,意思是说,它返回一个包含一些键值对的字典。键的名称是聚合值的标识符,值是计算出来的聚合值,键的名称是按照字段和聚合函数的名称自动生成出来的,如果你想要为聚合值指定一个名称,可以向聚合子句提供一个名字。
models.Book.objects.all().aggregate(aaa=Avg('price')) #{'aaa':3.14}
如果不止有一个聚合查询,可以在aggregate()括号内继续添加参数.
Django提供了以下聚合函数
1.expression 引用模型字段的一个字符串,或者一个query expression 2.output_field 用来表示返回值的model field,一个可选的参数 3.extra 关键字参数可以给聚合函数生成的SQL提供额外的信息 4.Avg 返回给定表达式的平均值,它必须是数值,除非指定不同的output_field 5.Count 返回与expression相关的对象的个数,有一个可选的参数distinct,如果distinct=True,那么Count将只计算唯一的实例,默认值是False 6.Max 返回给定字段的最大值 7.Min 返回给定字段的最小值 8.Sum 返回给定字段的总和
分组查询:annotate()
annotate()为调用QuerySet种每一个对象都生成一个独立的统计值,相当于数据库种的group by
统计每一本书作者个数
from django.db.models import Count num=Book.objects.annotate(author_num=Count('authors')).values('name','author_num')
总结
values在annotate()之前,表示group by,之后,表示为取值 filter在annotate()之前,表示过滤,之后,表示having
六.F查询与Q查询
F查询
现在位置的操作种,都是将模型字段与常量进行比较,但是,如果想将模型的一个字段与同一个模型的另一个字段进行比较该怎么办? 所有Django为我们提供了F表达式来完成这类操作 导入包: from django.db.models import F
如:查询comment_num数量大于read_num的书籍
models.Book.objects.filter(comment_num__gt=F('read_num'))
还可以对F()对象进行加减乘除,取模等运算操作.
如:将read_num的数量乘以二
models.Book.objects.all().update(read_num=F('read_num')*2)
Q查询
filter()等方法种的关键字参数查询都是通过逗号来实现'AND'的,如果需要执行复杂的查询,就需要使用Q对象 导入包:from django.db.models import Q
可以使用"&"或者"|"或者"~"来组合Q对象,分别表示与,或,非逻辑
如:查询作者为yven或者hwt的书籍信息
models.Book.objects.filter(Q(authors__name='yven') | Q(authors__name='hwt'))
查询函数可以混合使用Q对象和关键字参数,所有提供给查询函数的参数都将"AND"在一起,但是,如果出现Q对象,它必须位于所有关键字参数的前面。