Django-ORM-多表操作
Posted 小杨-先生
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Django-ORM-多表操作相关的知识,希望对你有一定的参考价值。
一、创建模型
表与表之间的关系:一对一、多对一、多对多
一对一
xx = models.OneToOneField(to=\'表名\',to_filed=\'字段名\',on_delete=models.CASCADE)
to_field可以不写,默认是关联到另一张表的主键,
on_delete在1.x版本的Django中不用写,默认是级联删除的,2.x版本的django要写。
一对多
xx = models.ForengnKey(to=\'表名\',to_field=\'字段名\',on_delete=models.CASCAD)
多对多
xx = models.ManyTOManyField(to=\'另外一个表名\') # 这是自动创建第三表
# ManyToManyField就是多对多
实例:
作者模型:一个作者有姓名和年龄。
作者详细模型:把作者的详情放到详情表,包含生日,手机号,家庭住址等信息。作者详情模型和作者模型之间是一对一的关系(one-to-one)
出版商模型:出版商有名称,所在城市以及email。
书籍模型: 书籍有书名和出版日期,一本书可能会有多个作者,一个作者也可以写多本书,所以作者和书籍的关系就是多对多的关联关系(many-to-many);一本书只应该由一个出版商出版,所以出版商和书籍是一对多关联关系(one-to-many)。
# models.py 文件
from django.db import models
# 作者表
class Author(models.Model):
name = models.CharField(max_length=32)
age = models.IntegerField()
# 和Authordetail建立一对一的关系,一对一的这个关系字段写在两个表的任意一个表里。
# 并且ORM会自动给你这个字段名字拼上一个_id,如:authordetail_id
authordetail = models.OneToOneField(to=\'AuthorDetail\', to_field=\'id\', on_delete=models.CASCADE)
# “OneToOneField()” 表示一对一
# “to” 指向表
# “to_field” 指向你关联字段,不写默认会自动关联主键字段
# “on_delete=models.CASCADE” 在2.x之后的需要写上,表示:是否级联删除
# 作者详细信息表
class AuthorDetail(models.Model):
birthday = models.DateField()
telephone = models.CharField(max_length=32)
addr = models.CharField(max_length=64)
# 出版社信息表 和书籍表示一对多的关系
class Publish(models.Model):
name = models.CharField(max_length=32)
city = models.CharField(max_length=32)
email = models.EmailField()
# 书籍信息表
class Book(models.Model):
name = models.CharField(max_length=32)
publishDate = models.DateField()
price = models.DecimalField(max_digits=5, decimal_places=2) # 小数点前五位,小数点后两位
# 建立一对多的关系,外键字段建立在多的一方,字段publishs如果是外键字段,那么它自动是int类型
publishs = models.ForeignKey(to=\'Publish\', on_delete=models.CASCADE)
# 建立多对多的关系,ManyToManyField 可以建立在两个模型中的任意一个
# 并且 author 并不是字段,只能说这个book类里面有author这个字段属性
# 它会自动建立第三张表,就用 author 去操作第三张表
author = models.ManyToManyField(to=\'Author\')
二、增删改
增加记录
一对一
# 方式一:
new_author_detail = models.AuthorDetail.objects.create( # 也是个记录对象
birthday=\'1943-08-04\',
telephone=\'8743845\',
addr=\'成都\'
)
models.Author.objects.create(
name=\'鲍勃\',
age=\'32\',
authordetail=new_author_detail
)
# 方式二:先查询出来,在对外键字段赋值,(常用)
obj = models.AuthorDetail.objects.filter(addr=\'四川\').first() # 转换成记录对象
models.Author.objects.create(
name=\'艾伦\',
age=\'23\',
authordetail_id=obj.id
)
一对多
# 方式一:
obj = models.Publish.objects.get(id=2)
models.Book.objects.create(
name=\'魔道祖师\',
publishDate=\'2011-09-03\',
price=33,
publishs=obj
)
# 方式二:常用
models.Book.objects.create(
name=\'魔道祖师2\',
publishDate=\'2011-09-03\',
price=34,
publishs_id=models.Publish.objects.get(name=\'中国华侨出版社\').id
)
多对多
# 方式一:常用
book_obj = models.Book.objects.get(id=1) # 获取这本书的记录对象
book_obj.author.add(*[1, 2]) # 在使用记录对象中的author属性add添加作者id
# 方式二:
author1 = models.Author.objects.get(name=\'艾伦3\') # 先查出作者记录对象
author2 = models.Author.objects.get(name=\'田姊\')
book_obj = models.Book.objects.get(name=\'格局\') # 在查出书籍记录对象
book_obj.author.add(*[author2, author1]) # 书籍记录对象add添加作者记录对象
删除记录
一对一
一对一和一对多的删除和单表删除是一样的
一对一 表一外键关联到表二,表一删除,不影响表二,表二删除会影响表一。
一对多
# 删除中国华侨出版社,并且还会级联删除所有的所有中国华侨出版社的书籍
models.Publish.objects.get(name=\'中国华侨出版社\').delete()
models.Book.objects.get(name=\'格局\').delete() # 只删除书籍不影响出版社
多对多
book_obj = models.Book.objects.get(name=\'格局\')
book_obj.author.remove(1) # 删除对应的格局书籍的作者id
book_obj.author.remove(*[1, 3]) # 删除对应的格局书籍的多个作者id
book_obj.author.clear() # 清空这本书籍的所有作者
book_obj.author.add(1) # 新增
book_obj.author.set([1, 2]) # 删除后添加,——>更新
修改记录
一对一
models.Author.objects.filter(name=\'鲍勃\').update(
name=\'小杨\',
age=18,
authordetail=models.AuthorDetail.objects.get(id=6), # 方式一:
authordetail_id=4 # 方式二:
)
一对多
models.Book.objects.filter(pk=3).update(
name=\'回首往事\',
publishs=models.Publish.objects.get(id=2), # 方式一:
publishs_id=1 # 方式二:
)
多对多
更新的是第三张表的字段
book_obj = models.Book.objects.get(id=2)
book_obj.author.set([1, 2, 3])
# clear 清空
# add 增加
三、基于对象的跨表查询(子查询)
正向查询和反向查询:
关系属性(字段)写在那个类(表)里面,从当前类(表)的数据去查询它关联类(表)的数据叫做正向查询,反之叫做反向查询。
基表(基于那个表)决定了正向还是反向
一对一
# 正向查询
# 查询作者为小杨的电话
author_obj = models.Author.objects.get(name=\'小杨\')
tel = author_obj.authordetail.telephone
print(tel)
# 反向查询
# 查询电话为4323211的作者姓名
author_obj = models.AuthorDetail.objects.get(telephone=\'4323211\')
name = author_obj.author.name
print(name)
"""
正向查询:Authorobj.authordetail 对象.关联属性名称
--------------------->
Author AuthorDetail
<---------------------
反向查询:AuthorDetailobj.author 对象.小写类名
"""
一对多
# 正向查询
# 查询时间简史这本书的出版社
book_obj = models.Book.objects.get(name=\'时间简史\')
name = book_obj.publishs.name
print(name)
# 反向查询
# 查询中国华侨出版社出版了那些书
pub_obj = models.Publish.objects.get(name=\'中国华侨出版社\')
name = pub_obj.book_set.all()
print(name)
"""
正向查询:Bookobj.publishs 对象.关联属性名称
--------------------->
Book Publish
<---------------------
反向查询:Publishobj.book_set.all() 对象.小写类名_set
"""
多对多
# 正向查询
# 时间简史这本书是那些作者写的
book_obj = models.Book.objects.get(name=\'时间简史\')
obj = book_obj.author.all()
print(obj)
# 反向查询
# 作者田姊写了那些书
author_obj = models.Author.objects.get(name=\'田姊\')
obj = author_obj.book_set.all()
print(obj)
"""
正向查询:Bookobj.author.all() 对象.关联属性名称
--------------------->
Book Author
<---------------------
反向查询:Authorobj.book_set.all() 对象.小写类名_set
"""
四、基于双下划线的跨表查询(连表join)
普通版
一对一
# 查询小杨的电话
# 正向查询
obj = models.Author.objects.filter(name=\'小杨\').values(\'authordetail__telephone\')
print(obj)
# 反向查询
obj = models.AuthorDetail.objects.filter(author__name=\'小杨\').values(\'telephone\')
print(obj)
# 查询电话为4323211的作者名字
# 正向查询
obj = models.Author.objects.filter(authordetail__telephone=\'4323211\').values(\'name\')
print(obj)
# 反向查询
obj = models.AuthorDetail.objects.filter(telephone=\'4323211\').values(\'author__name\')
print(obj)
一对多
# 查询时间简史是哪个出版社出版的
# 正向查询
obj = models.Book.objects.filter(name=\'时间简史\').values(\'publishs__name\')
print(obj)
# 反向查询
obj = models.Publish.objects.filter(book__name=\'时间简史\').values(\'name\')
print(obj)
# 中国华侨出版社出版了那些书
# 正向查询
obj = models.Book.objects.filter(publishs__name=\'中国华侨出版社\').values(\'name\')
print(obj)
# 反向查询
obj = models.Publish.objects.filter(name=\'中国华侨出版社\').values(\'book__name\')
print(obj)
多对多
# 时间简史是由那些作者写的
# 正向查询
obj = models.Book.objects.filter(name=\'时间简史\').values(\'author__name\')
print(obj)
# 反向查询
obj = models.Author.objects.filter(book__name=\'时间简史\').values(\'name\')
print(obj)
# 田姊写了那些书
# 正向查询
obj = models.Book.objects.filter(author__name=\'田姊\').values(\'name\')
print(obj)
# 反向查询
obj = models.Author.objects.filter(name=\'田姊\').values(\'book__name\')
print(obj)
进阶版
1、查询中国华侨出版社出版的书的名称以及作者的名字
# 方式一:基于表为Book
obj = models.Book.objects.filter(publishs__name=\'中国华侨出版社\').values(\'name\', \'author__name\')
print(obj)
# 方式二:基于表为Publish
obj = models.Publish.objects.filter(name=\'中国华侨出版社\').values(\'book__name\', \'book__author__name\')
print(obj)
# 方式三:基于表为Author
obj = models.Author.objects.filter(book__publishs__name=\'中国华侨出版社\').values(\'book__name\', \'name\')
print(obj)
2、手机号以432开头的作者出版过的所有书籍以及出版社名称
# 方式一:基于表为Author
obj = models.Author.objects.filter(authordetail__telephone__startswith=432).values(\'book__name\', \'book__publishs__name\')
print(obj)
# 方式二:基于表为Publish
obj = models.Publish.objects.filter(book__author__authordetail__telephone__startswith=\'432\').values(\'book__name\', \'name\')
print(obj)
# 方式三:基于表为AuthorDetail
obj = models.AuthorDetail.objects.filter(telephone__startswith=\'432\').values(\'author__book__name\', \'author__book__publishs__name\')
print(obj)
五、聚合查询、分组查询、F和Q查询
聚合查询
聚合查询使用 aggregate() :他是一个终止句子,它返回一个包含一些键值对的字典。
聚合函数:Avg、Max、Min、Sun、Count。使用的时候需要导入
例如:计算所有图书的平均价格
from django.db.models import Avg
price_avg = models.Book.objects.all().aggregate(average_price=Avg(\'price\'))
print(price_avg)
# 输出
{\'average_price\': Decimal(\'40.000000\')}
分组查询
annotate( ):——为调用的QuerySet中每一个对象都生成一个独立的统计值(统计方法用聚合函数)
例如:查询每个出版社出版的书籍的平均价格
方式一:
from django.db.models import Avg
obj = models.Publish.objects.annotate(price_avg=Avg(\'book__price\')).values(\'name\', \'price_avg\')
print(obj)
# 输出
<QuerySet [{\'name\': \'光明日报出版社\', \'price_avg\': Decimal(\'48.500000\')}, {\'name\': \'中国华侨出版社\', \'price_avg\': Decimal(\'47.500000\')}]>
方式二:
from django.db.models import Avg
obj = models.Book.objects.values(\'publishs__name\').annotate(price_avg=Avg(\'price\'))
print(obj)
# 输出
<QuerySet [{\'publishs__name\': \'光明日报出版社\', \'price_avg\': Decimal(\'48.500000\')}, {\'publishs__name\': \'中国华侨出版社\', \'price_avg\': Decimal(\'47.500000\')}]>
F和Q查询
F查询:
1、F( ) 可以用来比较同一个model 实例中两个不同字段的值。
实例:查询Book表中点赞数大于评论数的书籍
from django.db.models import F
obj = models.Book.objects.filter(good__gt=F(\'comment\'))
print(obj)
2、也支持F( ) 对象之间以及F( ) 对象和常数之间的加减乘除和取模的操作
实例:查询点赞数大于评论数两倍的书籍
from django.db.models import F
obj = models.Book.objects.filter(good__gt=F(\'comment\')*2)
print(obj)
3、修改操作也可以批量修改
实例:将每本书的价格提升30
from django.db.models import F
models.Book.objects.all().update(price=F(\'price\')+30)
Q查询:
1、Q查询可以使用&(与)、|(或)、~
(非)操作符组合。
实例:查询Book书籍中作者名字为“小杨”或者“小明”的书籍
from django.db.models import Q
obj = models.Book.objects.filter(Q(author__name=\'小杨\')|Q(author__name=\'小明\'))
print(obj)
# 输出
<QuerySet [<Book: 魔道祖师1>, <Book: 山海经>]>
2、可以组合&
和|
操作符以及使用括号进行分组来编写任意复杂的Q
对象进行Q嵌套,多层Q嵌套等。
实例:查询作者为 “小杨” 和非 ”光明日报出版社“ 出版的,并且价格小于100的书籍
from django.db.models import Q
obj = models.Book.objects.filter(Q(Q(author__name=\'小杨\') & ~Q(publishs__name=\'光明日报出版社\')) & Q(price__lt=100)).values(\'name\')
print(obj)
ORM执行原生SQL
obj = models.Book.objects.raw(\'select * from app01_book;\')
for i in obj:
print(i.name)
# 输出
山海经
时间简史
回首往事
魔道祖师1
魔道祖师2
魔道祖师3
以上是关于Django-ORM-多表操作的主要内容,如果未能解决你的问题,请参考以下文章