Django框架 - 模型关系详解
Django 框架中,模型是数据层,也就是管理数据
在整个数据库的设计中,还需要设计模型关系,也叫数据库关系
关系概述
一对一
一个表中的一条数据对应另一个表中的一条数据
-- 例如用户和用户详细信息,在数据库的设计中,应该分开存储
-- 假设需要存储的字段有:id,用户名,密码,手机号,邮箱,头像,年龄,性别,地址,学历,身份证号...
-- 大量字段可以设置在一个表中进行数据存储,但是效率不高,所以通常把这个数据表垂直分表
-- 即把表中的字段进行分离,分到不同的表中存储
-- 用户表,存储主要信息
-- id,用户名,密码,手机号,邮箱,头像,年龄,性别
-- 用户详情表,存储其他信息
-- id,地址,学历,身份证号
-- 分表后,需要建立两个表之间的关系,在用户详情表中增加字段 uid,存储当前行数据的用户 id,即用户表的主键
-- id,uid,地址,学历,身份证号
-- 如果用户表中的主键 id 在用户详情表中是唯一的,那么此关系为一对一关系
一对多
一个表中的一个数据对应另一个表中的多个数据
-- 例如学生和班级
-- 学生表
-- id,姓名,年龄,手机号
-- 班级表
-- id,班级1,班级2,班级3
-- 在多的一端定义外键字段关联一的一端
-- 一对多的关系非常常见
多对多
a表中的一个数据对应b表中的多个数据,同时b表中的一个数据也对应a表中的多个数据,即双向一对多
-- 例如老师和班级
-- 老师1:班级1,班级3
-- 老师2:班级2,班级3
-- 老师3:班级1,班级2,班级4
-- 老师表
-- id 老师
1 老师1
2 老师2
3 老师3
-- 班级表
-- id 班级
1 班级1
2 班级2
3 班级3
4 班级4
-- 建立第三个表,描述表与表之间的关系
-- 老师_班级_关系表
-- id 老师tid 班级cid
1 老师1 班级1
2 老师1 班级3
3 老师2 班级2
4 老师2 班级3
5 老师3 班级1
6 老师3 班级2
7 老师3 班级4
关系的实现
- 逻辑关系
在需要的表中增加一个字段,存储另一个表中的主键,通过程序来控制和管理表的关系
- 物理关系
通过数据库的外键索引创建表的关系,这是在数据库上物理形式创建的索引关系,不推荐使用此方法
缺点:外键会导致表与表之间耦合,update和delete操作都会涉及相关联的表,十分影响sql的性能,甚至会造成死锁,高并发情况下容易造成数据库性能下降,大数据高并发业务场景数据库使用以性能优先
模型关系
一对一
一对一模型关系的定义
sid = models.OneToOneField(to,on_delete=models.CASCADE)
外键字段定义在要与主要信息表相关联的表中
# 学生类
class Stu(models.Model):
name = models.CharField(max_length=20)
age = models.IntegerField()
phone = models.CharField(man_length=11)
# 学生详情类
class Stuinfo(models.Model):
# 创建一对一模型关系
sid = models.OneToOneField(Stu,on_delete=models.CASCADE)
addr = models.CharField(100)
team = models.CharField(20)
一对一模型关系的使用
# 添加
data = {'name':'messi','age':33,'phone':'3','address':'maj','team':'ulg'}
# 先添加Stu中的数据
stuobj = models.Stu()
stuobj.name = data['name']
stuobj.age = data['age']
stuobj.phone = data['phone']
stuobj.save()
# 再添加Stuinfo中的数据
# stuobj = models.Stu.objects.get(id=6)
sinfo = models.Stuinfo()
sinfo.address = data['address']
sinfo.team = data['team']
# Stuinfo模型类中,定义了外键sid,添加时要指定sid的数据,并且要求是一个对象
sinfo.sid = stuobj
sinfo.save()
# 查询
# 1.根据Stu对象获取Stuinfo数据
stuobj = models.Stu.objects.get(id=5)
# 通过Stu对象直接获取与自己关联的Stuinfo对象
print(stuobj.stuinfo.address)
# 2.根据Stuinfo对象获取Stu数据
sinfo = models.Stuinfo.objects.last()
# 通过被关联的Stuinfo对象的sid字段获取Stu对象
print(sinfo.sid.name)
# 删除
# 删除Stu中的数据后,Stuinfo中对应的数据也被删除
stuobj = models.Stu.objects.get(id=9)
stuobj.delete()
# 删除Stuinfo中的数据后,Stu中对应的数据不受影响
sinfo = models.Stuinfo.objects.get(sid=4)
sinfo.delete()
一对多
一对多模型关系的定义
lid = models.ForeignKey(to,on_delete=models.CASCADE)
外键字段定义在多的一端
# 班级模型
class League(models.Model):
lname = models.CharField(max_length=20)
# 学生模型
class Stu(models.Model):
name = models.CharField(max_length=20)
age = models.IntegerField()
phone = models.CharField(max_length=11)
# 一对多,外键字段
lid = models.ForeignKey(to=League,on_delete=models.CASCADE,default='1')
# 如果Stu模型类已经创建,添加外键字段lid时,要求设置默认值
# 解决方法:1.注释全部模型,重新创建
# 2.先创建League模型类,添加数据后,再为Stu模型类定义外键字段并设置默认值
一对多模型关系的使用
# 添加
# 创建League数据
l = models.League.objects.all()
print(l)
# 创建Stu数据时需要指定lid
data = {'name':'xavi','age':6,'phone':'37','lid':l[0]}
s = models.Stu(**data)
s.save()
# 查询
# 1.根据League查询Stu
l = models.League.objects.get(id=1)
print(l)
print(l.stu_set.all())
# 2.根据Stu查询League
s = models.Stu.objects.all()
print(s[1].lid)
# 删除
l = models.League.objects.get(id=1)
l.delete()
多对多
多对多模型关系的定义
models.ManyToManyField(to)
外键字段可以定义在任一表中
# 班级模型
class League(models.Model):
lname = models.CharField(max_length=20)
# 教师模型
class Teacher(models.Model):
tname = models.CharField(max_length=20)
# 多对多,外键字段
league = models.ManyToManyField('League')
多对多模型关系的使用
# 创建教师
t1 = models.Teacher(**{'tname':'爱因斯坦'})
t2 = models.Teacher(**{'tname':'狄拉克'})
t3 = models.Teacher(**{'tname':'海森堡'})
t1.save()
t2.save()
t3.save()
# 创建班级
l1 = models.League(**{'lname':'physics1'})
l2 = models.League(**{'lname':'physics2'})
l3 = models.League(**{'lname':'physics3'})
l4 = models.League(**{'lname':'physics4'})
l1.save()
l2.save()
l3.save()
l4.save()
# 创建关系
# 获取所有教师和班级对象
ls = models.League.objects.all()
ts = models.Teacher.objects.all()
# 添加
# 给班级设置教师
ls[2].teacher_set.set([ts[0],ts[1]])
# 给教师设置班级
ts[0].league.set([ls[2],ls[3],ls[4]])
# 查询
# 通过教师查询班级
res = ls[2].teacher_set.all()
# 通过班级查询教师
res = ts[1].league.all()
print(res)
# 删除
# 删除任意表中的数据,关系表中与其相关的关系将被同时删除,另一个表不受影响
ls[2].delete()