Django框架模型关系详解
在Django的框架中 模型时数据层,也就是管理我们的数据
在整个数据库的设计中,还有一种关系的设置,模型关系,或者叫做数据库关系
关系的讲解
一对一
简单理解就是两个表中的数据是一一对应的,例如a表中的一条数据对应着b表中的一条
例如:一个用户和用户的详情信息在数据库的设计中,应该是分开存储的。
-- 假设需要存储一个用户的很多信息,其中包括了以下字段
-- ID,用户名,密码,邮箱,手机号,年龄,性别,学历,籍贯,民族,紧急联系人,头像,地址。。。。
-- 1 张三。 123。@qq.com,。。 硕士 山西
-- 2。李四。 567 。。 本科 山东。。。
-- 像这么多的字段可以设置在一个表中进行数据的存储,但是效率不高。通常情况下会把这个数据表进行垂直分表
-- 垂直分表,就是把表中的字段进行分离,分到不同的表中进行存储
-- 可以像以下方式进行 垂直分表
-- 用户 表 中存储 用户的主要的常用的信息数据
-- id 用户名,密码,邮箱,手机号,头像,年龄,性别,
-- 1 张三 。123。 @qq.com
-- 2 李四。 567 。。
-- 用户详情 表 中存储其它的用户信息,不常用或不重要的信息数据
-- ID,学历,籍贯,民族,紧急联系人,地址。。。
-- 1,本科。山东
-- 2,硕士 山西
-- 出现了一个问题,就是怎么证明 用户详情表中数据哪个是张三的,哪个是李四的。
-- 如何解决这个问题呢?
-- 给用户详情表多增加一个字段,uid,这个字段是当前行数据的用户id
-- ID,UID,学历,籍贯,民族,紧急联系人,地址。。。
-- 1, 2 本科。山东
-- 2, 1 硕士 山西
-- UID这个字段存储的是 用户表中 用户的ID主键,这样两个表就建立了关系。
-- 如果用户id主键在用户详情中只能有一个,那么这种关系就是一对一关系
mysql> select * from home_stu;
+----+--------+-----+---------+
| id | name | age | phone |
+----+--------+-----+---------+
| 1 | 张三 | 20 | 1231231 |
| 2 | 李四 | 22 | 1231231 |
| 3 | 王五 | 21 | 1231231 |
+----+--------+-----+---------+
3 rows in set (0.00 sec)
mysql> select * from home_stuinfo;
+----+---------+--------+--------+
| id | address | xueli | sid_id |
+----+---------+--------+--------+
| 1 | 山东 | 本科 | 1 |
| 2 | 山西 | 本科 | 3 |
| 3 | 河南 | 本科 | 2 |
+----+---------+--------+--------+
一对多
一对多关系就是一个表中的数据,对应着另外一个表中多条数据
或者反过来理解,就会多条数据同属于另外一个表的一条数据
```sql — 比如 新闻分类和新闻 — 新闻分类表 id 新闻分类名称 1 经济新闻 2 国际新闻 3 地方新闻
— 新闻表 id 新闻的标题 所属新闻分类 作者 发布时间 新闻内容 。。。 1 中国改开以来经济突飞猛进 1 2。 北京自2019年11月起限制进京证办理次数 3 3。 美国特朗普被弹劾,成为了特没谱。 2 4 2019年被称为未来最差的十年, 1
— 一对多的关系在项目非常常见: — 商品分类和商品,班级和学生,商品和商品图片
<a name="000a6247"></a>
### 多对多
多对多的关系就是,a表中的一条数据,对应着b表中的多条数据。同时,b表中的一条数据也对应着a表中多条数据,是双向的一对多关系。
例如: 一本书可以有多个标签 书名。 标签 <<道德经>> 古典文籍,传统文化, <<三国演义>> 历史文籍,小说 <<山海经>> 古典文集,传统文化,小说
上面这个案例中,看一本书是不是有多个标签 反过来看标签,是不是一个标签中也可以有多本书 举例:道德经这本书有两个标签,同时标签 古典文集 下是不是也有多本书呢?
书籍表 id 书名 作者 1 <<道德经>> 老子 2 <<三国演义>> 罗贯中 3 <<山海经>> 不详
标签表 id 标签名 1。 古典文籍 2。 传统文化 3。 历史文籍 4。 小说
建立第三个表,专门定义描述 多个表的关系 书籍标签关系表 id. 书籍BID 标签TID 1。 1 1 2。 1 2 3。 2 3 4。 2 4 5。 3 1 6。 3 2 7。 3 4
例如 班级和老师,一个班级有多个老师授课,一个老师到多个班级授课。
<a name="aef40cbd"></a>
## 关系的实现
了解了数据库中表的关系后,我们想一下,如何实现表中的关系呢?
- 逻辑关系 (自己用代码逻辑实现)
- 直接在需要的表中增加一个普通的字段,存储对应的另外一个表中的主键
- 通过程序或者业务逻辑来控制和管理这个表关系
- 物理关系(使用数据库的外键索引来实现)
- 通过数据库的 外键索引 创建表的关系
- 这是一种在数据库上物理形式创建的索引关系。
- 不推荐使用物理外键方式建立关系:外键会导致表与表之间耦合,update与delete操作都会涉及相关联的表,十分影响sql 的性能,甚至会造成死锁。高并发情况下容易造成数据库性能下降,大数据高并发业务场景数据库使用以性能优先
<a name="a62e0102"></a>
## 模型关系
<a name="0bc5b2d6"></a>
#### 一对一关系的定义实现
models.OneToOneField(Stu,on_delete=models.CASADE)
django文档:[https://docs.djangoproject.com/en/2.2/topics/db/examples/one_to_one/](https://docs.djangoproject.com/en/2.2/topics/db/examples/one_to_one/)
```python
# 定义 学生 模型
class Stu(models.Model):
name = models.CharField(max_length=5)
age = models.IntegerField()
phone = models.CharField(max_length=11)
# 定义 学生详情 模型
class Stuinfo(models.Model):
# 创建 一对一模型关系.
sid = models.OneToOneField(Stu,on_delete=models.CASCADE)
address = models.CharField(max_length=100)
xueli = models.CharField(max_length=10)
一对一模型关系的使用
# 添加数据
# data = {'name':'王五','age':21,'phone':'1231231','address':'河南','xueli':'本科'}
# 先添加 用户数据
# stuobj = models.Stu()
# stuobj.name = data['name']
# stuobj.age = data['age']
# stuobj.phone = data['phone']
# stuobj.save()
# 给用户创建用户详情数据
# sinfo = models.Stuinfo()
# sinfo.address = data['address']
# sinfo.xueli = data['xueli']
# # 需要注意,在Stuinfo这个模型类中,定义了sid这个外键,因为在添加时,要指定这个外键的数据
# sinfo.sid = stuobj # 注意这个外键字段要求是一个对象
# sinfo.save()
# 查询数据
# 1.可以根据用户对象获取用户详情数据
# stuobj = models.Stu.objects.get(id=2)
# print(stuobj.stuinfo) # 通过用户对象 获取 用户详情对象
# print(stuobj.stuinfo.address)
# 2.也可以根据用户详情数据,获取用户对象
# sinfo = models.Stuinfo.objects.first()
# print(sinfo.address)
# print(sinfo.sid.name) # 通过用户详情对象,和sid字段获取用户信息
# 删除
# 1. 删除用户后,对用户详情有影响吗?
# 当前用户对象删除时,会随之删除相关的关联数据,对应的用户详情也会被删除
stuobj = models.Stu.objects.get(id=1)
stuobj.delete()
# 2. 删除用户详情,对用户有影响吗?
# 删除用户详情,对用户没有任何影响,因为用户详情依赖用户,但是用户不依赖用户详情
一对多模型关系的定义
models.ForeignKey(关联的模型类,on_delete=models.CASCADE)
把这个外键字段定义在多的一端,例如班级和学生,一个班级对应多个学生,因为外键定义在学生那边。
# 定义 学生 模型
class Stu(models.Model):
name = models.CharField(max_length=5)
age = models.IntegerField()
phone = models.CharField(max_length=11)
# 一对多
bid = models.ForeignKey('Banji',on_delete=models.CASCADE)
# 定义 学生详情 模型
class Stuinfo(models.Model):
# 创建 一对一模型关系.
sid = models.OneToOneField(Stu,on_delete=models.CASCADE)
address = models.CharField(max_length=100)
xueli = models.CharField(max_length=10)
# 定义 班级 模型
class Banji(models.Model):
# 班级名称
bname = models.CharField(max_length=10)
'''
注意 如果Stu模型类已经创建,给它添加bid外键字段时,要求进行选择默认值。
方案:
全部注释模型,重新创建,
或者先创建班级模型,添加一些数据后在给Stu模型类定义bid并设置默认值
'''
一对多模型关系的使用
# 添加数据,创建关系
# 先创建班级数据
bs = models.Banji.objects.all()
print(bs)
# 创建学生数据时需要指定对应的班级数据
studata = {
'name':'罗胖',
'age':53,
'phone':'312312',
'bid':bs[2]
}
# obj = models.Stu(**studata)
# obj.save()
# 查询数据
# 1. 可以通过班级查询学生
bs = models.Banji.objects.get(id=2)
print(bs.bname)
# 查询这个班级对应所有的学生,
print(bs.stu_set.all())
# 2. 可以通过学生查询班级
ss = models.Stu.objects.all()
print(ss)
print(ss[3].bid.bname)
# 删除数据
# 1. 删除班级,会影响学生吗? 会影响
obj = models.Banji.objects.get(id=2)
obj.delete()
# 2. 删除学生,会影响班级吗?
多对多模型关系的定义
models.ManyToManyField(to,** options)
# 书籍 模型
class Books(models.Model):
bname = models.CharField(max_length=20)
author = models.CharField(max_length=10)
# 标签 模型
class Tags(models.Model):
tname = models.CharField(max_length=10)
# 多对多模型关系定义
book = models.ManyToManyField('Books')
多对多模型关系的使用
# 创建书籍
# b1 = models.Books(**{'bname':'道德经','author':'老子'})
# b2 = models.Books(**{'bname':'三国演义','author':'罗贯中'})
# b3 = models.Books(**{'bname':'山海经','author':'不详'})
# b1.save()
# b2.save()
# b3.save()
# 创建标签
# t1 = models.Tags(**{'tname':'古典文籍'})
# t2 = models.Tags(**{'tname':'传统文化'})
# t3 = models.Tags(**{'tname':'历史文籍'})
# t4 = models.Tags(**{'tname':'小说'})
# t1.save()
# t2.save()
# t3.save()
# t4.save()
# 创建关系
bs = models.Books.objects.all() # 所有的 书籍对象
ts = models.Tags.objects.all() # 所有的 标签对象
# 可以 给书籍设置 标签
# bs[0].tags_set.set([ts[0],ts[1]])
# 可以 给标签设置 书籍
# ts[3].book.set([bs[1],bs[2]])
# 查询
# 可以通过标签获取书籍
# res = ts[3].book.all() # 获取标签中的所有书籍数据
# 可以通过书籍获取标签
# res = bs[0].tags_set.all() # 获取书籍中的所有标签数据
# print(res)
# 删除,删除书籍或者标签都会影响标签与书籍的关系,其它不变