Web开发基础之Django三
一,ORM概念
1,ORM介绍
对象关系映射 用于实现面向对象编程语言里不同类型系统数据之间的转换。
其就是使用面向对象的方式,操作数据库进行表管理和增删改查操作等。
2,ORM的作用和优点
Django中的对ORM进行封装,对接数据库引擎,执行对应的SQL语句。
使用者无需关心数据库引擎具体实现的软件(Mysql,sqlite......)。重点把握业务逻辑实现即可。之后如果数据库软件更换需要迁移,直接修改相关配置即可。
优点:
ORM使操作数据库变的更为简单、快捷。无需直接编写SQL语句,快速开发。
二,ORM建表
1,映射关系
在Django框架中,通过ORM模型进行数据库的管理和操作。
通过Python代码,Django的ORM模型将代码转为SQL语句,操作数据库。
以下为对应关系:
类名 ==== 表名
属性 ==== 字段
类实例对象 ==== 表记录
2,创建模型(表定义)
创建一个app03应用。模型代码的学习
python manage.py startapp app03
创建应用出错
ModuleNotFoundError: No module named 'app03'
排错:之前创建过该应用的路由,在主路由urlr.py内删除对应项目的路由即可
对应的setting也需要删除
创建应用后查看文件夹
设置路由
主路由
app03路由
setting添加对应项目
创建一张表,只需在应用下的model.py中定义对应的类模型即可。继承models.Model。
常用的模型字段有哪些: CharField -> 字符串类型 BooleanField -> 布尔类型 IntegerField -> 整数类型 DateField / DateTimeField -> 时间类型 EmailField -> Email类型 TextField -> 文本类型 更多: https://docs.djangoproject.com/en/1.11/ref/models/fields/#field-types https://docs.djangoproject.com/en/1.11/ref/models/fields/#django.db.models.CharField
示例:编写一个类模型
from django.db import models # Create your models here. class Host(models.Model): hostname = models.CharField() ip = models.CharField() type = models.CharField() status = models.CharField()
3,字段属性
字段属性,用来限制字段的最大长度,Null值,默认值,主键,唯一性,备注等信息
(1)max_length CharField需要max_length参数来指定VARCHAR数据库字段的大小。 (2)null 如果为True,Django 将用NULL来在数据库中存储空值。 默认值是 False。 (3)default 字段的默认值。可以是一个值或者可调用对象。如果可调用 ,每有新对象被创建它都会被调用。 (4)primary_key 如果为True,那么这个字段就是模型的主键。如果你没有指定任何一个字段的primary_key=True,Django 就会自动添加一个IntegerField字段做为主键,所以除非你想覆盖默认的主键,否则没必要设置任何一个字段的primary_key=True。 (5)unique 如果该值设置为 True, 这个数据字段的值在整张表中必须是唯一的。 (6)verbose_name 备注的名字,可读更好。 (7)choices 由元祖组成的一个可迭代对象(例如,列表或元组),用来给字段提供选择项。 如果设置了choices ,默认的表单将是一个选择框而不是标准的文本框,而且这个选择框的选项就是choices 中的选项。 举一个例子:我们在使用探测主机功能的时候,我们会记录成功或者失败,这个时候我们就可以使用choices。
修改models.py
from django.db import models # Create your models here. Types = ( (1, 'web服务器'), (2, '数据库主服务器'), (3, '负载均衡服务器'), (4, '数据库从服务器'), (5, '数据库中间件服务器'), (6, '缓存中间件服务器'), (7, '日志分析服务器'), ) Status = ( (0, '下线'), (1, '在线') ) class Host(models.Model): hostname = models.CharField(max_length=50, verbose_name='主机名称') ip = models.CharField(max_length=15, verbose_name='IP地址') type = models.IntegerField(choices=Types, verbose_name='服务器类型') status = models.IntegerField(choices=Status, verbose_name='服务器状态')
4,模型原数据
模型元数据是“任何不是字段的数据”,比如排序选项(ordering),数据库表名(db_table)。在模型中添加class Meta是完全可选的,所有选项都不是必须的。
Meta选项参数
(1)db_table 指定自定义数据库表名的。Django有一套默认的按照一定规则生成数据模型对应的数据库表名,如果你想使用自定义的表名,就通过这个属性指定。 若不提供该参数, Django 会使用 app_label + '_' + module_name 作为表的名字。当你通过db_table覆写表名称时,强烈推荐使用小写字母给表命名 (2)ordering 这个字段是告诉Django模型对象返回的记录结果集是按照哪个字段排序的 例如: class Meta: ordering = ['- name'] 它是一个字符串的列表或元组。每个字符串是一个字段名,前面带有可选的“-”前缀表示倒序。前面没有“-”的字段表示正序。 (3)verbose_name 可读性更高的名字。可以认为是一个表名注释。
5,迁移数据
创建和迁移数据库表,一般执行两个步骤
①makegrations 根据类模型 创建迁移文件
②migrate 通过迁移文件,进行数据表的创建和修改操作(连接数据库,管理表)
注意:
当模型初次创建和修改都需要执行
①进入操作命令行
②执行makegrations创建迁移文件
d:\web\devops\mydjango>python manage.py makemigrations app03 Migrations for 'app03': app03\migrations\0001_initial.py - Create model Host
创建的文件在以下目录
如果遇到以下问题
需要在settings.py 配置
③执行migrate迁移命令
d:\web\devops\mydjango>python manage.py migrate app03 Operations to perform: Apply all migrations: app03 Running migrations: Applying app03.0001_initial... OK
以上操作就完成了Django中ORM模型。数据表的创建和修改已经完成。
问:Django连接数据库和数据表在哪里?表创建到哪儿了?
答:默认Django使用的sqlite数据库,sqlite是一个小型数据库,一个文件。
打开sqlite需要安装database控件
安装控件参考:https://www.cnblogs.com/minseo/p/14109410.html
使用database控件打开文件
查看表
插入数据
修改Mate重新做一次数据迁移
d:\web\devops\mydjango>python manage.py makemigrations app03 Migrations for 'app03': app03\migrations\0002_auto_20201209_1648.py - Change Meta options on host - Rename table for host to host
查看生成的新文件
迁移
D:\web\devops\mydjango>python manage.py migrate app03 Operations to perform: Apply all migrations: app03 Running migrations: Applying app03.0002_auto_20201209_1648... OK
注意:如果已经使用database连接需要断开连接否则会出现lock错误提示
查看是否修改
6,更换数据库
在Django框架中,默认提供了sqllite数据库,进行开发和调试。
实际业务环境中,可以选择为业务数据库,比如说MySQL
Windows安装MySQL(安装过程不详述)
配置Django使用MySQL数据库
修改setting.py配置
DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': '', #你的数据库名称 数据库需要自己提前建好 'USER': '', #你的数据库用户名 'PASSWORD': '', #你的数据库密码 'HOST': '', #你的数据库主机,留空默认为localhost 'PORT': '3306', #你的数据库端口 'OPTIONS': { "init_command": "SET sql_mode='STRICT_TRANS_TABLES'", } } }
本次配置如下
DATABASES = { # 'default': { # 'ENGINE': 'django.db.backends.sqlite3', # 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), # } 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': 'mydjango', 'USER': 'root', 'PASSWORD': 'root', 'HOST': '127.0.0.1', 'PORT': '3306', 'OPTIONS': {'init_command': "SET sql_mode='STRICT_TRANS_TABLES'", } } }
②使用MySQL创建库
create database mydjango default character set utf8 collate utf8_general_ci;
③执行数据库迁移操作
python manage.py makemigrations app03 python manage.py migrate app03
报错
安装pymsql包
pip install pymysql
在__init__.py初始化导入
import pymysql pymysql.install_as_MySQLdb()
再次执行数据库迁移
执行成功 进入数据库查看
三,ORM操作
1,添加数据
Django 使用一种直观的方式把数据库表中的数据表示成Python 对象:一个模型类代表数据库中的一个表,一个模型类的实例代表这个数据库表中的一条特定的记录。
使用关键字参数实例化模型实例来创建一个对象,然后调用save() 把它保存到数据库中。
也可以使用一条语句创建并保存一个对象,使用create()方法
定义url路由
在视图中导入模型类
①save()方法
class AddView(View): def get(self, request): # 方法一 start obj = Host(hostname='server01', ip='192.168.17.100', type=1, status=0) obj.save() return HttpResponse('添加')
使用浏览器访问
查询数据库可以看到数据已经添加进去了
方法二
obj = Host() obj.hostname = 'server02' obj.ip = '192.168.17.101' obj.type = 2 obj.status = 1 obj.save() return HttpResponse('添加')
②create()方法
方法三
Host.objects.create(hostname='server03', ip='192.168.17.102', type=1, status=1) return HttpResponse('添加')
create可以传入关键字参数**kwargs
方法四
data = {'hostname': 'server04', 'ip': '192.168.17.102', 'type': 1, 'status': 1} Host.objects.create(**data) return HttpResponse('添加')
四种方法代码
class AddView(View): def get(self, request): # 方法一 start # obj = Host(hostname='server01', ip='192.168.17.100', type=1, status=0) # obj.save() # return HttpResponse('添加') # 方法一 end # 方法二 start # obj = Host() # obj.hostname = 'server02' # obj.ip = '192.168.17.101'ZQ # obj.type = 2 # obj.status = 1 # obj.save() # return HttpResponse('添加') # 方法二 end # 方法三 start # Host.objects.create(hostname='server03', ip='192.168.17.102', type=1, status=1) # return HttpResponse('添加') # 方法三 end # 方法四 start # data = {'hostname': 'server04', 'ip': '192.168.17.102', 'type': 1, 'status': 1} # Host.objects.create(**data) # return HttpResponse('添加') # 方法四 end
2,查询数据
定义路由
①all方法
本次只有一条数据
打印对象及对应的属性值
PS:模型类中定义返回的字段内容
②get方法
只取出一条数据,返回的是一个数据对象,如果查询的数据有多条或者没有都会抛出异常。
如果数据如下
例如查询status值为1的数据有两条则会报错抛出异常
查询status值为2则匹配不到则报以下异常
注意:使用get方法只有在匹配到一条数据的时候才能正常,当匹配不到或者匹配到多条数据则会报错
使用get方法获取数据无法使用for循环遍历,只能通过属性值(例如 data.hostname)打印出对应的属性
③filter方法
返回的是一个Queryset,即使只查出一条数据,也是一个Queryset。查询的数据没有也是一个空的Queryset。Queryset其实跟列表类似,列表的切片Queryset也可以
查询无匹配不抛出异常返回一个空列表
④exclude方法
排除方法,把满足条件的排除匹配其他的
⑤values方法
select * from host select hostname,ip from host
values方法提供了查询需要选的字段筛选
如果需要查询多个字段可以使用以下方法
也可以先筛选然后再使用values方法
注意:get方法查询的是单个对象,无法遍历也无法把get出来的对象再次使用values方法查询
3,更新数据
定义路由
①操作对象更新数据
class UpdateView(View): def get(self, request): # 操作对象方式更新 data = Host.objects.get(hostname='server01') data.hostname = 'Server01' data.save() return HttpResponse('更新')
以上为使用get查询数据,get只有在查询到唯一数据才不会报错,在查询到多条或者没有查询到均报错,下面演示使用filter更新数据
class UpdateView(View): def get(self, request): data = Host.objects.filter(status=1) for one in data: one.status = 0 one.save() return HttpResponse('更新')
使用fiter查询到的是一个对象,需要遍历,如果没有匹配则不进行更新,如果匹配到1个会进行更新,如果匹配到多个则多个更新
②使用update方法更新数据
class UpdateView(View): def get(self, request): # update方式更新 Host.objects.filter(hostname='server02').update(hostname='Server02') return HttpResponse('更新')
同理方法2匹配到多个则多个更新,使用get查询的数据无法使用update方法因为使用get获取到的是对象本身,只能使用方法1进行更新
4,删除数据
定义路由
定义视图
使用get查询删除数据和使用filter查询删除数据一致
class DeleteView(View): def get(self, request): Host.objects.get(id=5).delete() return HttpResponse('删除')
5,ORM内置查询方法
使用__关键查询字段 双下划线
contains “模糊”大小写敏感的包含指定字符串 icontains “模糊”大小写不敏感的包含指定字符串 startswith, endswith 以指字字符串开头或结尾 in 在给定的列表内 gt 大于 gte 大于或等于 lt 小于 lte 小于或等于 range 在指定范围内
示例
注意:不能使用>或者< 符号
内置方法也可用于get查询
四,ORM关联关系
一对一:一个运动员对应一个号码
多对一(一对多):一个代表对可以被多个运动员申报
多对多:多个运动员可以报多个项目
一对多 就是一个对多个的关系。
比如:一本书只能对应一个出版社,而一个出版社可以出版多本书,这样就是一对多的关系。
一对多的关系,主要是通过主键和外键进行关联的。
在Django的ORM模型中,使用ForeignKey定义一对多的关系。
①创建模型一对多关系
class Book(models.Model): name = models.CharField(max_length=32, verbose_name='图书名称') # max_digits=4数中允许的最大数目的数字 decimal_places=2精确到小数点后两位 price = models.DecimalField(max_digits=5, decimal_places=2, verbose_name='价格') # 使用外键方式增加对应关系 关联表为publish 该字段名称会被命名为publish_id publish = models.ForeignKey('Publish') # 元数据定义 class Meta: # pass # 自定义表名称 db_table = 'book' # 注释 verbose_name = '图书表' class Publish(models.Model): name = models.CharField(max_length=32, verbose_name='出版社名称') address = models.CharField(max_length=200, verbose_name='出版社地址') # 元数据定义 class Meta: # pass # 自定义表名称 db_table = 'publish' # 注释 verbose_name = '出版社表'
同步生成数据表写入MySQL
python manage.py makemigrations app03 python manage.py migrate app03
登录MySQL查看表结构
②多对一关系操作
添加数据 注意需要先添加一的关系,才能够添加多的关系。也就是此例中,先添加出版社,才可以添加图书。顺序和关联的先后,需要处理清楚。
先添加出版社信息
创建路由
创建视图
先创建视图写入出版社信息
class ManyToOneView(View): def get(self, request): # pass # 先添加一的关系即先有出版社才可以出版图书 Publish.objects.create(name='清华大学出版社', address='北京') Publish.objects.create(name='东北大学出版社', address='沈阳') return HttpResponse('多对一测试操作')
web浏览
查看MySQL数据库已经写入这两条数据
再添加图书信息
注意:需要把添加出版社信息注释 否则会重复添加
class ManyToOneView(View): def get(self, request): # pass # 先添加一的关系即先有出版社才可以出版图书 # Publish.objects.create(name='清华大学出版社', address='北京') # Publish.objects.create(name='东北大学出版社', address='沈阳') # return HttpResponse('多对一测试操作') # 添加图书信息 Book.objects.create(name='Linux高级运维详解', price='89.60', publish=Publish.objects.get(name='清华大学出版社')) Book.objects.create(name='DevOps', price='64.50', publish=Publish.objects.get(name='东北大学出版社')) return HttpResponse('添加图书')
注意:本示例直接把查询出来的出版社对象赋值给图书对象对应的出版社字段publish
下面两端代码意思是一致的,只不过第一条代码更加易读
Book.objects.create(name='Linux高级运维详解', price='89.60', publish=Publish.objects.get(name='清华大学出版社'))
Book.objects.create(name='Linux高级运维详解', price='89.60', publish_id=1)
以下代码虽然比较长但是更加清晰
# 实例化书籍对象 book = Book() # 赋值书名和价格 book.name = 'DevOps' book.price = 64.5 # 查询出版社对象 publish = Publish.objects.get(name='清华大学出版社') print(publish, publish.id) # 找出出版社id并赋值给书籍对象 book.publish_id = publish.id book.save() return HttpResponse('一对多操作')
简化代码直接把书籍对象赋值给书籍的关键字publish
book = Book() book.name = 'DevOps' book.price = 64.5 publish = Publish.objects.get(name='清华大学出版社') book.publish = publish book.save() return HttpResponse('一对多操作')
使用get查询方法如果查询不到会报错,优化代码使用try
book = Book() book.name = 'DevOps' book.price = 64.5 try: publish = Publish.objects.get(name='北京大学出版社') book.publish = publish book.save() except Exception as e: print(e) return HttpResponse('一对多操作')
如果对应的出版社信息不存在则把错误打印出来
因为出版社是唯一的所以本列最好使用get方法结合try,当然也可以使用filter查询出出版社信息然后遍历出出版社对象赋值给书籍对象
book = Book() book.name = 'DevOps' book.price = 64.5 publish = Publish.objects.filter(name='清华大学出版社') for one in publish: book.publish = one book.save() return HttpResponse('一对多操作')
如果使用filter方法查询的出版社不存在则因为publish对象为空则for语句不执行,不会报错,也不会往数据库添加书籍信息
book = Book() book.name = 'DevOps' book.price = 64.5 publish = Publish.objects.filter(name='北京大学出版社') print(publish) # 如果有出版社信息则执行操作 # 否则打印出版社不存在信息 if publish: for one in publish: print(one) book.publish_id = one.id book.save() else: print('出版社不存在') return HttpResponse('一对多操作')
MySQL查询
查询数据 通过图书名称查询属于哪个出版社(正向查询)
查询解析
data = Book.objects.filter(name__startswith="Linux")
查询以Linux开头的图书,data数据为一个可迭代的对象(可以为空,可以为1个或多个本次查询包含1个)
修改代码一步步打印对应的对象
data = Book.objects.filter(name__startswith="Linux") print('查询到的书的可迭代对象', data) print('书的第一个迭代对象加参数publish的到的是出版社对象', data[0].publish) print('出版社对象加参数name的到出版社名称', data[0].publish.name) for one in data: print(one, one.publish.name) return HttpResponse('查询')
输出如下
以上查询如果在MySQL使用语句查询可以使用以下步骤查询
①内联查询出所有数据
mysql> select * from book inner join publish on book.publish_id=publish.id; +----+-------------------------+-------+------------+----+-----------------------+---------+ | id | name | price | publish_id | id | name | address | +----+-------------------------+-------+------------+----+-----------------------+---------+ | 1 | Linux高级运维详解 | 89.60 | 1 | 1 | 清华大学出版社 | 北京 | | 2 | DevOps | 64.50 | 2 | 2 | 东北大学出版社 | 沈阳 | | 3 | Python高级开发 | 66.66 | 1 | 1 | 清华大学出版社 | 北京 | +----+-------------------------+-------+------------+----+-----------------------+---------+
内联表book和publish 条件为book.publish_id=publish.id即图书对应的出版社id与出版社id一一对应查询出所有内联数据,本次一共有3条
②设置条件从内联数据查询出需要的数据
本次设置条件为以Linux开头的图书
mysql> select * from book inner join publish on book.publish_id=publish.id where book.name like 'Linux%'; +----+-------------------------+-------+------------+----+-----------------------+---------+ | id | name | price | publish_id | id | name | address | +----+-------------------------+-------+------------+----+-----------------------+---------+ | 1 | Linux高级运维详解 | 89.60 | 1 | 1 | 清华大学出版社 | 北京 | +----+-------------------------+-------+------------+----+-----------------------+---------+
③只取出出版社信息
把显示数据的*号改成出版社信息
mysql> select publish.name from book inner join publish on book.publish_id=publish.id where book.name like 'Linux%'; +-----------------------+ | name | +-----------------------+ | 清华大学出版社 | +-----------------------+
查询清华大学出版社出版了那几本书(反向查询)
先再添加一个出版社和图书
MySQL查看
通过出版社名称清华大学出版社查询出版了多少书
以上查询使用MySQL语句实现
①内联查询出所有数据
内联条件为出版社publish的id对应图书book的publish_id
mysql> select * from publish inner join book on publish.id=book.publish_id; +----+-----------------------+---------+----+-------------------------+-------+------------+ | id | name | address | id | name | price | publish_id | +----+-----------------------+---------+----+-------------------------+-------+------------+ | 1 | 清华大学出版社 | 北京 | 1 | Linux高级运维详解 | 89.60 | 1 | | 2 | 东北大学出版社 | 沈阳 | 2 | DevOps | 64.50 | 2 | | 1 | 清华大学出版社 | 北京 | 3 | Python高级开发 | 66.66 | 1 | +----+-----------------------+---------+----+-------------------------+-------+------------+
②设置条件从内联查询的数据中过滤出想要查询的内容
本次的过滤条件是publish.name等于清华大学出版社
mysql> select * from publish inner join book on publish.id=book.publish_id where publish.name='清华大学出版社'; +----+-----------------------+---------+----+-------------------------+-------+------------+ | id | name | address | id | name | price | publish_id | +----+-----------------------+---------+----+-------------------------+-------+------------+ | 1 | 清华大学出版社 | 北京 | 1 | Linux高级运维详解 | 89.60 | 1 | | 1 | 清华大学出版社 | 北京 | 3 | Python高级开发 | 66.66 | 1 | +----+-----------------------+---------+----+-------------------------+-------+------------+
③再次细化只取出图书信息
mysql> select book.name from publish inner join book on publish.id=book.publish_id where publish.name='清华大学出版社'; +-------------------------+ | name | +-------------------------+ | Linux高级运维详解 | | Python高级开发 | +-------------------------+
删除数据 删除出版社
访问web页面删除
查看MySQL数据
MySQL级联关系如果主表的数据删除了 对应了表数据也会删除
2,多对多
比如一本书可以多个作者合作编写,一个作者也可以编写多本书,这样就是多对多的关系
再Django模型中,使用ManyToManyField定义一对多的关系
恢复原数据表 drop book表和publish表然后重新进行数据迁移
删除以下文件
重新数据迁移
添加出版社和图书信息
作者和图书多对多关系图
修改模板models.py
完整代码
from django.db import models # Create your models here. Types = ( (1, 'web服务器'), (2, '数据库主服务器'), (3, '负载均衡服务器'), (4, '数据库从服务器'), (5, '数据库中间件服务器'), (6, '缓存中间件服务器'), (7, '日志分析服务器'), ) Status = ( (0, '下线'), (1, '在线') ) class Host(models.Model): hostname = models.CharField(max_length=50, verbose_name='主机名称') ip = models.CharField(max_length=15, verbose_name='IP地址') type = models.IntegerField(choices=Types, verbose_name='服务器类型') status = models.IntegerField(choices=Status, verbose_name='服务器状态') # 元数据定义 class Meta: # pass # # 不定义默认 为app03_host db_table = 'host' # # 返回数据结果集 采用hostname倒序方式排序 # # select * from hostlist oreder by hostname desc # ordering = ['-hostname'] # # 表名称注释 verbose_name = '服务器主机资源信息管理表' # 定义返回字段 # def __str__(self): # return self.hostname class Book(models.Model): name = models.CharField(max_length=32, verbose_name='图书名称') # max_digits=4数中允许的最大数目的数字 decimal_places=2精确到小数点后两位 price = models.DecimalField(max_digits=5, decimal_places=2, verbose_name='价格') # 使用外键方式增加对应关系 关联表为publish 该字段名称会被命名为publish_id publish = models.ForeignKey('Publish') author = models.ManyToManyField('Author') # 元数据定义 class Meta: # pass # 自定义表名称 db_table = 'book' # 注释 verbose_name = '图书表' class Publish(models.Model): name = models.CharField(max_length=32, verbose_name='出版社名称') address = models.CharField(max_length=200, verbose_name='出版社地址') # 元数据定义 class Meta: # pass # 自定义表名称 db_table = 'publish' # 注释 verbose_name = '出版社表' class Author(models.Model): name = models.CharField(max_length=50, verbose_name='作者姓名') job = models.CharField(max_length=50, verbose_name='公种') # 元数据定义 class Meta: # pass # 自定义表名称 db_table = 'author' # 注释 verbose_name = '作者表'
迁移数据
查看MySQL
查看建表语句 该表有两个外键 book表结构没有变化
添加数据
首先添加几个作者信息
①路由
②视图
class ManyToManyView(View): def get(self, request): Author.objects.create(name='张晓明', job='coder') Author.objects.create(name='李晓彤', job='SA') Author.objects.create(name='张大龙', job='app') return HttpResponse('添加作者')
访问页面添加3个作者
MySQL数据库查看
导出MySQL的binlog日志可以看到插入的语句
MySQL数据库查看
设置书籍的作者
例如书籍《 Linux高级运维详解》有两个作者 作者1张晓明 作者id为1 作者2 李晓彤作者id为2
添加对应关系
修改视图
class ManyToManyView(View): def get(self, request): # Author.objects.create(name='张晓明', job='coder') # Author.objects.create(name='李晓彤', job='SA') # Author.objects.create(name='张大龙', job='app') # return HttpResponse('添加作者') # 查询对应图书返回图书对象 book = Book.objects.get(name='Linux高级运维详解') # 查询对应作者返回作者对象 author1 = Author.objects.get(name='张晓明') author2 = Author.objects.get(name='李晓彤') print(book, author1, author2) # 把图书添加对应作者 book.author.add(author1, author2) return HttpResponse('添加图书与作者对应关系')
访问页面添加
MySQL的binlog日志显示执行如下,即相当于给对应书籍添加了两个作者
MySQL数据查询
相当于书籍id为1的书有两个作者 一个作者在作者表author内的id为1 一个作者在表author内的id为2
查询
查询书籍《Linux高级运维详解》的作者是谁
class ShowView(View): def get(self, request): data = Book.objects.filter(name='Linux高级运维详解') print(data, data.values('name', 'author__name')) for one in data: print(one, one.publish.name, one.author.values('name')) return HttpResponse('查询')
查询结果
解析:多对多查询出书籍对象 然后使用values方法使用关键字author__name即可查询到该书籍对应的作者
注意:这里author__name是两个下划线
便利书籍对象使用author.values方法关键字是name也可以查询到该书籍的对应作者
使用MySQL查询语句查询书籍作者
表之间关系如下
例如查询书籍《Linux高级运维详解》的作者有谁
①查询表book内联book_author
条件是book_author的书籍id book_id与书籍表book的id相同
mysql> select * from book inner join book_author on book.id=book_author.book_id; +----+-------------------------+-------+------------+----+---------+-----------+ | id | name | price | publish_id | id | book_id | author_id | +----+-------------------------+-------+------------+----+---------+-----------+ | 1 | Linux高级运维详解 | 89.60 | 1 | 1 | 1 | 1 | | 1 | Linux高级运维详解 | 89.60 | 1 | 2 | 1 | 2 | +----+-------------------------+-------+------------+----+---------+-----------+
②再次内联表作者表author
条件是book_author的作者id author_id与作者表author的id相同
mysql> select * from book inner join book_author on book.id=book_author.book_id inner join author on author.id=book_author.author_id; +----+-------------------------+-------+------------+----+---------+-----------+----+-----------+-------+ | id | name | price | publish_id | id | book_id | author_id | id | name | job | +----+-------------------------+-------+------------+----+---------+-----------+----+-----------+-------+ | 1 | Linux高级运维详解 | 89.60 | 1 | 1 | 1 | 1 | 1 | 张晓明 | coder | | 1 | Linux高级运维详解 | 89.60 | 1 | 2 | 1 | 2 | 2 | 李晓彤 | SA | +----+-------------------------+-------+------------+----+---------+-----------+----+-----------+-------+
③细化输出只输出作者信息
mysql> select author.name from book inner join book_author on book.id=book_author.book_id inner join author on author.id=book_author.author_id; +-----------+ | name | +-----------+ | 张晓明 | | 李晓彤 | +-----------+
查询
通过作者查询该作者出版的那些书
视图
class ShowView(View): def get(self, request): author = Author.objects.filter(name='张晓明') for one in author: print(one.book_set.values('name')) return HttpResponse('查询')
查询结果
通过MySQL查询语句查询
mysql> select book.name from author inner join book_author on author.id=book_author.author_id inner join book on book.id=book_author.book_id where author.name="张晓明"; +-------------------------+ | name | +-------------------------+ | Linux高级运维详解 | +-------------------------+
解析:联合3个表,最后指定作者条件过滤出来该作者出版的书籍
本次示例对应的图书表关系图示如下