Django ORM 实现数据的多表 增删改查

假定下面这些概念、字段与关系:

  • 作者模型:一个作者有姓名和年龄。
  • 作者详细模型:把作者的详情放到详情表,手机号,家庭住址信息。
  • 作者详情模型 和 作者模型之间是一对一的关系(one-to-one)。
  • 出版社模型:出版社有名称,所在城市以及email。
  • 书籍模型: 书籍有书名和价格、出版日期。
  • 一本书可能会有多个作者,一个作者也可以写多本书,所以作者和书籍的关系就是多对多的关联关系(many-to-many)。
  • 一本书只应该由一个出版商出版,所以出版商和书籍是一对多关联关系(one-to-many)。
  • 书跟作者是多对多关系,利用Django 的建表语句,可以新生成一张”关系表”—> book2author。

ORM中的外键创建和mysql几乎一样,以创建图书表,出版社表,作者表和作者详情表。

三个关键字里面的参数,to用于指定跟哪张表有关系,自动关联主键。to_field\to_fields,也可以自己指定关联字段。

ManyToManyField不会在表中创建实际的字段,而是告诉 Django ORM 自动创建第三张关系表。

ForeignKey、OneToOneField会在字段的后面自动添加 _id后缀,如果你在定义模型类的时候自己添加了该后缀那么迁移的时候还会添加,所以不要自己加下划线id后缀。

1 创建模型

from django.db import models

出版社
class Publish(models.Model):
    # 主键
    nid = models.AutoField(primary_key = True)
    name = models.CharField(max_length = 55)
    city = models.CharField(max_length = 55)
    # email有特定的格式!
    email = models.EmailField()
    def __str__(self):
        return 'nid: %s, name: %s, city: %s, email: %s\n' % (self.nid, self.name, self.city, self.email)

作者详细
class AuthorDetail(models.Model):
    nid = models.AutoField(primary_key = True)
    # 日期的格式
    birthday = models.DateField()
    # 手机号
    telephone = models.BigIntegerField()
    addr = models.CharField(max_length = 55)
    def __str__(self):
        return 'nid: %s, birthday: %s, telephone: %s, addr: %s\n' % (self.nid, self.birthday, self.telephone, self.addr)

作者表
class Author(models.Model):
    nid = models.AutoField(primary_key = True)
    name = models.CharField(max_length = 55)
    # 年龄,int 类型的小数字就可以
    age = models.IntegerField()

    # 由于作者与作者详细表是一对一的关系:所以选择在作者表中这样建立外键
    # 注意这里还是只写 authordetail 就可以了,_id 程序会自动给加的!
    # 注意这里on_delete一定要加!
    # 一对一!
    authordetail = models.OneToOneField(to = 'AuthorDetail', to_field = 'nid', on_delete = models.CASCADE)
    def __str__(self):
        return 'nid: %s, name: %s, age: %s, authordetail: %s\n' % (self.nid, self.name, self.age, self.authordetail)

书籍
class Book(models.Model):
    nid = models.AutoField(primary_key = True)
    title = models.CharField(max_length = 55)
    # 出版日期,日期格式
    pub_date = models.DateField()
    # 价格,最大位数5位,小数后保留两位
    price = models.DecimalField(max_digits = 5, decimal_places = 2)

    # 与出版社表关联的字段 publish_id
    # 注意自己写的时候只写publish就可以了!Django会自动补上_id
    # 注意:on_delete必须要加上!!!
    # 注意 null=true表示允许为空值
    # 一对多!
    publish = models.ForeignKey(to = 'Publish', to_field = 'nid', on_delete = models.CASCADE, null = True)

    # 书跟作者是多对多的关系。理论上需要新建一张关系表。分别将其与书籍表与作者表关联起来!
    authors = models.ManyToManyField(to = 'Author')
    def __str__(self):
        return 'nid: %s, title: %s, pub_date: %s, price: %s, publish: %s, authors: %s\n' % (self.nid, self.title, self.pub_date, self.price, self.publish, self.authors)
"""
    create table book2author(
        id int primary_key auto_increment,
        book_id int,0
        author_id int,
        foreign_key (book_id) references Book(nid),
        foreign_key (author_id) references Author(nid),
    );
"""

2 执行命令

python manage.py makemigrations
python manage.py migrate
查看一下数据库中是否生成了 "5" 张表

3 录入数据

models.Publish.objects.create(name = '东方出版社',city = "beijing", email = "dongfang@beijing.com")
models.Publish.objects.create(name = '京东出版社',city = "shanghai", email = "jingdong@shanghai.com")
models.Publish.objects.create(name = '立方出版社',city = "guangzhou", email = "wuliu@guangzhou.com")

models.Book.objects.create(title = 'Java', price = 199.93, pub_date = '2020-02-23', publish_id = 1)
models.Book.objects.create(title = 'golang', price = 219.5, pub_date = '2019-09-17', publish_id = 2)
models.Book.objects.create(title = 'python', price = 328.21, pub_date = '2020-11-15', publish_id = 3)
models.Book.objects.create(title = 'django', price = 150.6, pub_date = '2018-08-26', publish_id = 1)
models.Book.objects.create(title = 'gin', price = 110.23, pub_date = '2020-06-05', publish_id = 1)
models.Book.objects.create(title = 'spring', price = 138.17, pub_date = '2020-03-16', publish_id = 3)

models.AuthorDetail.objects.create(birthday = "1998-07-14", telephone = "13710398561", addr = "上海苏州")
models.AuthorDetail.objects.create(birthday = "2003-10-11", telephone = "17764251377", addr = "深圳")
models.AuthorDetail.objects.create(birthday = "1997-03-27", telephone = "15368482379", addr = "北京朝阳")
models.AuthorDetail.objects.create(birthday = "2002-11-21", telephone = "17622488326", addr = "杭州")
models.AuthorDetail.objects.create(birthday = "1998-05-16", telephone = "13642599728", addr = "成都")
models.AuthorDetail.objects.create(birthday = "2005-09-08", telephone = "15367536245", addr = "湖南长沙")

models.Author.objects.create(name = '小酒', age = 22, authordetail_id = 1)
models.Author.objects.create(name = '小美', age = 19, authordetail_id = 2)
models.Author.objects.create(name = '小花', age = 24, authordetail_id = 3)
models.Author.objects.create(name = '小爱', age = 20, authordetail_id = 4)
models.Author.objects.create(name = '小英', age = 23, authordetail_id = 5)
models.Author.objects.create(name = '小雨', age = 17, authordetail_id = 6)
一对多外键增删改查
方法一
publish_obj = models.Publish.objects.filter(nid = 1).first()
models.Book.objects.create(title='beego', price = 100.99, pub_date='2020-03-26', publish = publish_obj)

方法二
models.Book.objects.create(title = 'dubbo', price = 98.79, pub_date = '2015-12-16', publish_id = 3)

修改
models.Book.objects.filter(pk = 5).update(publish_id = 2)
publish_obj = models.Publish.objects.filter(pk = 1).first()
models.Book.objects.filter(pk = 5).update(publish = publish_obj)

删
models.Publish.objects.filter(pk = 2).delete()  # 级联删除
多对多增删改查
book_obj = models.Book.objects.filter(title = 'django').first()
xiaojiu = models.Author.objects.filter(nid = 1).first()
xiaoyu = models.Author.objects.filter(nid = 6).first()
book_obj.authors.add(xiaojiu, xiaoyu)   # 书籍name为django的书籍绑定一个主键为1和6的作者
book_obj.authors.add(2, 3)
"""
add 给第三张关系表添加数据,括号内既可以传数字也可以传对象 并且都支持多个
"""

查询主键为 4 的书籍的所有作者的名字
bookVal = models.Book.objects.filter(nid = 4).first()
rets = bookVal.authors.all().values('name')
print(rets)
&#x7ED3;&#x679C;: <queryset [{'name': '小酒'}, {'name': '小雨'}]>

&#x4E0E;&#x8FD9;&#x672C;&#x4E66;&#x5173;&#x8054;&#x7684;&#x6240;&#x6709;&#x4F5C;&#x8005;&#x5BF9;&#x8C61;&#x96C6;&#x5408; ---> QuerySet&#x5BF9;&#x8C61;&#x3002;[obj1, obj2,......]
bookAll = models.Book.objects.filter(nid = 4).first()
res = bookAll.authors.all()
print(res)

&#x4FEE;&#x6539;
book_obj = models.Book.objects.filter(nid = 4).first()
book_obj.authors.set([2, 3])  # &#x62EC;&#x53F7;&#x5185;&#x5FC5;&#x987B;&#x7ED9;&#x4E00;&#x4E2A;&#x53EF;&#x8FED;&#x4EE3;&#x5BF9;&#x8C61;
book_obj.authors.set([3])  # &#x62EC;&#x53F7;&#x5185;&#x5FC5;&#x987B;&#x7ED9;&#x4E00;&#x4E2A;&#x53EF;&#x8FED;&#x4EE3;&#x5BF9;&#x8C61;

author_obj = models.Author.objects.filter(pk = 4).first()
author_obj1 = models.Author.objects.filter(pk = 5).first()
book_obj.authors.set([author_obj, author_obj1])  # &#x62EC;&#x53F7;&#x5185;&#x5FC5;&#x987B;&#x7ED9;&#x4E00;&#x4E2A;&#x53EF;&#x8FED;&#x4EE3;&#x5BF9;&#x8C61;
"""
set &#x62EC;&#x53F7;&#x5185;&#x5FC5;&#x987B;&#x4F20;&#x4E00;&#x4E2A;&#x53EF;&#x8FED;&#x4EE3;&#x5BF9;&#x8C61;&#xFF0C;&#x8BE5;&#x5BF9;&#x8C61;&#x5185;&#x65E2;&#x53EF;&#x4EE5;&#x6570;&#x5B57;&#x4E5F;&#x53EF;&#x4EE5;&#x5BF9;&#x8C61; &#x5E76;&#x4E14;&#x90FD;&#x652F;&#x6301;&#x591A;&#x4E2A;
"""

&#x89E3;&#x9664;&#x591A;&#x5BF9;&#x591A;&#x5173;&#x7CFB;&#xFF0C;&#x6CE8;&#x610F;first&#x5F97;&#x52A0;
book_obj = models.Book.objects.filter(nid = 4).first()
&#x6CE8;&#x610F;&#x8FD9;&#x91CC;&#x7684; 4 &#x4EE3;&#x8868; author_id
book_obj.authors.remove(4)
&#x5728;&#x7B2C;&#x4E09;&#x5F20;&#x5173;&#x7CFB;&#x8868;&#x4E2D;&#x6E05;&#x7A7A;&#x67D0;&#x4E2A;&#x4E66;&#x7C4D;&#x4E0E;&#x4F5C;&#x8005;&#x7684;&#x7ED1;&#x5B9A;&#x5173;&#x7CFB;
book_obj.authors.clear()
"""
clear &#x62EC;&#x53F7;&#x5185;&#x4E0D;&#x8981;&#x52A0;&#x4EFB;&#x4F55;&#x53C2;&#x6570;
"""</queryset>

正反向的概念

&#x6B63;&#x5411;: &#x5916;&#x952E;&#x5B57;&#x6BB5;&#x5728;&#x6211;&#x624B;&#x4E0A;&#x90A3;&#x4E48;&#xFF0C;&#x6211;&#x67E5;&#x4F60;&#x5C31;&#x662F;&#x6B63;&#x5411;
&#x53CD;&#x5411;: &#x5916;&#x952E;&#x5B57;&#x6BB5;&#x5982;&#x679C;&#x4E0D;&#x5728;&#x624B;&#x4E0A;&#xFF0C;&#x6211;&#x67E5;&#x4F60;&#x5C31;&#x662F;&#x53CD;&#x5411;
book >>> &#x5916;&#x952E;&#x5B57;&#x6BB5;&#x5728;&#x4E66;&#x90A3;&#x513F;(&#x6B63;&#x5411;) >>> publish
publish >>> &#x5916;&#x952E;&#x5B57;&#x6BB5;&#x5728;&#x4E66;&#x90A3;&#x513F;(&#x53CD;&#x5411;) >>> book
&#x4E00;&#x5BF9;&#x4E00;&#x548C;&#x591A;&#x5BF9;&#x591A;&#x6B63;&#x53CD;&#x5411;&#x7684;&#x5224;&#x65AD;&#x4E5F;&#x662F;&#x5982;&#x6B64;

"""
&#x6B63;&#x5411;&#x67E5;&#x8BE2;&#x6309;&#x5B57;&#x6BB5;
&#x53CD;&#x5411;&#x67E5;&#x8BE2;&#x6309;&#x8868;&#x540D;&#x5C0F;&#x5199;
    &#x8868;&#x540D;&#x5C0F;&#x5199;_set
    ...

"""
1 &#x67E5;&#x8BE2;&#x4E66;&#x7C4D;&#x4E3B;&#x952E;&#x4E3A;1&#x7684;&#x51FA;&#x7248;&#x793E;
book_obj = models.Book.objects.filter(pk = 1).first()
&#x4E66;&#x67E5;&#x51FA;&#x7248;&#x793E; &#x6B63;&#x5411;
res = book_obj.publish
print(res)

2 &#x67E5;&#x8BE2;&#x4E66;&#x7C4D;&#x4E3B;&#x952E;&#x4E3A;2&#x7684;&#x4F5C;&#x8005; &#xFF08;&#x591A;&#x5BF9;&#x591A;&#xFF09;
book_obj = models.Book.objects.filter(pk = 1).first()
&#x4E66;&#x67E5;&#x4F5C;&#x8005; &#x6B63;&#x5411;
author = book_obj.authors
print(author) # app.Author.None
authorAll = book_obj.authors.all()  # <queryset [<author: author object>, <author: author object>]>
for obj in authorAll:
    print(obj.name, obj.authordetail.telephone)

3 &#x67E5;&#x8BE2;&#x4F5C;&#x8005; &#x5C0F;&#x7F8E;&#x4FE1;&#x606F;
author_obj = models.Author.objects.filter(name = '&#x5C0F;&#x7F8E;').first()
res = author_obj.authordetail
print(res)

4 &#x67E5;&#x8BE2;&#x51FA;&#x7248;&#x793E;&#x662F; &#x7ACB;&#x65B9;&#x51FA;&#x7248;&#x793E; &#x7684;&#x4E66;
publish_obj = models.Publish.objects.filter(name = '&#x7ACB;&#x65B9;&#x51FA;&#x7248;&#x793E;').first()
&#x51FA;&#x7248;&#x793E;&#x67E5;&#x4E66;  &#x53CD;&#x5411;
bookSet = publish_obj.book_set
bookAll = publish_obj.book_set.all()
print(bookSet) # &#x7ACB;&#x65B9;&#x51FA;&#x7248;&#x793E; app.Book.None
print(bookAll)

5 &#x67E5;&#x8BE2;&#x4F5C;&#x8005;&#x662F; &#x5C0F;&#x7231; &#x5199;&#x8FC7;&#x7684;&#x4E66;
author_obj = models.Author.objects.filter(name = '&#x5C0F;&#x7231;').first()
&#x4F5C;&#x8005;&#x67E5;&#x4E66;      &#x53CD;&#x5411;
bookSet = author_obj.book_set
bookAll = author_obj.book_set.all()
print(bookSet) # app.Book.None
print(bookAll)

6 &#x67E5;&#x8BE2;&#x624B;&#x673A;&#x53F7;&#x662F; 15368482379 &#x7684;&#x4F5C;&#x8005;&#x4FE1;&#x606F;
author_detail_obj = models.AuthorDetail.objects.filter(telephone = 15368482379).first()
print(author_detail_obj)
res = author_detail_obj.author
print(res)</author:></queryset>
1 &#x67E5;&#x8BE2; &#x5C0F;&#x9152; &#x7684;&#x624B;&#x673A;&#x53F7;
res = models.Author.objects.filter(name = '&#x5C0F;&#x9152;').values('authordetail__telephone')
print(res)
&#x53CD;&#x5411;
res = models.AuthorDetail.objects.filter(author__name = '&#x5C0F;&#x9152;')  # &#x62FF;&#x4F5C;&#x8005;&#x59D3;&#x540D;&#x662F; &#x5C0F;&#x9152; &#x7684;&#x4F5C;&#x8005;&#x8BE6;&#x60C5;
res = models.AuthorDetail.objects.filter(author__name = '&#x5C0F;&#x9152;').values('telephone', 'author__name')
print(res)

2 &#x67E5;&#x8BE2;&#x4E66;&#x7C4D;&#x4E3B;&#x952E;&#x4E3A;1&#x7684;&#x51FA;&#x7248;&#x793E;&#x540D;&#x79F0;&#x548C;&#x4E66;&#x7684;&#x540D;&#x79F0;
res = models.Book.objects.filter(pk = 1).values('title', 'publish__name')
print(res)
&#x53CD;&#x5411;
res = models.Publish.objects.filter(book__nid = 1).values('name', 'book__title')
print(res)

3 &#x67E5;&#x8BE2;&#x4E66;&#x7C4D;&#x4E3B;&#x952E;&#x4E3A;1&#x7684;&#x4F5C;&#x8005;&#x59D3;&#x540D;
res = models.Book.objects.filter(pk = 1).values('authors__name')
print(res)
&#x53CD;&#x5411;
res = models.Author.objects.filter(book__nid = 1).values('name')
print(res)

4 &#x67E5;&#x8BE2;&#x4E66;&#x7C4D;&#x4E3B;&#x952E;&#x662F;1&#x7684;&#x4F5C;&#x8005;&#x7684;&#x624B;&#x673A;&#x53F7;
book author authordetail
res = models.Book.objects.filter(pk = 1).values('authors__authordetail__telephone')
print(res)
&#x7EC3;&#x4E60;: &#x67E5;&#x8BE2; &#x7ACB;&#x65B9;&#x51FA;&#x7248;&#x793E; &#x8FC7;&#x7684;&#x6240;&#x6709;&#x4E66;&#x7C4D;&#x7684;&#x540D;&#x5B57;&#x4EE5;&#x53CA;&#x4F5C;&#x8005;&#x7684;&#x59D3;&#x540D;
&#x6B63;&#x5411;&#x67E5;&#x8BE2;
res = models.Book.objects.filter(publish__name = "&#x7ACB;&#x65B9;&#x51FA;&#x7248;&#x793E;").values_list("title", "authors__name")
print(res)

&#x53CD;&#x5411;&#x67E5;&#x8BE2;
res = models.Publish.objects.filter(name = "&#x7ACB;&#x65B9;&#x51FA;&#x7248;&#x793E;").values_list("book__title", "book__authors__name")
print(res)

&#x7EC3;&#x4E60;: &#x624B;&#x673A;&#x53F7;&#x4EE5; 153 &#x5F00;&#x5934;&#x7684;&#x4F5C;&#x8005;&#x51FA;&#x7248;&#x8FC7;&#x7684;&#x6240;&#x6709;&#x4E66;&#x7C4D;&#x540D;&#x79F0;&#x4EE5;&#x53CA;&#x51FA;&#x7248;&#x793E;&#x540D;&#x79F0;
res = models.Book.objects.filter(authors__authordetail__telephone__regex="153").values_list("title", "publish__name")
print(res)

res = models.Author.objects.filter(authordetail__telephone__startswith = "153").values("book__title", "book__publish__name")
print(res)

1 自行创建第三张表(全自动)

  • 优点:不需要你手动创建第三张表
  • 缺点:第三张表不是你手动创建的,字段是固定的无法拓展
class Book(models.Model):
    title = models.CharField(max_length = 32, verbose_name = "&#x4E66;&#x540D;")
    authors = models.ManyToManyField(to = "Author")

class Author(models.Model):
    name = models.CharField(max_length = 32, verbose_name = "&#x4F5C;&#x8005;&#x59D3;&#x540D;")

2 通过 ManyToManyField 自动创建第三张表(纯手动)了解即可

  • 优点:第三张表可以任意拓展字段
  • 缺点:orm 查询不方便
class Book(models.Model):
    title = models.CharField(max_length = 32, verbose_name = "&#x4E66;&#x540D;")

&#x901A;&#x8FC7; ORM &#x81EA;&#x5E26;&#x7684; ManyToManyField &#x81EA;&#x52A8;&#x521B;&#x5EFA;&#x7B2C;&#x4E09;&#x5F20;&#x8868;
class Author(models.Model):
    name = models.CharField(max_length = 32, verbose_name = "&#x4F5C;&#x8005;&#x59D3;&#x540D;")

class Author2Book(models.Model):
    author = models.ForeignKey(to = "Author", on_delete = models.CASCADE)
    book = models.ForeignKey(to = "Book", on_delete = models.CASCADE)

    class Meta:
        unique_together = ("author", "book")

3 设置 ManyToManyField 并指定自行创建的第三张表(半自动)推荐使用

  • 优点:结合自动和手动优点
  • 缺点:多对多字段不支持增删改查:add(), remove(), set(), clear()
class Book(models.Model):
    title = models.CharField(max_length = 32, verbose_name = "&#x4E66;&#x540D;")

&#x81EA;&#x5DF1;&#x521B;&#x5EFA;&#x7B2C;&#x4E09;&#x5F20;&#x8868;&#xFF0C;&#x5E76;&#x901A;&#x8FC7; ManyToManyField &#x6307;&#x5B9A;&#x5173;&#x8054;
class Author(models.Model):
    name = models.CharField(max_length = 32, verbose_name = "&#x4F5C;&#x8005;&#x59D3;&#x540D;")
    books = models.ManyToManyField(to = "Book", through = "Author2Book", through_fields = ("author", "book"))
    # through &#x662F;&#x8868;&#x540D;&#x79F0;
    # through_fields &#x63A5;&#x53D7;&#x4E00;&#x4E2A;2&#x5143;&#x7EC4; ('field1'&#xFF0C;'field2')
    # field1 &#x662F;&#x5B9A;&#x4E49;ManyToManyField&#x7684;&#x6A21;&#x578B;&#x5916;&#x952E;&#x7684;&#x540D;(author)
    # field2 &#x662F;&#x5173;&#x8054;&#x76EE;&#x6807;&#x6A21;&#x578B;&#xFF08;book&#xFF09;&#x7684;&#x5916;&#x952E;&#x540D;

class Author2Book(models.Model):
    author = models.ForeignKey(to = "Author", on_delete = models.CASCADE)
    book = models.ForeignKey(to = "Book", on_delete = models.CASCADE)

TypeError: init() missing 1 required positional argument: ‘on_delete’。

Original: https://www.cnblogs.com/huaxiayuyi/p/16694239.html
Author: 娇小赤雅
Title: Django ORM 实现数据的多表 增删改查

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/809229/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球