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 email
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.两种设置外键字段的方式

  1. 实际字段指定id publish_id=id
  2. 虚拟字段指定对象 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
分享
二维码
海报
Django 多表操作
Django 多表操作 一、准备表和基础数据 1.准备表 1.表设计 from django.db import models # Create your models here. class Books(models.Model): titl……
<<上一篇
下一篇>>