day52 Django-ORM多表增删改查
day52 Django-ORM多表增删改查
创建关系字段
class Author(models.Model):
name=models.CharField(max_length=32)
age=models.IntegerField()
ad=models.OneToOneField(to="AuthorDetail",to_field="id",on_delete=models.CASCADE)
# to_field="id" 可以不写,默认找主键
# to="AuthorDetail", to=可以不用写
# models.SET_NULL 置空
# on_delete=models.CASCADE 默认是级联删除,想做级联更新,直接去数据库修改表结构
class AuthorDetail(models.Model):
birthday=models.DateField()
telephone=models.CharField(max_length=16)
addr=models.CharField(max_length=64)
class Publish(models.Model):
name=models.CharField(max_length=32)
city=models.CharField(max_length=32)
class Book(models.Model):
title = models.CharField(max_length=32)
publishDate=models.DateField()
price=models.DecimalField(max_digits=5,decimal_places=2)
publishs=models.ForeignKey(to="Publish",to_field="id",on_delete=models.CASCADE)
authors=models.ManyToManyField(to='Author',)
基本的建表语句与单表操作一致。多个表关系有一对一、一对多和多对多,分别可以用下面这三种方式创建:
# 一对一,外键 + 唯一
ad=models.OneToOneField(to="AuthorDetail",to_field="id",on_delete=models.CASCADE)
# 一对多,外键
publishs=models.ForeignKey(to="Publish",to_field="id",on_delete=models.CASCADE)
# 多对多,创建第三张表
authors=models.ManyToManyField(to='Author',)
操作一对一或一对多的关联表的时候,我们可以通过类属性或者表字段的形式来进行操作,字段名通常是类属性名 + 下划线 + 外键名
:
- 类属性:
ad
、publishs
- 表字段:
ad_id
、publishs_id
对于多对多的关系,可以通过直接操作新生成的第三张表,表名为 应用名 + 下划线 + 当前表名 + 下划线 + 属性名
,也可以通过类属性和表字段来操作第三张表:
- 类属性:
authors
- 表名:
app01_book_authors
- 表字段:
id
、author_id
、book_id
其具体用法会在下面详细讨论。
多表增删改查
增加
一对一
如前所述,一对一增加数据可以通过使用类属性或表字段的方式添加与另一个表相关联的字段:
models.AuthorDetail.objects.create(
birthday='2018-11-11',
telephone='15122220000',
addr='北京',
)
models.Author.objects.create(
name='金龙',
age=2,
ad=models.AuthorDetail.objects.get(id=1), # 类属性
)
models.Author.objects.create(
name='金龙2',
age=2,
ad_id=2, # 表字段
)
一对多
models.Book.objects.create(
title='白洁',
publishDate='2011-01-01',
price=200,
publishs=models.Publish.objects.get(id=1)
)
models.Book.objects.create(
title='白洁第二部',
publishDate='2011-01-01',
price=300,
publishs_id=1
)
多对多
一本书可能会有多个作者,一个作者也可能写很多本书。比如,编号为1的书是编号为1和2的两个作者所著,那么在作者与书籍的关系表中,我们就应该创建书1对作者1和书1对作者2两条关系,就像这样:
+----+---------+-----------+
| id | book_id | author_id |
+----+---------+-----------+
| 1 | 1 | 1 |
| 2 | 1 | 2 |
+----+---------+-----------+
具体的操作用两种方式,一种是通过对象添加,另一种是通过值直接添加
方式1
通过找到作者的object对象添加:
book_obj = models.Book.objects.get(id=1)
author1 = models.Author.objects.get(id=1)
author2 = models.Author.objects.get(id=2)
book_obj.authors.add(author1,author2)
book_obj.authors.add(*[author1,author2])
方式2
通过作者id,直接添加书籍的作者信息:
book_obj = models.Book.objects.get(id=1)
book_obj.authors.add(1,2)
book_obj.authors.add(*[1,2])
删除
一对一和一对多
一对一和一对多的删除都是找到object对象之后,直接delete
models.Author.objects.get(id=1).delete()
多对多
多对多的删除需要使用query对象进行操作:
book_obj = models.Book.objects.get(id=1) # 找到id为1的书
book_obj.authors.remove(3) #删除id为1的书与id为3的作者之间的关系
book_obj.authors.remove(2,3) # 删除id为1的书与id为2和3的作者之间的关系
book_obj.authors.clear() # 清空id为1的书的所有作者信息
#修改操作
book_obj.authors.set(['4',]) #先清空再添加 clear+add
修改
修改 update,一对多和一对一操作时和单表的是一样的:
models.Book.objects.filter(id=1).update(
title='白洁新版',
# publishs=models.Publish.objects.get(id=3),
publishs_id=3
)
查询
基于对象的跨表查询
一对一
正向查询
查询金龙2这个作者的家庭住址
author_obj = models.Author.objects.get(name='金龙2')
# author_obj.ad -- 直接拿到authordetail表中的那个条记录对象
print(author_obj.ad.addr)
反向查询
查询电话号码以151开头的那个作者的姓名
authordetail_obj = models.AuthorDetail.objects.filter(telephone__startswith='151').first()
print(authordetail_obj.author.name)
一对多
正向查询
白洁新版这本书是哪个出版社出版的
book_obj = models.Book.objects.filter(title='白洁新版').first()
print(book_obj.publishs.name)
反向查询
东京出版社出版了哪些书
pub_obj = models.Publish.objects.get(name='东京出版社')
books = pub_obj.book_set.all().values('title')
# 东京出版社出版的名称中还有白洁的那些书
books = pub_obj.book_set.filter(title__contains='白洁').values('title')
print(books)
多对多
正向查询
白洁新版这本书的作者都有谁
book_obj = models.Book.objects.filter(title='白洁新版').first()
authors = book_obj.authors.all().values('name')
print(authors)
反向查询
查询一下金龙2写了哪些书
jinlong2_obj = models.Author.objects.get(name='金龙2')
ret = jinlong2_obj.book_set.all().values('title')
print(ret)
跨表查询总结
- 正向查询靠属性
- 如果是一对一,反向查询靠小写类名
- 一对多和多对多,反向查询靠小写类名_set