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-多表操作的主要内容,如果未能解决你的问题,请参考以下文章

Django-ORM操作数据库无数据问题

Django-ORM操作

Django-ORM系统详述

django-ORM操作

Django-ORM操作

Django-orm操作的基础认识