DjangoORM多表实例

Posted 礁之

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了DjangoORM多表实例相关的知识,希望对你有一定的参考价值。

文章目录


此文章参考菜鸟教程:Django ORM – 多表实例 | 菜鸟教程 (runoob.com)

Django版本:

>>> django.VERSION  
(4, 1, 0, 'final', 0)

PS:基于前几章的进度进行修改

一、Django ORM 多表实例

  • 表与表之间的关系可以分为以下三种:
  1. 一对一:一个人对应一个的身份证号,数据字段设置为unique
  2. 一对多:一个家庭有多个人,一般通过外键来实现
  3. 多对多:一个学生有多门课程,一个课程有很多学生,一般通过第三个表来实现关联
  • 下面是菜鸟的示例图

创建模型

  • 修改app1/app1_model/models.py
# -*- coding: utf-8 -*-
from django.db import models

class Book(models.Model):
    title = models.CharField(max_length=32)		
    price = models.DecimalField(max_digits=5,decimal_places=2)	
    pub_date = models.DateField()
    publish = models.ForeignKey("Publish",on_delete=models.CASCADE)  #多对一
    authors = models.ManyToManyField("Author")  #多对多

class Publish(models.Model):
    name = models.CharField(max_length=32)
    city = models.CharField(max_length=64)
    email = models.EmailField()

class Author(models.Model):
    name = models.CharField(max_length=32)
    age = models.SmallIntegerField()
    au_detail = models.OneToOneField("AuthorDetail",on_delete=models.CASCADE)  #一对一

class AuthorDetail(models.Model):
    gender_choices = (
        (0,"女"),
        (1,"男"),
        (2,"保密"),
    )
    gender = models.SmallIntegerField(choices=gender_choices)
    tel = models.CharField(max_length=32)
    addr = models.CharField(max_length=64)
    birthday = models.DateField()
  • 说明:
  1. EmailField数据类型是邮箱格式,底层继承CharField进行了封装,相当于mysql中的varchar
  2. 一般不需要级联更新
  3. 如果有模型类存在外键,在创建数据时,需要先创建外键关联的模型类数据,否则在创建包含外键的模型类数据时,外键的关联模型类数据会找不到
  • 设置一对一OneToOneField = ForeignKey("关联类名",unique=True)

  • 外键在一对多中设置,即models.ForeignKey("关联类名",on_delete=models.CASCADE),后面是设置外键删除时的操作:

  1. CASCADE:默认选项,表示级联删除
  2. PROTECT:保护模式,使用该选项,在删除时,会抛出ProtectedError的错误
  3. SET_NULL:置空模式,删除时,外键字段被设置为空,前提是blank=Truenull=True,定义该字段时,需要外键允许为空
  4. SET_DEFAULT:删除时,外键字段设置为默认值,使用该选项时,需要注意外键的上一个默认值
  5. SET( ):括号里可以是函数,设置自定义的值
  6. DO_NOTHING:什么也不做
  • 在终端创建数据表
(test) PS F:\\django\\app1> python .\\manage.py makemigrations app1_model 
Migrations for 'app1_model':
  app1_model\\migrations\\0002_authordetail_publish_alter_book_id_author_and_more.py
    - Create model AuthorDetail
    - Create model Publish
    - Alter field id on book
    - Create model Author
    - Add field authors to book
    - Alter field publish on book
    
    
(test) PS F:\\django\\app1> python .\\manage.py migrate app1_model 
Operations to perform:
  Apply all migrations: app1_model
Running migrations:
  Applying app1_model.0002_authordetail_publish_alter_book_id_author_and_more... OK
  • 查看mysql数据库,可以看到有一个app1_model_book_authors这个就是多对多的第三张表

插入数据

insert into app1_model_publish(name,city,email) values ("华山出版社","华山","hs@163.com"),("明教出版社","黑木崖","mj@163.com");

 
# 先插入 authordetail 表中多数据
insert into app1_model_authordetail(gender,tel,addr,birthday) values (1,13432335433,"华山","1994-5-23"),(1,13943454554,"黑木崖","1961-8-13"),(0,13878934322,"黑木崖","1996-5-20"); 


# 再将数据插入 author,这样 author 才能找到 authordetail 
insert into app1_model_author(name,age,au_detail_id) values ("令狐冲",25,1),("任我行",58,2),("任盈盈",23,3);
  • 执行

二、ORM 插入数据

一对多—外键 ForeignKey

方式一:传输对象的形式,返回值的数据类型是对象,即书籍对象

  • 具体步骤:
  1. 获取出版社对象
  2. 给书籍的出版社属性pulish传入出版社对象
- 修改app1/app1/views.py文件
# -*- coding: utf-8 -*-
from django.shortcuts import render,HttpResponse
from app1_model import models

def add_book(request):
    #books = models.Book.objects.create(title="Python",price=500,publish="Python出版社",pub_date="1970-12-10")  
    pub_obj = models.Publish.objects.filter(pk=1).first()
    book = models.Book.objects.create(title="Python教程",price=200,pub_date="2011-11-11",publish=pub_obj)
    print(book,type(book))
    return HttpResponse(book)
  • 访问127.0.0.1:8000/add_book

方式二:传入对象id的形式

  • 由于传过来的数据一般都是id,所以传入对象id是比较常用的
  • 在一对多中,设置外键属性的类中,即指的表,Mysql中显示的字段名称是外键属性名_id,返回值的数据类型是对象,即书籍对象
  • 步骤:
  1. 获取出版社对象的id
  2. 给书籍的关联出版社字段pulish_id传入出``版社对象的id`
- 修改app1/app1/views.py文件
# -*- coding: utf-8 -*-
from django.shortcuts import render,HttpResponse
from app1_model import models

def add_book(request):
    #books = models.Book.objects.create(title="Python",price=500,publish="Python出版社",pub_date="1970-12-10")  
    pub_obj = models.Publish.objects.filter(pk=1).first()
    pk = pub_obj.pk
    book = models.Book.objects.create(title="Django教程",price=100,pub_date="2012-12-12",publish_id=pk)
    print(book,type(book))
    return HttpResponse(book)
  • 访问测试

多对多(Many ToManyField):在第三张表添加数据

方式一:传入对象形式,没有返回值

  • 步骤:
  1. 获取作者对象
  2. 获取书籍对象
  3. 给书籍对象的authors属性用add的方法纯如作者对象
- 修改app1/app1/views.py文件
# -*- coding: utf-8 -*-
from django.shortcuts import render,HttpResponse
from app1_model import models

def add_book(request):
    #books = models.Book.objects.create(title="Python",price=500,publish="Python出版社",pub_date="1970-12-10")  
    chong = models.Author.objects.filter(name="令狐冲").first() 
    book = models.Book.objects.filter(title="Python教程").first()  
    book.authors.add(chong)
    return HttpResponse(book)
  • 访问127.0.0.1:8000/add_book进行测试

  • 查看数据库信息

方式二:传入对象id形式,没有返回值

  • 步骤:
  1. 获取作者对象的id
  2. 获取书籍对象
  3. 给书籍对象的authors属性使用add的方法传入作者的id
# -*- coding: utf-8 -*-
from django.shortcuts import render,HttpResponse
from app1_model import models

def add_book(request):
    #books = models.Book.objects.create(title="Python",price=500,publish="Python出版社",pub_date="1970-12-10")  
    #chong = models.Author.objects.filter(name="令狐冲").first()
    xing = models.Author.objects.filter(name="任我行").filter()
    for i in xing:
        print(i.pk)
        book = models.Book.objects.filter(title="Django教程").first()
        book.authors.add(i.pk)
    return HttpResponse(xing)
  • 访问127.0.0.1:8000/add_book进行测试

  • 查看数据库信息

三、关联管理器——对象调用

  • 前提:

多对多:需要双向均有关联管理器

一对多:只有多的那个类的对象需要关联管理器,也就是只有反向才有

注意:一对多只能反向

  • 语法格式:

正向:属性名称

反向:小写类名+_set

  • 常用方法:

1、add()

add()主要用于多对多,把指定的模型对象添加到关联对象集(对象表)中

注意:add()在一对多,即外键中,只能通过传入对象的方式不能通过传入id的方式

*[]的使用,下面是传入对象的方式

# -*- coding: utf-8 -*-
from django.shortcuts import render,HttpResponse
from app1_model import models

def add_book(request):
    #books = models.Book.objects.create(title="Python",price=500,publish="Python出版社",pub_date="1970-12-10")  
    book_obj = models.Book.objects.get(id=2)
    author_list = models.Author.objects.filter(id__gt=2)
    book_obj.authors.add(*author_list)  #将id大于2的作者对象添加到这本书的作者集合中
    return HttpResponse("ok")
  • 访问测试

  • 查看数据库信息,可以发现id为2的书籍,作者对象的id都是大于2的,即id为2和3的作者

  • 传入对象id的方式
# -*- coding: utf-8 -*-
from django.shortcuts import render,HttpResponse
from app1_model import models

def add_book(request):
    #books = models.Book.objects.create(title="Python",price=500,publish="Python出版社",pub_date="1970-12-10")  
    book_obj = models.Book.objects.get(id=2)
    #author_list = models.Author.objects.filter(id__gt=2)
    book_obj.authors.add(*[1,3])  #将id为1和3的作者添加到这本书作者的集合中
    return HttpResponse("ok")
  • 访问测试

  • 查看数据库信息,可以发现id为2的书籍,3个作者都已经添加到了集合中

  • 下面是反向,反向应该是小写表面_set
# -*- coding: utf-8 -*-
from django.shortcuts import render,HttpResponse
from app1_model import models

def add_book(request):
    #books = models.Book.objects.create(title="Python",price=500,publish="Python出版社",pub_date="1970-12-10")  
    ying = models.Author.objects.filter(name="任盈盈").first()
    book = models.Book.objects.filter(title="Python教程").first()
    ying.book_set.add(book)
    return HttpResponse("ok")
  • 访问测试

  • 查看数据库信息,可以看到Python教程(id=1)数据的作者添加了任盈盈(id=3)

2、create()

  • 创建一个新的对象,并且同时将它添加到关联对象集之中,返回新创建的对象
# -*- coding: utf-8 -*-
from django.shortcuts import render,HttpResponse
from app1_model import models

def add_book(request):
    #books = models.Book.objects.create(title="Python",price=500,publish="Python出版社",pub_date="1970-12-10")  
    pub = models.Publish.objects.filter(name="明教出版社").first()
    wo = models.Author.objects.filter(name="任我行").first()
    book = wo.book_set.create(title="ORM教程",price=150,pub_date="2012-10-10",publish=pub)
    print(book,type(book))
    return HttpResponse("ok")
  • 访问测试

  • 查看数据库信息


3、remove()

  • 从关联对象集中移除执行的对象,对于ForeignKey对象,这个方法尽在null=True即可以为空时存在,没有返回值
  • 先来看一下数据库信息,确定好要删除的行

  • 例如要删除书籍2和作者2的行
# -*- coding: utf-8 -*-
from django.shortcuts import render,HttpResponse
from app1_model import models

def add_book(request):
    #books = models.Book.objects.create(title="Python",price=500,publish="Python出版社",pub_date="1970-12-10")  
    author_obj = models.Author.objects.get(id=2)
    book_obj = models.Book.objects.get(id=2)
    author_obj.book_set.remove(book_obj)
    return HttpResponse("ok")
  • 访问测试

  • 查看数据库信息,可以看到已经删除

4、clear()

  • 从关联对象集合中移除所有对象,删除关联,不会删除对象。对于ForeignKey对象,这个方法仅在null=True即可以为空时存在,没有返回值
# -*- coding: utf-8 -*-
from django.shortcuts import render,HttpResponse
from app1_model import models

def add_book(request):
    #books = models.Book.objects.create(title="Python",price=500,publish="Python出版社",pub_date="1970-12-10")  
    book = models.Book.objects.filter(title="Django教程").first()   
    book.authors.clear()    #删除django教程的所有关联
    return HttpResponse("ok")
  • 访问测试

  • 查看数据库信息,可以看到Book表中的Django教程行还是存在的,但是关联表book_authors中的Django教程(id=2)的行已经被删除

四、ORM查询

  • 基于对象的跨表查询

  • 语法:

正向:属性名称

反向:小写类名_set

1、一对多

  • 查询主键为3的书籍出版社所在的城市,即正向查询
# -*- coding: utf-8 -*-
from django.shortcuts import render,HttpResponse
from app1_model import models

def add_book(request):
    #books = models.Book.objects.create(title="Python",price=500,publish="Python出版社",pub_date="1970-12-10")  
    book = models.Book.objects.filter(pk=3).first()
    res = book.publish.city
    print(res,type(res))
    return HttpResponse("ok")
  • 访问测试

  • 查看终端输出和数据库信息,可以看到是相同的

  • 查询华山出版社出版的书籍名称,即反向
  • 反向的写法:对象.小写类名_set(pub.book_set)可以跳转到关联的表,即书籍表
  • pub.book_set.all():取出书籍表中的所有书籍对象,在一个QuerySet对象中,遍历取出书籍对象
# -*- coding: utf-8 -*-
from django.shortcuts import render,HttpResponse
from app1_model import models

def add_book(request):
    #books = models.Book.objects.create(title="Python",price=500,publish="Python出版社",pub_date="1970-12-10")  
    pub = models.Publish.objects.filter(name="华山出版社").first()
    res = pub.book_set.all()
    for i in res:
        print(i.title)
    return HttpResponse("ok")
  • 访问测试

  • 查看终端输出和数据库信息


2、一对一

  • 正向:对象.属性(author.au_detail)可以跳转到关联的表
  • 下面查询令狐冲的电话
# -*- coding: utf-8 -*-
from django.shortcuts import render,HttpResponse
from app1_model import models

def add_book(request):
    #books = models.Book.objects.create(title="Python",price=500,publish="Python出版社",pub_date="1970-12-10")  
    author = models.Author.objects.filter(name="令狐冲").first()
    res = author.au_detail.tel
    print(res,type(res))
    return HttpResponse("ok")
  • 访问测试

  • 查看终端输出和数据库信息

  • 查询到所有住址在黑木崖的作者姓名,即反向,一对一的反向,使用对象.小写类名即可,不用加_set
  • 反向:对象.小写类名(addr.author)可以跳转到关联的表
# -*- coding: utf-8 -*-
from django.shortcuts import render,HttpResponse
from app1_model import models

def add_book(request):
    #books = models.Book.objects.create(title="Python",price=500,publish="Python出版社",pub_date="1970-12-10")  
    addr = models.AuthorDetail.objects.filter(addr="黑木崖").first()  
    res = addr.author.name
    print(res,type(res))
    return HttpResponse("ok")
  • 访问测试

  • 查看终端输出和数据库信息,由于上面查询时使用了first()方法,所以只输出了一个作者名称

3、多对多

  • 正向:对象.属性(book.authors)可以跳转到关联的表

  • 下面寻找Python教程的所有作者以及手机号,即正向,由于作者表里并没有手机号,所以还需要通过对象.属性(i.au_detail)跳转到关联的表

# -*- coding: utf-8 -*-
from django.shortcuts import render,HttpResponse
from app1_model import models

def add_book(request):
    #books = models.Book.objects.create(title="Python",price=500,publish="Python出版社",pub_date="1970-12-10")  
    book = models.Book.objects.filter(title="Python教程").first()
    res = book.authors.all()
    for i in res:
        print(i.name,i.au_detail.tel)
    return HttpResponse("ok")
  • 访问测试

  • 查看终端输出和数据库信息

  • 下面查询任我行写的所有书籍的名称,即反向
# -*- coding: utf-8 -*-
from django.shortcuts import render,HttpResponse
from app1_model import models

def add_book(request):
    #books = models.Book.objects.create(title="Python",price=500,publish="Python出版社",pub_date="1970-12-10")  
    author = models.Author.objects

以上是关于DjangoORM多表实例的主要内容,如果未能解决你的问题,请参考以下文章

DjangoORM多表实例

薅羊毛啦! 价值$1000动态规划答疑讲堂, 令狐冲老师为您划重点!

1$答疑讲堂 | 令狐冲现场剖析如何学好动态规划,为你解答疑惑

令狐冲老师的动态规划总结

漫画 | 面向对象之《葵花宝典》,欲练神功,必先......

多表连接(从两个表取数,放在一个表里)