Django 多表操作
Django 多表操作
一、准备表和基础数据
1.准备表
1.表设计
from django.db import models
# Create your models here.
class Books(models.Model):
title = models.CharField(verbose_name='书名', max_length=32)
price = models.DecimalField(verbose_name='价格', max_digits=8, decimal_places=2)
publish_time = models.DateField(auto_now_add=True)
"""
提示: django自带的sqlite3数据库对日期格式不是很敏感 处理的时候容易出错
DateField
DateTimeField
两个重要参数
auto_now:每次修改数据的时候都会自动更新当前时间
auto_now_add:只在数据创建的时候记录一次创建时间 后续不会自动更改
"""
def __str__(self):
return self.title
class Book(models.Model):
title = models.CharField(verbose_name='书名', max_length=32)
price = models.DecimalField(verbose_name='价格', max_digits=8, decimal_places=2)
publish_time = models.DateField(verbose_name='出版日期', auto_now_add=True)
# 一对多 外键字段建在多的一方
publish = models.ForeignKey(to='Publish',on_delete=models.DO_NOTHING)
# 多对多 外键字段推荐建在查询频率较高的表中
authors = models.ManyToManyField(to='Author')
def __str__(self):
return self.title
class Publish(models.Model):
title = models.CharField(verbose_name='名称', max_length=32)
addr = models.CharField(verbose_name='地址', max_length=128)
email = models.EmailField(verbose_name='邮箱')
def __str__(self):
return self.title
class Author(models.Model):
name = models.CharField(verbose_name='姓名', max_length=32)
age = models.IntegerField(verbose_name='年龄')
# 一对一 外键字段推荐建在查询频率较高的表中
author_detail = models.OneToOneField(to='AuthorDetail',on_delete=models.DO_NOTHING)
def __str__(self):
return self.name
class AuthorDetail(models.Model):
phone = models.BigIntegerField(verbose_name='电话')
addr = models.CharField(verbose_name='地址', max_length=32)
def __str__(self):
return self.addr
'''
执行python3 manage.py makemigrations 报错TypeError: __init__() missing 1 required positional argument: 'on_delete'
缺少一个必需的位置参数:“on_delete”,解决方法:https://blog.csdn.net/m0_38109046/article/details/82660038
'''
2.准备数据
先往出版社、作者表、作者详情表准备一些数据。
因为一对一关系外键和一对多差不多,我们一对多来操作,把一对一的表先建立好,不做操作。
出版社表是一个被图书表关联的表我们先建立这个被关联表。
书籍表和关系表通过orm实现。
publish | |||
---|---|---|---|
id | title | addr | |
1 | 东方红出版社 | 中国 | 275242996@qq.com |
2 | 安慕希出版社 | 月球 | 202135897@qq.com |
3 | 太空出版社 | 太空 | 999888666@111.com |
author | |||
---|---|---|---|
id | name | age | author_detail_id |
1 | zhaozong | 8 | 3 |
2 | Shawn | 28 | 1 |
3 | kangkang | 3 | 2 |
authordetail | ||
---|---|---|
id | phone | addr |
2 | 112 | 天水 |
1 | 114 | 山东 |
3 | 119 | 重庆 |
二、一对多表结构的增删改
1.两种设置外键字段的方式
- 实际字段指定
id publish_id=id
- 虚拟字段指定对象
publish=publish_obj
2.增create
#方式一:直接传指定字段
models.Book.objects.create(title='论语', price=666.98, publish_id=1)
models.Book.objects.create(title='孟子', price=444.44, publish_id=2)
models.Book.objects.create(title='老子', price=555.55, publish_id=3)
#方式二:传数据对象
publish_obj = models.Publish.objects.filter(pk=1).first()
models.Book.objects.create(title='三字经',price=666.66,publish=publish_obj)
3.删delete
models.Book.objects.filter(pk=1).delete() # 级联删除
4.改update
#方式一:实际字段
models.Book.objects.filter(pk=2).update(publish_id=1)
#方式二:虚拟字段
publish_obj = models.Publish.objects.filter(pk=2).first()
models.Book.objects.filter(pk=2).update(publish=publish_obj)
三、多对多表结构的增删改(API)本质就是操作虚拟中间表(第三张表)
1.增add
- add:括号内既可以传数字也可以传对象 并且都支持多个
# 给书籍增加作者
book_obj = models.Book.objects.filter(pk=3).first()
print(book_obj.authors) # app01.Author.None(就类似于你已经到了第三张关系表了)
book_obj.authors.add(1) # 添加一个book_id是3对应的author_id是1的记录
book_obj.authors.add(2, 3)
book_obj = models.Book.objects.filter(pk=3).first() # id为3的书籍
authors1 = models.Author.objects.filter(pk=1).first() # id为1的作者
authors2 = models.Author.objects.filter(pk=2).first() # id为2的作者
authors3 = models.Author.objects.filter(pk=3).first() # id为3的作者
book_obj.authors.add(authors1) # id为3的书籍添加一个id为1的作者
book_obj.authors.add(authors2, authors3) # id为3的书籍添加id为2和3的作者
2.删remove
- 括号内既可以传数字也可以传对象并且都支持多个
book_obj = models.Book.objects.filter(pk=3).first()
book_obj.authors.remove(2) # 删除中间表中book_id是3的对应的author_id是2的记录
book_obj.authors.remove(1, 3)
author_obj = models.Author.objects.filter(pk=2).first() # id为2的作者
author_obj1 = models.Author.objects.filter(pk=3).first() # id为3的作者
book_obj.authors.remove(author_obj, author_obj1) # 从id为3的书籍中删除这两个作者
3.改set
- 括号内必须传一个可迭代对象,该对象内既可以是数字也可以是对象 并且都支持多个
- set操作是一种新增操作. 执行set一上来先将括号内没有指定的全部清除, 如果有则保留, 没有则新增. 如果想修改某一个数据, 必须对源数据进行指定, 不然源数据将会被清除.
book_obj = models.Book.objects.filter(pk=3).first()
book_obj.authors.set([1, 2]) # 先删除author_id不是等于1,2的, 如果没有1, 2则添加
book_obj = models.Book.objects.filter(pk=3).first()
authors1 = models.Author.objects.filter(pk=1).first()
authors2 = models.Author.objects.filter(pk=2).first()
book_obj.authors.set([authors1, authors2])
4.清空clear
- 括号内不要加任何参数
book_obj = models.Book.objects.filter(pk=3).first()
book_obj.authors.clear() # 清空book_id为3所对应的所有author_id
十三、查询储备知识
1.正向查询
- 正向查询按照字段
- 正向:外键字段在的一方查不在的一方,外键字段在谁那儿,谁查另外的人就是正向
- 如果返回结果多个还需要
.all()
2.反向查询
- 反向查询按照表名小写
- 反向:没有外键字段,就是外键字段不在的一方查在的一方
- 如果返回结果多个还需要连接
_set.all()
# 提示:
书籍与作者, 外键字段在书籍.
作者与作者详情, 外键字段在作者.
书籍与出版社外键字段在书籍.
总结:正向反向查询就是判断你是否有关联的外键字段
十四、子查询:基于对象的跨表查询
1.正向查询
- 表内有关联字段, 可以直接使用
[对象].[字段名]
的方式查询到结果 - 当结果有多个情况下,需要加上
.all()
, 如果只是一个则直接拿到对象
示例:
'''
正向查询按照字段
当你的结果可能有多个的情况下就需要加.all()
book_obj.authors.all()
如果是一个则直接拿到数据对象
book_obj.publish
author_obj.author_detail
'''
# 1. 查询书籍主键为1的出版社
book_obj = models.Book.objects.filter(pk=1).first() # 先拿到对象
print(book_obj.publish.title) # 出版社字段在book中,直接点取值
# 2. 查询书籍主键为2的作者: 一本书可能被多个作者出版使用.all()
book_obj = models.Book.objects.filter(pk=2).first() # 先拿到书籍对象
print(book_obj.authors) # app01.Author.None (作者外键在book表中,直接点取值)
print(book_obj.authors.all()) # <QuerySet [<Author: kangkang>, <Author: zhaozong>]>
# 3. 查询作者kangkang的电话号码:
author_obj = models.Author.objects.filter(name='kangkang').first() # 先拿到作者对象
print(author_obj.author_detail) # AuthorDetail object(详情字段在作者表中,直接点)
print(author_obj.author_detail.phone) # 112
2.反向查询
- 表内没有关联的字段, 查询的结果有多个就必须加
_set.all()
- 如果查询结果就一个则不需要加
_set.all()
'''
反向查询按表名小写
如果查询结果可以有多个就必须需要加_set.all()
publish_obj.book_set.all()
author_obj.book_set.all()
如果查询结果只有一个的时候就不需要加_set.all()
author_detail_obj.author
'''
# 4. 查询出版社是东方出版社出版的书: 一个出版社可以出版多本书使用.all()
publish_obj = models.Publish.objects.filter(title='东方出版社').first()
print(publish_obj.title) # app01.Book.None
print(publish_obj.title.all()) # <QuerySet [<Book: 论语>, <Book: 三字经>]>
# 5. 查询作者是jason写过的书: 一个作者jason可以写过多本书
author_obj = models.Author.objects.filter(title='jason').first()
print(author_obj.book_set) # app01.Book.None
print(author_obj.book_set.all()) # <QuerySet [<Book: 夕阳西下>]>
# 6. 查询手机号是114的作者姓名
author_detail_obj = models.AuthorDetail.objects.filter(phone=114).first()
print(author_detail_obj.author.name) # jason
3.总结
'''子查询: 基于对象的跨表查询'''
# 正向: 查询书籍主键为1的出版社
models.Book.objects.filter(pk=1).first().publish.name
# 正向: 查询书籍主键为2的作者
models.Book.objects.filter(pk=2).first().authors.all()
# 正向: 查询作者jason的电话号码
models.Author.objects.filter(name='jason').first().author_detail.phone
# 反向: 查询出版社是东方出版社出版的书
models.Publish.objects.filter(name='东方出版社').first().publish_set.all()
# 反向: 查询作者是jason写过的书
models.Author.objects.filter(name='jason').first().book_set.all()
# 反向: 查询手机号是110的作者姓名
models.AuthorDetail.objects.filter(phone=110).first().author.name
十五、基于双下划线的跨表查询 : 连表查询
正向查询使用字段名, 反向查询表名小写
1.示例:查找书名是“红楼梦”的书的作者的手机号码
# 正向查询
res = models.Book.objects.filter(title='红楼梦').values('authors__author_detail__phone')
print(res)
# 反向查询
res = models.Author.objects.filter(book__title='红楼梦').values('author_detail__phone')
print(res)
res = models.AuthorDetail.objects.filter(author__book__title='红楼梦').values('phone')
print(res)
2.示例 : 查询城市是北京的出版社出版过的所有书籍
# 正向查询
res = models.Publish.objects.filter(addr='北京').values('book__title')
print(res) # <QuerySet [{'book__title': '红楼梦'}, {'book__title': '包子秘籍'}]>
# 反向查询
res = models.Book.objects.filter(publish__addr='北京').values('title')
print(res) # <QuerySet [{'title': '红楼梦'}, {'title': '包子秘籍'}]>
3.示例:查询手机号以133开头的作者出版过的书籍名称以及书籍出版社名称
# 正向查询
res = models.AuthorDetail.objects.filter(phone__startswith=133).values('author__book__title','author__book__publish__name')
print(res)
# 反向查询
res = models.Author.objects.filter(author_detail__phone__startswith=133).values('book__title','book__publish__name')
print(res)
res = models.Book.objects.filter(authors__author_detail__phone__startswith=133).values('title','publish__name')
print(res)
res = models.Publish.objects.filter(book__authors__author_detail__phone__startswith=133).values('book__title','name')
print(res)
4.总结
'''连表查询: 基于双下划线的连表查询'''
# 查询jason的手机号和作者姓名
# 正向
models.Author.objects.filter(name='jason').values('author_detail__phone', 'name')
# 反向
models.AuthorDetail.objects.filter(author__name='jason').values('phone', 'author__name')
# 查询书籍主键为1的出版社名称和书的名称
# 正向
models.Book.objects.filter(pk=1).values('publish__name', 'title')
# 反向
models.Publish.objects.filter(book__pk=1).values('name' 'book__title')
# 查询书籍主键为1的作者姓名
# 正向
models.Book.objects.filter(pk=1).values('authors__name')
# 反向
models.Author.objects.filter(book_pk=1).values('name')
# 查询书籍主键是1的作者的手机号
# 正向
models.Book.objects.filter(pk=1).values('authors__author_detail__phone')
# 反向
models.AuthorDetail.objects.filter(author__book__pk=1).values('phone')
版权声明:
作者:淘小欣
链接:https://blog.taoxiaoxin.club/153.html
来源:淘小欣的博客
文章版权归作者所有,未经允许请勿转载。
THE END
0
二维码
海报
Django 多表操作
Django 多表操作
一、准备表和基础数据
1.准备表
1.表设计
from django.db import models
# Create your models here.
class Books(models.Model):
titl……

共有 0 条评论