- ubuntu12.04环境下使用kvm ioctl接口实现最简单的虚拟机
- Ubuntu 通过无线网络安装Ubuntu Server启动系统后连接无线网络的方法
- 在Ubuntu上搭建网桥的方法
- ubuntu 虚拟机上网方式及相关配置详解
CFSDN坚持开源创造价值,我们致力于搭建一个资源共享平台,让每一个IT人在这里找到属于你的精彩世界.
这篇CFSDN的博客文章Django模型层实现多表关系创建和多表操作由作者收集整理,如果你对这篇文章有兴趣,记得点赞哟.
继续上面一篇文章的内容,本文介绍多表操作。使用django ORM可以创建多表关系,并且也支持多张表之间的操作,以创建表关系和查询两部分说明django ORM的多表操作。以作者、图书、出版社和作者信息几张表作为案例进行说明.
注意:在实际开发中不推荐使用外键建立表关系即不使用级联更新和级联删除,而是推荐使用逻辑上的外键关系建立表关系.
上述的四张表中,图书和出版社这两表的关系属于一对多的关系,外键建立在查询频率高的一方。作者和作者详情表属于一对一关系,外键建立在查询频率高的一方,作者和图书属于多对多关系,需要第三张表存储关系,建议将外键建在查询频率高的一方。创建表时一定要执行数据库迁移命令哦~ 。
创建表关系时可以先将表模型创建出来,然后再添加外键字段,另外在使用django ORM创建外键关系时,关联的外键字段会自动在字段后加_id,表与表之间的关系默认以主键作为关联字段。另外在创建表关系时,不建议使用实质的外键进行关联,而是通过使用逻辑上的关系来指定表关系.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
|
class
Book(models.Model):
name
=
models.CharField(max_length
=
60
, verbose_name
=
'图书名'
)
# 浮点数字段,max_digits表示数字共8位, decimal_place表示小数点占2位
price
=
models.DecimalField(max_digits
=
8
, decimal_places
=
2
, verbose_name
=
'图书价格'
)
inventory_num
=
models.IntegerField(verbose_name
=
'库存数量'
)
sell_num
=
models.IntegerField(verbose_name
=
'卖出数量'
)
# 一对多关系外键字段创建使用 ForeigenKey(to='表名'),默认关联主键字段, db_constraint=Flase表示不建立实质的外键关系,只是逻辑上的关系
publish
=
models.ForeignKey(to
=
'Publish'
, on_delete
=
models.DO_NOTHING, db_constraint
=
False
, verbose_name
=
'外键关联出版社'
)
# 多对多关系,使用ManyToManyField(to='表名'),author是一个虚拟的字段,主要是用来告诉ORM,书籍和作者是多对多的关系,而且ORM会自动创建多对多关系的第三张
author
=
models.ManyToManyField(to
=
'Author'
, on_delete
=
models.DO_NOTHING, db_constraint
=
False
, verbose_name
=
'外键关联作者'
)
class
Publish(models.Model):
name
=
models.CharField(max_length
=
12
, verbose_name
=
'出版社名称'
)
class
Author(models.Model):
name
=
models.CharField(max_length
=
10
, verbose_name
=
'作者名称'
)
# 一对一关系使用OneToOneField(to='表名')
author_detail
=
models.OneToOneField(to
=
'AuthorDetail'
, on_delete
=
models.DO_NOTHING, db_constraint
=
False
, verbose_name
=
'外间关联作者详情'
)
class
AuthorDetail(models.Model):
age
=
models.IntegerField(verbose_name
=
'年龄'
)
phone
=
models.CharField(max_length
=
11
, verbose_name
=
'手机号'
)
|
另外还需补充一点,多对多的表关系共有三种创建方式,分别是全自动创建、半自动创建和全手动创建:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
|
# 全自动创建 - ManyToManyField,一般这种方式可以满足需求
'''
使用全自动创建多对多关系的优点就是无需手动创建第三张表,非常方便,django ORM直接提供操作第三张表关系的方法
缺点就是无法扩展第三张关系表
'''
class
Book(models.Model):
name
=
models.CharField(max_length
=
32
)
authors
=
models.ManyToManyField(to
=
'Author'
)
class
Author(models.Model):
name
=
models.CharField(max_length
=
32
)
# 纯手动创建 - ForeignKey手动创建第三张表
'''
第三张表完全取决于手动的扩展,但是需要写的代码较多,而且无法使用ORM提供的简单方法
'''
class
Book(models.Model):
name
=
models.CharField(max_length
=
32
)
class
Author(models.Model):
name
=
models.CharField(max_length
=
32
)
class
Book2Author(models.Model):
book_id
=
models.ForeignKey(to
=
'Book'
)
author_id
=
models.ForeignKey(to
=
'Author'
)
# 半自动创建,通过ManyToManyField的参数控制第三张表
class
Book(models.Model):
name
=
models.CharField(max_length
=
32
)
authors
=
models.ManyToManyField(
to
=
'Author'
,
# 告诉ORM不需要自动帮忙创建第三张关系表
through
=
'Book2Author'
,
# 告诉ORM第三张关系表对应的表的外键字段
through_fields
=
(
'book'
,
'author'
)
# 通过哪两个字段关联表,当前在哪张表,就先写哪个表的关联字段
)
class
Author(models.Model):
name
=
models.CharField(max_length
=
32
)
class
Book2Author(models.Model):
book
=
models.ForeignKey(to
=
'Book'
)
author
=
models.ForeignKey(to
=
'Author'
)
|
首先介绍多表操作的增删改操作,因为多表的查询数据操作稍微麻烦一点,单独另外开小灶.
一对一和一对多的增删改操作基本是一致的.
增加数据有两种方式,一种方式是通过实际字段来添加,另一种方式是通过虚拟字段对象赋值添加.
1
2
3
4
5
6
|
# 方式1:通过实际字段
book_obj
=
models.Book.objects.create(name
=
'哈利波特'
, price
=
10.2
, publish_id
=
1
)
# 方式2,先获取出版社对象,再将书籍和出版社通过出版社对象进行关联
publis_obj
=
models.Publish.objects.
filter
(pk
=
1
).first()
book_obj
=
models.Book.objects.create(name
=
'哈利波特'
, price
=
10.2
, publish
=
publis_obj)
|
需要说明一点,在实际项目开发中删除数据并不是真的删除了,而是使用一个布尔类型的字段标识该数据是否删除。 删除数据的时候如果不指定on_delete=models.DO_NOTHING默认是级联更新级联删除的.
1
|
models.Publish.objects.
filter
(pk
=
1
).delete()
|
修改数据同增加数据一样有两种方式.
1
2
3
4
5
6
|
# 方式1
models.Book.objects.
filter
(pk
=
1
).update(publish_id
=
1
)
# 方式2
pub_obj
=
models.Publish.objects.
filter
(pk
=
2
).first()
models.Book.objects.
filter
(pk
=
1
).update(publish
=
pub_obj)
|
首先需要明确的是,多对多的增删改是在操作第三张关系表,但是第三张关系表是django自动创建的,如何通过代码进入第三张表呢?多对多的外键关系被建在book表,多对多的外键字段是author字段,因此通过book_obj.author即可操作第三张表了。 对于django自动创建的第三张表的多对多关系,django提供了额外的方法对数据进行操作.
add()方法给第三张关系表添加数据,括号内既可以传数字也可以传对象,并且都支持多个同时操作.
1
2
3
4
5
6
7
8
9
10
11
12
|
# 方式1,直接添加id值
book_obj
=
models.Book.objects.
filter
(pk
=
1
).first()
book_obj.author.add(
1
)
# 增加 1 1记录,即id为1的书绑定id为1的作者
book_obj.author.add(
2
,
3
)
# 增加两条记录,1 2 和 1 3
# 方式2,通过对象添加关系
book_obj
=
models.Book.objects.
filter
(pk
=
2
).first()
author_obj1
=
models.Author.objects.
filter
(pk
=
1
).first()
author_obj2
=
models.Author.objects.
filter
(pk
=
2
).first()
author_obj3
=
models.Author.objects.
filter
(pk
=
3
).first()
book_obj.author.add(author_obj1)
# 增加1条记录
book_obj.author.add(author_obj2, author_obj3)
# 增加2条
|
remove()方法用来为第三张表删除数据,同样的,括号内既可以传数字也可以传对象,并且支持多条数据同时操作.
1
2
3
4
5
6
7
8
9
|
# 方式1:直接删除值
book_obj
=
models.Book.objects.
filter
(pk
=
1
).first()
book_obj.author.remove(
2
)
# 删除book_id为1和author_id都为2的记录
book_obj.authors.remove(
1
,
3
)
# 删除多条
# 方式2:通过对象删除
author_obj1
=
models.Author.objects.
filter
(pk
=
2
).first()
author_obj2
=
models.Author.objects.
filter
(pk
=
3
).first()
book_obj.authors.remove(author_obj1, author_obj2)
|
set()方法用来修改第三张表,该方法是一个覆盖操作,用新的关系覆盖之前的关系,该方法的参数必须是一个列表或者元组,指出数字或对象,也支持多条数据同时操作.
1
2
3
4
5
6
7
8
9
|
# 方式1:直接通过值进行修改
book_obj
=
models.Book.objects.
filter
(pk
=
1
).first()
book_obj.author.
set
([
2
])
# 将book_id为1对应的author_id修改为2
book_obj.authors.
set
([
1
,
2
])
# 将书的作者设置为id=1 和id=2的作者
# 方式2:通过对象进行修改
author_obj2
=
models.Author.objects.
filter
(pk
=
2
).first()
author_obj3
=
models.Author.objects.
filter
(pk
=
3
).first()
book_obj.authors.
set
([author_obj2, author_obj3])
|
clear()方法会清空第三张关系表中某个对象的绑定关系.
1
2
|
book_obj
=
models.Book.objects.
filter
(pk
=
1
).first()
book_obj.author.clear()
|
在进行多表查询操作前,需要了解一个概念,什么是正向查询和反向查询.
如果查询比较复杂时可以采用子查询的方式,子查询就是分步骤查询的意思,先查询得到的结果作为后查询的条件.
正向查询按字段,如果有多个结果需要外键字段.all(),那么怎么判断查询的结果有多个呢?如果在不加.all()的情况下得到的结果是应用名.模型名.None比如first.Author.None这种情况下说明ORM 语句没有错误,只是查询到的结果有多个,就需要加.all()。当查询结果只有一个时得到的是一个模型对象,如果为多个就是QuerySet对象.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
# 一对多关系查询:查询书籍主键为1的书籍由哪个出版社出版
book_obj
=
models.Book.objects.
filter
(pk
=
1
).first()
res
=
book_obj.publish
# Publish object (1)
print
(res.name)
# 多对多关系查询:查询数据主键为1的作者
book_obj
=
models.Book.objects.
filter
(pk
=
1
).first()
res
=
book_obj.author
# first.Author.None,说明结果有多个
res_many
=
book_obj.author.
all
()
# <QuerySet [<Author: Author object (1)>, <Author: Author object (2)>]>
# 一对一关系查询:查询作者lili的年龄
author_obj
=
models.Author.objects.
filter
(name
=
'lili'
).first()
res
=
author_obj.author_detail
print
(res.phone)
|
反向查询如果是一对一的话是表名小写,如果是一对多或者多对多时反向查询是表明小写——set,另外如果有多个结果需要在表明小写后再加_set.all(),判断结果是否有多个的方法与正向查询相同。当查询结果只有一个时得到的是一个模型对象,如果为多个就是QuerySet对象.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
# 一对多关系查询,查询出版社是东方出版社出版的书
publish_obj
=
models.Publish.objects.
filter
(name
=
'东方'
).first()
res
=
publish_obj.book_set.
all
()
# <QuerySet [<Book: Book object (1)>, <Book: Book object (2)>]>
# 多对多关系查询,查询作者是lili写过的书
author_obj
=
models.Author.objects.
filter
(name
=
'lili'
).first()
res
=
author_obj.book_set
# first.Book.None,说明有多个结果
res_many
=
author_obj.book_set.
all
()
print
(res_many)
# <QuerySet [<Book: Book object (1)>]>
# 一对一关系查询,查询手机号是119的作者
author_detail_obj
=
models.AuthorDetail.objects.
filter
(phone
=
'119'
).first()
res
=
author_detail_obj.author
print
(res.name)
|
联表查询就是像MySQ里面SQL语句的联表查询一样,只不过在django的ORM里面使用基于双下划线联表查询(跨表查询)。联表查询的可以使用一行代码查询结果,联表查询也遵循正反向关系.
1
2
3
4
5
6
7
8
9
10
|
# 一对多关系查询:查询书籍主键为1的出版社名称和书名
# 首先获取书籍对象,书籍是查询publish的基表,因此获取书名直接values('names')即可,而出版社的名字是通过外键字段跳到出版社的表中农,需要通过__找到需要的字段值
res
=
models.Book.objects.
filter
(pk
=
1
).values(
'name'
,
'publish__name'
)
# <QuerySet [{'name': '哈利波特', 'publish__name': '东方'}]>
# 多对多关系查询,查询书籍主键为1的作者姓名
res
=
models.Book.objects.
filter
(pk
=
1
).values(
'author__name'
)
# <QuerySet [{'author__name': 'lili'}, {'author__name': 'nana'}]>
# 一对一关系查询,查询lili的手机号和姓名
res
=
models.Author.objects.
filter
(name
=
'lili'
).values(
'name'
,
'author_detail__phone'
).first()
print
(res.get(
'name'
), res.get(
'author_detail__phone'
))
|
1
2
3
4
5
6
7
8
9
10
11
|
# 一对多关系查询:查询数据主键为1的出版社名称和书的名字
res
=
models.Publish.objects.
filter
(book__id
=
1
).values(
'name'
,
'book__name'
)
# <QuerySet [{'name': '东方', 'book__name': '哈利波特'}]>
# 多对多关系查询:查询书籍主键为1的作者姓名和书名
res
=
models.Author.objects.
filter
(book__id
=
1
).values(
'name'
,
'book__name'
)
# <QuerySet [{'name': 'lili', 'book__name': '哈利波特'}, {'name': 'nana', 'book__name': '哈利波特'}]>
# 一对一关系查询:查询作者id是1作者的姓名和手机号
res
=
models.AuthorDetail.objects.
filter
(author__id
=
1
).values(
'author__name'
,
'phone'
)
# <QuerySet [{'author__name': 'lili', 'phone': '119'}]>
# 综合大查询:查询书籍主键是1的作者的手机号,首先获取书籍对象,书籍关联了作者表,作者表又关联了作者详情表
res
=
models.Book.objects.
filter
(pk
=
1
).values(
'author__author_detail__phone'
)
# <QuerySet [{'author__author_detail__phone': '119'}, {'author__author_detail__phone': '120'}]>
|
聚合查询通常情况下是配合分组一起使用的,聚合查询就是用一些统计工具,比如最大值,最小值,平均值等,聚合函数的导入方式from django.db.models import Max, Min, Sum, Count, Avg,如果在不分组的情况下使用聚合函数需要在aggregate()方法内使用.
1
2
3
4
5
6
7
|
from
django.db.models
import
Min
,
Max
,
Sum
,Count,Avg
# 统计书的平均价格
res
=
models.Book.objects.aggregate(Avg(
'price'
))
print
(res)
# 可以将这些聚合函数同时使用
res
=
models.Book.objects.aggregate(
Max
(
'price'
),
Sum
(
'price'
),Count(
'pk'
))
print
(res)
|
聚合函数通常和分组一起使用,分组查询的方法是annotate,默认以models.分组依据作为分组依据,即表的主键进行分组,如果annotate()方法前面出现了values()那么就会按照values中指定的值进行分组。分组查询支持__跨表查询.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
from
django.db.models
import
Sum
,
Max
,
Min
, Avg, Count
# 1.统计每本书的作者个数
res
=
models.Book.objects.annotate(author_num
=
Count(
'author'
)).values(
'name'
,
'author_num'
)
# author_num是自己定义的字段用来存储统计出来的每本书对应的作者个数,暂时存为表中的字段
# 2.统计每个出版社卖的最便宜的书的价格
res
=
models.Publish.objects.annotate(min_price
=
Min
(
'book__price'
)).values(
'name'
,
'min_price'
)
# 3.统计不止一个作者的图书
# 先按照图书分组,算出每本书的作者数量,再过滤出作者数量大于1的数据
res
=
models.Book.objects.annotate(author_num
=
Count(
'author'
)).
filter
(author_num__gt
=
1
).values(
'name'
,
'author_num'
)
# 4.查询每个作者出版书的总价格
res
=
models.Author.objects.annotate(sum_price
=
Sum
(
'book__price'
)).values(
'name'
,
'sum_price'
)
|
F查询可以获得表中某个字段的数据值,尤其适合表中两个字段之间的比较运算,在操作字符类型的数据时,F不能直接做字符串的拼接,需要借助Concat和Value.
1
2
3
4
5
6
7
|
from
django.db.models
import
F
# 1.查询卖出数量大于库存数量的书籍
res
=
models.Book.objects.
filter
(sell_num__gt
=
F(
'inventory_num'
))
# 将所有书籍的价格提升20元
res
=
models.Book.objects.update(price
=
F(
'price'
)
+
20
)
|
F查询对于字符串的操作需要借助Concat和Value两个方法:
1
2
3
4
5
|
# 将所有书的名称后面加上爆款两个字
from
django.db.models.functions
import
Concat
from
django.db.models
import
F, Value
models.Book.objects.update(name
=
Concat(F(
'name'
),Value(
'爆款'
)))
|
使用filter()进行条件过滤时,采用的是逻辑与and的操作,如果想要将多个筛选条件更改为or或者not的关系则需要借助Q查询。在Q查询中|表示or的关系,~表示not的关系.
1
2
3
4
|
import
django.db.models
import
Q
# 查询卖出数量大于100或者价格小于20的书籍
res
=
models.Book.objects.
filter
(~Q(sell_num__gt
=
100
) | Q(price__lt
=
20
))
|
另外Q查询还有另一个比较高级的用法,就是可以将查询条件的左边也变成字符串的形式.
1
2
3
4
5
6
7
8
9
|
# 先产生一个Q的实例
q
=
Q()
# 修改q的连接条件的关系
q.connector
=
'or'
q.children.append((
'sell_num__gt'
,
100
))
q.children.append((
'price__lt'
,
200
))
res
=
models.Book.objects.
filter
(q)
# filter的条件是Q实例化产生的对象,每个条件默认还是and关系,可以修改
print
(res)
|
MySQL为了保证数据的安全有一个事务的机制,django既然能够连接MySQL那么django就可以支持MySQL的事务机制。下述代码就是在django中开启事务:
1
2
3
4
5
6
7
8
|
from
django.db
import
transaction
try
:
with transaction.atomic():
# 在with代码快内书写的所有orm操作都属于同一个事务
...
except
Exception as e:
print
(r)
...
|
到此这篇关于Django模型层实现多表关系创建和多表操作的文章就介绍到这了,更多相关Django 多表操作内容请搜索我以前的文章或继续浏览下面的相关文章希望大家以后多多支持我! 。
原文链接:https://juejin.cn/post/6985408898433810446 。
最后此篇关于Django模型层实现多表关系创建和多表操作的文章就讲到这里了,如果你想了解更多关于Django模型层实现多表关系创建和多表操作的内容请搜索CFSDN的文章或继续浏览相关文章,希望大家以后支持我的博客! 。
前言: 有时候,一个数据库有多个帐号,包括数据库管理员,开发人员,运维支撑人员等,可能有很多帐号都有比较大的权限,例如DDL操作权限(创建,修改,删除存储过程,创建,修改,删除表等),账户多了,管理
所以我用 Create React App 创建并设置了一个大型 React 应用程序。最近我们开始使用 Storybook 来处理和创建组件。它很棒。但是,当我们尝试运行或构建应用程序时,我们不断遇
遵循我正在创建的控件的代码片段。这个控件用在不同的地方,变量也不同。 我正在尝试编写指令来清理代码,但在 {{}} 附近插入值时出现解析错误。 刚接触 Angular ,无法确定我错过了什么。请帮忙。
我正在尝试创建一个 image/jpeg jax-rs 提供程序类,它为我的基于 post rest 的 Web 服务创建一个图像。我无法制定请求来测试以下内容,最简单的测试方法是什么? @POST
我一直在 Windows 10 的模拟器中练习 c。后来我改用dev C++ IDE。当我在 C 中使用 FILE 时。创建的文件的名称为 test.txt ,而我给出了其他名称。请帮助解决它。 下面
当我们创建自定义 View 时,我们将 View 文件的所有者设置为自定义类,并使用 initWithFrame 或 initWithCode 对其进行实例化。 当我们创建 customUITable
我正在尝试为函数 * Producer 创建一个线程,但用于创建线程的行显示错误。我为这句话加了星标,但我无法弄清楚它出了什么问题...... #include #include #include
今天在做项目时,遇到了需要创建JavaScript对象的情况。所以Bing了一篇老外写的关于3种创建JavaScript对象的文章,看后跟着打了一遍代码。感觉方法挺好的,在这里与大家分享一下。 &
我正在阅读将查询字符串传递给 Amazon 的 S3 以进行身份验证的文档,但似乎无法理解 StringToSign 的创建和使用方式。我正在寻找一个具体示例来说明 (1) 如何构造 String
前言:我对 C# 中任务的底层实现不太了解,只了解它们的用法。为我在下面屠宰的任何东西道歉: 对于“我怎样才能开始一项任务但不等待它?”这个问题,我找不到一个好的答案。在 C# 中。更具体地说,即使任
我有一个由一些复杂的表达式生成的 ILookup。假设这是按姓氏查找人。 (在我们简单的世界模型中,姓氏在家庭中是唯一的) ILookup families; 现在我有两个对如何构建感兴趣的查询。 首
我试图创建一个 MSI,其中包含 和 exe。在 WIX 中使用了捆绑选项。这样做时出错。有人可以帮我解决这个问题。下面是代码: 错误 error LGH
在 Yii 中,Create 和 Update 通常使用相同的形式。因此,如果我在创建期间有电子邮件、密码、...other_fields...等字段,但我不想在更新期间专门显示电子邮件和密码字段,但
上周我一直在努力创建一个给定一行和一列的 QModelIndex。 或者,我会满足于在已经存在的 QModelIndex 中更改 row() 的值。 任何帮助,将不胜感激。 编辑: QModelInd
出于某种原因,这不起作用: const char * str_reset_command = "\r\nReset"; const char * str_config_command = "\r\nC
现在,我有以下由 original.df %.% group_by(Category) %.% tally() %.% arrange(desc(n)) 创建的 data.frame。 DF 5),
在今天之前,我使用/etc/vim/vimrc来配置我的vim设置。今天,我想到了创建.vimrc文件。所以,我用 touch .vimrc cat /etc/vim/vimrc > .vimrc 所
我可以创建一个 MKAnnotation,还是只读的?我有坐标,但我发现使用 setCooperative 手动创建 MKAnnotation 并不容易。 想法? 最佳答案 MKAnnotation
在以下代码中,第一个日志语句按预期显示小数,但第二个日志语句记录 NULL。我做错了什么? NSDictionary *entry = [[NSDictionary alloc] initWithOb
我正在使用与此类似的代码动态添加到数组; $arrayF[$f+1][$y][$x+1] = $value+1; 但是我在错误报告中收到了这个: undefined offset :1 问题:尝试创
我是一名优秀的程序员,十分优秀!