多表操作

一、Django多表ORM设计规则

'''
1. 关联的表之间建议建立外键,但可以取消关联关系(db_constraint=False)
2. 关联表之间的外键字段建议采用对应类名的全小写
3. 采用关联表的主键或对象均能进行操作
'''
'''
# 一对多:出版社(一) 书籍(多,外键建在多的一方,依赖于出版社)
# 一对一:作者详情(一) 作者(一,外键在任意一方均可,一旦外键放在作者中,作者依赖于作者详情,要保证唯一性)
# 多对多:作者(多)书籍(多)建立关系表(存放两个表的外键信息 => 将建表转化为关系对应字段)

# Book书籍:id  name  price  publish_date  publish(publish_id)
class Book(models.Model):
    id = models.AutoField(primary_key=True)
    name = models.CharField(max_length=20)
    price = models.DecimalField(max_digits=5, decimal_places=2)
    publish_date = models.DateField()
    publish = models.ForeignKey(to='Publish', to_field='id')
    # 多对多关系字段,该字段不会再book表中形成字段,是用来创建关系表的
    author = models.ManyToManyField(to='Author')

# Author作者:id  name auto_detail(auto_detail_id)
class Author(models.Model):
    id = models.AutoField(primary_key=True)
    name = models.CharField(max_length=20)
    # author_detail = models.ForeignKey(to='AuthorDetail', to_field='id', unique=True)
    author_detail = models.OneToOneField(to='AuthorDetail', to_field='id')

# AuthorDetail作者详情: id  age  telephone  info
class AuthorDetail(models.Model):
    id = models.AutoField(primary_key=True)
    age = models.IntegerField()
    telephone = models.BigIntegerField()
    # 存大文本
    info = models.TextField()

# Publish出版社:id  name  address
class Publish(models.Model):
    id = models.AutoField(primary_key=True)
    name = models.CharField(max_length=20)
    address = models.CharField(max_length=64)
'''

二、一对多关系

规则:

'''
1. 关系中 『多』 依赖于 『一』
2. Django 1.x版本外键关联默认有级联删除,2.x版本需要手动明确外键的级联删除(on_delete=models.CASCADE)
'''

① 增

'''
①对象操作
# 先有出版社,才有书籍
publish = Publish.objects.create(name='老男孩出版社',address='上海')
# 外键为关联对象
Book.objects.create(name='龙族',price=33.33,publish_date='2018-10-3',publish=publish)

②主键操作
id = Publish.objects.create(name='小女孩出版社',address='山西').id
# 外键字段为关联对像主键
Book.objects.create(name='西游记',price=66.66,publish_date='1990-10-17',publish_id=id)
'''

② 删

'''
# 删除出版社,默认有级联删除,出版社出版的数据全会被删除
Publish.objects.first().delete()

#删除书,出版社不会被删除
Book.objects.last().delete()
'''

③ 改

'''
# 书籍的出版社修改必须为已存在的出版社
①对象操作
publish = Pubish.objects.last()
book = Book.objects.last()
book.publish = publish
book.save()

②主键操作
publish_id = Publish.objects.last().id
Book.objects.filter(name='龙族).update(publish_id=publish_id)
'''

三、一对一关系

规则:

'''
1. 通过外键所在表决定依赖关系
'''

① 增、删、改

'''
# 操作规则同一对多关系,有外键的表依赖于另一张表

# 增:遵循操作顺序
①对象操作
auto_detail = AuthorDetail.objects.create(age=24,telephone=12888888888,info='天才')
Author.objects.create(name='henry',author_detail=auto_detail)
②主键操作
id = AuthorDetail.objects.create(age=23,telephone=13666666666,info='美丽迷人').id
Author.objects.create(name='wendy',author_detail_id=id)

# 删:拥有级联删除
AuthorDetail.objects.first().delete()

# 改:一般不考虑连表更新
'''

四、多对多关系

规则:

'''
1. 多对多关系存在关系表,关系表建议采用ManyToManyField字段处理
2. 需要手动创建关系表时,在字段中明确through与through_field值
'''

① 增、删、改

'''
通过关系表字段存在的类的对象获取关系表 book.author


b1 = Book.objects.first() 
b2 = Book.objects.all()[1]  
a1 = Author.objects.first()  
a2 = Author.objects.all()[1]

# 增:为书籍添加作者的主键或对象们
book.author.add(*args)
①对象添加
b1.author.add(a1,a2)
②主键添加
b2.author.add(a1.id,a2.id)

# 删:
①将关系记录全部清除
book.author.clear()
b2.author.clear()

②删除书籍已有作者的主键或对象们
book.author.remove(*args)
通过对象:b1.author.remove(a1)
通过主键:b1.author.remove(a2.id)

# 改:
①清空该本书的所有关系记录并添加新的作者的主键或对象(这种方法是添加了新的关系) 
book.author.clear()
book.author.add(*args)

b1.author.clear()
b1.author.add(a1.id)
b1.author.add(a2)

②设置作者的主键或对象形式的列表, 去除新数据中不存在的值,添加新数据中新有的值,与之前重复的值不发生任何变化保留下来
book.author.set([*args])

b2.author.set([a2.id])
b2.author.set([a1, a2])
'''

五、跨表查询规则:

'''
1. 正向逆向概念:从存放外键的表到关系表称之为正向跨表查询,反之称之为逆向查询
2. 正向查询通过外键字段名进行跨表查询
3. 逆向查询通过关联表对应类名小写进行跨表查询
'''

六、基于对象的跨表查询

'''
在跨表查询的规则上,跨表查询的结果为多条数据时需要在字段后添加_set

1.一对一
#正向 需求:打印第一个作者的电话
author = Author.objects.first()  # 查询得到作者对象
author_detail = author.author_detail  # 基于对象跨表获取作者详情对象,正向通过字段名 
print(author_detail.telephone)

#逆向 需求:最后一个作者详情的作者名
author_detail = AuthorDetail.objects.last()
author = author_detail.author  # 基于对象跨表获取作者对象,逆向通过表名小写 author
print(author.name)

2.一对多
#正向 需求:打印第一本书的出版社名
book = Book.objects.first()  # 查询得到书籍对象
publish = book.publish  # 获取出版社对象,正向通过字段名 publish
print(publish.name)

#逆向 需求:第一个出版社出版过的所有书的名字
publish = Publish.objects.first()
book_list = publish.book_set.all()  # 获取书籍对象们,逆向通过表名小写 book,多条数据添加_set
for book in book_list:
    print(book.name)

3.多对多
#正向 需求:第一本书的作者们的姓名
book = Book.objects.first()  # 查询得到书籍对象
author_list = book.author.all()   # 获取作者对象们,正向通过字段名 author
for author in author_list:
	print(author.name)

#逆向 需求:第二个作者写过的书们的书名
author = Author.objects.all()[1]
book_list = author.book_set.all()  # 获取书籍对象们,逆向通过表名小写 book,多条数据添加_set
for book in book_list:
	print(book.name)

'''

'''
多级跨表
#需求:第一个出版社出版过书们的作者们的电话号码
publish = Publish.objects.first()  # type: Publish
books = publish.book_set.all()
for book in books:  # type: Book
    authors = book.author.all()  # type: Author
    for author in authors:  # type: Author
        print(author.author_detail.telephone)
'''

七、基于双下划线的跨表查询 注:不能出现对象

'''
满足跨表查询规则
filter方法与values方法用来支持__查询规则,得到的是QuerySet类型

1.一对一
#需求: 查询所有大于80岁作者的名字与实际年龄
authors_infos =  Author.objects.filter(author_detail__age__gt=80).values('name', 'author_detail__age')
print(authors_infos)

2.一对多
#需求:第一本书的出版社名
publish_name =  Book.objects.filter(id=3).values('publish__name')
print(publish_name)

需求:第一个出版社出版过的书的名字和价格,
books_infos = Publish.objects.filter(id=2).values('book__name', 'book__price')
print(books_infos)

3.多对多
#需求:获取名字中包含e(不区分大小写)的作者出版过的书的书名与价格(需要去重)
book_infos=Author.objects.filter(name__icontains='e').values('book__name','book__price').distinct()
print(book_infos)

#需求:获取书籍价格不超过50元的作者名(需要去重)
author_infos=Book.objects.filter(price__lte=50).values('author__name').distinct()
print(author_infos)

# 多表关联:查询出版社在上海的出版过的所有书的 作者姓名、作者电话、具体出版社名 的相关信息
book_infos = Publish.objects.filter(address__contains='上海').values('book__author__name', 'book__author__author_detail__telephone', 'name')
print(book_infos)

'''