Loading

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
posted @ 2021-06-19 15:06  Mr-Yang`  阅读(101)  评论(0编辑  收藏  举报