本章节将介绍ORM中的关联模型,以下测试基于book管理
一、创建关联模型
创建app01并注册
编辑全局settings.py文件,如下:
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'app01.apps.App01Config', ###添加app01的配置
]
配置数据库连接
编辑全局settings.py文件,如下:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'NAME': 'Django',
'USER': 'root',
'PASSWORD': '123456',
'HOST': 'locahost',
'PORT': '3306',
}
}
创建关联模型
编辑app01下的models.py文件,如下:
from django.db import models
class Book(models.Model):
title = models.CharField(max_length=32)
price = models.DecimalField(max_digits=8, decimal_places=2) # 999999.99,浮点型8位数,小数点2位
publishDate = models.DateField()
# 一对多的关联属性,创建关联字段
publish = models.ForeignKey("Publish", on_delete=models.CASCADE) ###表中会添加一个publish_id字段,并设置级联删除
# 多对多的关联属性,创建关系表
authors = models.ManyToManyField("Author", db_table="book2author") # 多对多的时候,一般通过第三方表来实现,如果不命名的话,会自动创建一个book_authors的表
class Publish(models.Model):
name = models.CharField(max_length=32)
addr = models.CharField(max_length=32)
email = models.CharField(max_length=32)
class Author(models.Model):
name = models.CharField(max_length=32)
age = models.SmallIntegerField()
###设置一对一的关联属性
ad = models.OneToOneField("AuthorDetail", on_delete=models.CASCADE) ##添加ForeignKey时需设置级联删除。
class AuthorDetail(models.Model):
tel = models.IntegerField()
addr = models.CharField(max_length=32)
具体解释如下:
- 一对多
在一对多的关系中,需要使用models.ForeignKey在表示“多”的模型中定义一个外键字段,定义时需要指定表示“一”的模型类名。models.ForeignKey常用参数:
- to:该属性的第一个参数,表示外键指向的模型类的类名
- on_delete:表示外键指向的数据不存在时,本条数据该如何处理。
- CASCADE:级联操作,如果外键对应的在另一张表中的数据被删除了,那么本条数据也会被跟着删除。
- PROTECT:外键数据受保护,即只要本条数据还存在,则外键对应的在另一张表中的数据是无法被删除的。
- SET_NULL:删除时设置为空,如果外键对应的在另一张表中的数据被删除了,那么本条数据中的这个外键值就设置为空。
- SET_DEFUALT:设置为默认值,如果外键对应的在另一张表中的数据被删除了,那么本条数据中的这个外键值就设置为指定的默认值,但前提是此字段有指定的默认值。
- SET(obj/func):设置默认值,如果外键对应的在另一张表中的数据被删除了,那么本条数据中的这个外键值就设置为指定的值obj或者指定函数或方法func的返回值。
- DO_NOTHING:不做任何操作,一切只看数据库本身的约束。
- related_name: 一般用于反向查询,表示“多”的模型中定义外键后,Django会在表示“一”的模型中自动生成一个表示“多”的模型类名_set属性,如果不想使用这种默认的命名方式,就可以使用此参数指定自己需要的名称了。 - db_constraint:默认为True,指有外键约束,如果只想设置关联数据,取消外键约束,设置为Flase即可。
参考related.py文件,如下定义的ForeignKey方法:
def __init__(self, to, related_name=None, related_query_name=None,
limit_choices_to=None, symmetrical=None, through=None,
through_fields=None, db_constraint=True, db_table=None,
swappable=True, **kwargs):
try:
to._meta
except AttributeError:
assert isinstance(to, str), (
"%s(%r) is invalid. First parameter to ManyToManyField must be "
"either a model, a model name, or the string %r" %
(self.__class__.__name__, to, RECURSIVE_RELATIONSHIP_CONSTANT)
)
- 多对多
使用models.ManyToManyField指定即可(定义多对多关系时,Django会在数据库中自动生成一个关联两张表的中间表,但是在Django中不用我们自己去定义),同样的,也会在另一个模型中自动生成一个形如类名_set的属性。
- 一对一
使用models.OneToOneField指定即可,定义方法和参数使用与models.ForeignKey类似(因为models.OneToOneField就是通过models.ForeignKey来实现的,只不过是添加了一个“唯一”的约束),不同之处在于,在另一个外键对应的模型中自动生成的属性为类名的小写形式,当然,你不想用默认生成的属性,也可以通过related_name参数来指定。
添加数据
在models.py文件中添加如下内容:
AuthorDetail.objects.create(tel=122121212,addr="北京")
AuthorDetail.objects.create(tel=212121212,addr="南京")
AuthorDetail.objects.create(tel=313131313,addr="上海")
Publish.objects.create(name="北京出版社",addr="昌平",email="122122")
Publish.objects.create(name="南京出版社",addr="中山路",email="212122")
Publish.objects.create(name="上海出版社",addr="浦东",email="313313133")
执行数据库迁移
python manage.py makemigrations ###生成迁移文件
python manage.py migrate ###同步数据库
二、关联模型操作
一对多增删改查
- 创建路由
编辑全局配置文件urls.py文件,如下:
from django.contrib import admin
from django.urls import path
from app01 import views as app1
urlpatterns = [
path('admin/', admin.site.urls),
path('index',app1.addbook),
]
- 增加
from django.shortcuts import render,HttpResponse,redirect
from app01.models import Book,Publish,Author,AuthorDetail
def addbook(request):
##向book表中添加一条数据
##获取出版社名称
publish = Publish.objects.get(name="北京出版社")
##方法一
book = Book.objects.create(title="西游记",price=100,publishDate="2022-12-2",publish=publish)
###方法二
book = Book.objects.create(title="红楼梦", price=200, publishDate="2021-11-2", publish_id=5)
book = Book.objects.create(title="水浒传", price=180, publishDate="2020-10-2", publish_id=6)
###打印相关内容
print(book1.title) ###book对象中title已经复制到内存中
print(book1.publish) ###打印结果为一个对象
print(book1.publish.name) ###Django关联模型中会利用publish_id获得publish(get方式)的模型类对象
print(book1.publish_id) ##得到的就是当前book记录的publish_id,
return HttpResponse("成功")
运行django程序,界面登录,查看数据库添加成功
- 删除
from django.shortcuts import render,HttpResponse,redirect
from app01.models import Book,Publish,Author,AuthorDetail
def addbook(request):
##将book表中删除一条数据
Book.objects.filter(title="西游记").delete() ##直接删除即可
return HttpResponse("成功")
- 更新
from django.shortcuts import render,HttpResponse,redirect
from app01.models import Book,Publish,Author,AuthorDetail
def addbook(request):
##更新表中的数据
Book.objects.filter(title="红楼梦").update(name="三国演义") ##直接更新
return HttpResponse("成功")
- 查看
from django.shortcuts import render,HttpResponse,redirect
from app01.models import Book,Publish,Author,AuthorDetail
def addbook(request):
##查看表中的数据
book = Book.objects.filter(name="三国演义") ##和之前讲的一样,就不再次赘述
print(book)
return HttpResponse("成功")
多对多增删改查
- 增加
from django.shortcuts import render,HttpResponse,redirect
from app01.models import Book,Publish,Author,AuthorDetail
def addbook(request):
##为book书籍添加作者
##方法一
##获取book信息
book = Book.objects.get(title="水浒传")
author1 = Author.objects.get("小华")
author2 = Author.objects.get(name="小明")
book.authors.add(author1, author2)
##方法二
book = Book.objects.get(title="水浒传")
book.authors.add(1, 2) ###可以直接跟authorID
##方法三
book = Book.objects.get(title="水浒传")
author_list = [1,2] ###将作者定义为列表
book.authors.add(*author_list) ##将列表中的数据传进去
return HttpResponse("成功")
运行django程序,查看第三张关联表,如下:
- 删除
from django.shortcuts import render,HttpResponse,redirect
from app01.models import Book,Publish,Author,AuthorDetail
def addbook(request):
###删除书的作者
book = Book.objects.get(title="水浒传")
book.authors.remove(2) ###删除指定作者对象
####
book.authors.clear() ###删除该book下关联的所有作者对象
return HttpResponse("成功")
- 修改
from django.shortcuts import render,HttpResponse,redirect
from app01.models import Book,Publish,Author,AuthorDetail
def addbook(request):
###更新书的作者
book = Book.objects.get(title="水浒传")
book.authors.set([6,]) ###先clear删除后add添加
return HttpResponse("成功")
运行django程序,查看结果
也可以看到sql的运行过程,是先根据bookID
从app01_author
表中查出来该book对应的author的信息,在进行delete
删除,之后进行insert
添加,如下:
- 查看
from django.shortcuts import render,HttpResponse,redirect
from app01.models import Book,Publish,Author,AuthorDetail
def addbook(request):
###查询书的作者
book = Book.objects.get(title="水浒传")
author_list = book.authors.all().values("name","age")
print(author_list)
return HttpResponse("成功")
运行django程序,结果如下:
一对一增删改查
此处不多讲,只演示如何添加数据
from django.shortcuts import render,HttpResponse,redirect
from app01.models import Book,Publish,Author,AuthorDetail
def addbook(request):
##添加一条作者信息
author = Author.objects.create(name="小华",age=40,ad_id=3)
return HttpResponse("成功")
反向查询
前面介绍的都是基于book查看author,下面来介绍如何基于author查看book
上面说到创建多对多关联模型的时候,添加了related_name="books"参数,后面的名字可以随意定义
具体步骤如下:
from django.shortcuts import render,HttpResponse,redirect
from app01.models import Book,Publish,Author,AuthorDetail
def addbook(request):
###反向查询
###通过作者查出对应的书籍
authtor = Author.objects.get(pk=6) ###获取author的信息
print(authtor.books.all().values("title")) ###通过author调用books对象获取book信息
return HttpResponse("成功")
大致原理如下:
1: 通过定义的related_name得知是和哪张表是多对多的关系
2: 然后通过第三张关联表的authorID获取所有的book信息
3:通过获取的book对象来查找具体的哪个属性的信息
运行django程序,结果如下:
查看数据库中的数据,结果一致,如下: