Django | ORM多表操作

多表操作前期准备

models.py

class Book(models.Model):
    name = models.CharField(max_length=32,verbose_name='书名')
    price = models.DecimalField(max_digits=8,decimal_places=2,verbose_name='价格')
    publish_date = models.DateTimeField(auto_now_add=True,verbose_name='出版时间')
    publish = models.ForeignKey('Publish')  # 一对多
    authors = models.ManyToManyField('Author')   # 多对多


class Publish(models.Model):
    name = models.CharField(max_length=32,verbose_name='出版社名字')
    addr = models.CharField(max_length=32,verbose_name='出版社地址')

class Author(models.Model):
    name = models.CharField(max_length=32,verbose_name='作者名字')
    age = models.IntegerField(verbose_name='作者年龄')
    author_detail = models.OneToOneField('AuthorDetails')   # 一对一

class AuthorDetails(models.Model):
    phone = models.BigIntegerField(verbose_name='作者电话')
    addr = models.CharField(max_length=32,verbose_name='作者地址')

一对多的外键增删改查

# 一对多外键增加数据

    # 方式一:直接写实际字段
    models.Book.objects.create(name='红楼梦',price=111.11,publish_id=1)
    # 方式二:虚拟字段
    publish_obj = models.Publish.objects.filter(pk=2).first()
    models.Book.objects.create(name='三国演义',price=22.5,publish=publish_obj)

# 一对多外键删除数据

models.Pulish.objects.filter(pk=1).delete()

# 删除记录是级联删除的,所以对应的外键字段一会直接删除,取消关联关系
# 一对多外键修改数据

    # 方式一:直接写实际字段
    models.Book.objects.filter(pk=1).update(publish_id=2)

    # 方式二:虚拟字段
    publist_obj = models.Publish.objects.filter(pk=3).first()
    models.Book.objects.filter(pk=2).update(publish=publist_obj)

多对多外键增删改查:

# 多对多的增删改查其实就是在操作第三张虚拟表
# 如何添加作者
# 这张表不是我们自己创建的是系统帮助我们创建的,所以我们根本不能通过models的方法(.)到这张表

# 这里就要通过对象(.)外键的方式找到第三张表
# 多对多表关系的增加:
# 关键字:.add(主键值)
    # 方式一:
    book_obj = models.Book.objects.filter(pk=1).first()
    book_obj.authors.add(1)
   	book_obj.authors.add(2,3)   # 增加多个
    # 方式二:
    author_obj = models.Author.objects.filter(pk=1).first()
    author_obj1 = models.Author.objects.filter(pk=2).first()
    author_obj2 = models.Author.objects.filter(pk=3).first()
    book_obj.authors.add(author_obj)
    book_obj.authors.add(author_obj1,author_obj2)   # 增加多个

# 多对多表关系的删:
# 关键字:.remove(主键值)
    # 方式一:
    book_obj = models.Book.objects.filter(pk=1).first()
    book_obj.authors.remove(2,3)  # 同样支持多条数据同时删除
    # 方式二:对象
    book_obj1 = models.Book.objects.filter(pk=2).first()
    author_obj1 = models.Author.objects.filter(pk=1).first()
    author_obj2 = models.Author.objects.filter(pk=2).first()
    author_obj3 = models.Author.objects.filter(pk=3).first()
    book_obj1.authors.remove(author_obj1,author_obj2)

# 多对多表关系的修改
# 关键字:set()
    # 方式一:
    book_obj = models.Book.objects.filter(pk=1).first()
    book_obj.authors.set([2,3])  # 同样支持多条数据同时修改
    # 方式二:对象
    book_obj1 = models.Book.objects.filter(pk=2).first()
    author_obj1 = models.Author.objects.filter(pk=1).first()
    author_obj2 = models.Author.objects.filter(pk=2).first()
    author_obj3 = models.Author.objects.filter(pk=3).first()
    book_obj1.authors.set([author_obj1,author_obj2])

# 注意:set关键字括号内必须填写一个可迭代对象(列表或者元组)

# 多对多表关系的清空(清空在第三中某个书籍与作者的绑定关系)
# 关键字:clear()  #  括号内不要加任何参数

正反向查询概念

# 通过查看外键的位置来判断是正向查询还是反向查询
# 正向查询:外键字段在A表里 ,A查B表就是正向查询
# 反向查询:那么B查A表就是反向查询
# eg:
	Book(外键字段在这里) >>  Publish   # 正向查询
    Publist(外键字段不在这里)  >> Book  # 反向查询

通过正反向的概念我们再来研究跨表查询。

多表查询

引子:

# 正向查询按 字段(外键字段)
# 反向查询按 表名小写_set

# 注意:
在书写orm语句的时候跟写sql语句一样的
不要企图一次性将orm语句写完 如果比较复杂 就写一点看一点

子查询(基于对象的跨表查询)

# 查找主键值为1的书的出版社名字和出版社地址(正向)

book_obj = models.Book.objects.filter(pk=1).first()  # 先拿到主键为1的书籍对象
res = book_obj.publish  # 拿到外键字段所对应关系表中的对象
print(res)   # 对应出版社的对象
print(res.name)      # 出版社名字
print(res.addr)      # 出版社地址

# 查询书籍主键为2的作者(正向)
    book_obj = models.Book.objects.filter(pk=2).first()
    res = book_obj.authors    # 
    print(res)

# 我们发现返回了一个:app01.Author.None
# 注意这个返回结果是一个标志性的问题:在当返回结果可能为多个的时候(一本书可以由多名作者编写(多对多的表关系)必须加关键字.all())

 # # 查询书籍主键为2的作者(正向)
    book_obj = models.Book.objects.filter(pk=2).first()
    res = book_obj.authors.all()
    print(res)
    print(res[0].name)   # 因对象有多个所以要通过索引取值拿到单独的对象
    print(res[1].name)

# 查询作者gary的电话号码(正向)
    author_obj = models.Author.objects.filter(name='gary').first()
    print(author_obj)
    res = author_obj.author_detail
    print(res)
    print(res.phone)
    print(res.addr)

# 查询是南方出版社出版的书(无外键字段(反向))
    publish_obj = models.Publish.objects.filter(name='东方出版社').first()
    res = publish_obj.book
    print(res)

# 反向查询上边有提到需要加入_set
# 查询是南方出版社出版的书(无外键字段(反向))
    publish_obj = models.Publish.objects.filter(name='南方出版社').first()
    res = publish_obj.book_set.all()  # 并且使用到.all()方法
    print(res)

# 5.查询作者是jason写过的书
    author_obj = models.Author.objects.filter(name='jason').first()
    res = author_obj.book_set.all()
    print(res)

# 6.查询手机号是110的作者姓名
    authorsetails_obj = models.AuthorDetails.objects.filter(phone='110').first()
    res1 = authorsetails_obj.author
    print(res1)

总结

# 基于对象 
反向查询的时候
    当你的查询结果可能有多个的时候 就必须加_set.all()
    当你的结果只能有一个的时候 不需要加_set.all()

联表查询(基于双下划线的跨表查询)

# 同样遵循
# 正向查询按字段
# 反向查询按表名小写

习题:

# 查询作者gary的手机号(必须用一行代码展示)
    res = models.Author.objects.filter(name='gary').values('author_detail__phone','name')
    print(res)
    
# 利用values方法查询
注意:此时Author为基表可通过values方法直接查到该表的属性值


# 反向:(不允许.Author)
    res = models.AuthorDetails.objects.filter(author__name='gary').values('phone','author__name')
    print(res)

# 查询书籍主键为1的出版社的名称和书的名称
    res = models.Book.objects.filter(pk=1).values('name','publish__book')
    print(res)

# 方式二:反向
    res = models.Publish.objects.filter(book__id='1').values('name','book__name')
    print(res)

# 3.查询书籍主键为1的作者姓名
res = models.Book.objects.filter(pk=1).values('authors__name')
print(res)
# 方式二:反向
    res = models.Author.objects.filter(book__id=1).values('name')
    print(res)

最终练习:

# 查询书籍主键是1的作者的手机号
# 注意:这道题涉及到三张表
# 思路:通过书籍查作者,再通过作者查作者详情
    res = models.Book.objects.filter(pk=1).values('authors__author_detail__phone')

posted @ 2022-03-04 16:13  JasonBorn  阅读(49)  评论(0编辑  收藏  举报