老规矩,创项目
我们在创建模型时,需要迁移模型至数据库,而每迁移一次都会生成一条迁移记录在django_migrations表中
这样假如再次迁移时,服务器会比对迁移记录,假如不按照规则,强行删除0001迁移文件重新迁移,这时候比对时候,发现已经有迁移记录,所以迁移失败。
迁移原理
分两步实现
- 生成迁移文件
- 执行迁移文件
迁移文件的生成
- 根据models文件生成对应的迁移文件
- 根据models和已有迁移文件差别 生成新的迁移文件(假如需要修改已有模型)
执行迁移文件
先去迁移记录查找,哪些文件未迁移过
- app_label + 迁移文件名字
- 执行未迁移的文件
- 执行完毕,记录执行过的迁移文件
重新迁移
- 删除迁移文件
- 删除迁移文件产生的表
- 删除迁移记录
模型的对应关系
1 : 1
1 : N
M : N
常见的几种数据关系,django都提供了很好的支持,这些关系都是我们的业务需求而来。
1:1
使用models.OneToOneField()进行关联
class Card(models.Model):
person = models.OneToOneField(Person)
绑定卡与人的一对一关系,默认情况下,当人被删除的情况下,与人绑定的卡就也删除了,这个可以使用on_delete进行调整
on_delete
models.CASCADE 默认值
models.PROTECT 保护模式
models.SET_NULL 置空模式
models.SET_DEFAULT 置默认值
models.SET() 删除的时候重新动态指向一个实体
访问对应元素 person.pcard
应用场景
- 用于复杂表的拆分
- 扩展新功能
Django中 OneToOneField
- 使用的时候,关系声明还是有细微差别的
1:1的实现
- 使用外键实现的
- 对外键添加了唯一约束
数据删除
级联表
- 主表
- 从表
- 谁声明关系谁就是从表
在开发中如何确认主从
- 当系统遭遇不可避免毁灭时,只能保留一张表,这个表就是你的主表
默认特性(CASECADE)
- 从表数据删除,主表不受影响
- 主表数据删除,从表数据直接删除
SET
SET_NULL
- 允许为NULL
SET_DEFAULT
- 存在默认值
SET()
- 指定值
级联数据获取
- 主获取从 隐性属性 默认就是级联模型的名字
- 从获取主,显性属性,就是属性的名字
为什么要有1:1
拆分
假如用户表由于功能很强大,有200个字段,这样一张表很庞大,我们需要拆分成很多表,这样我们我们用户表只有用户名密码,最基本的信息,而其他信息,都绑定要用户表上。
扩展
一个用户表在设计的时候是增量操作,但是不让在表本身增加字段,比如增加token认证,我们将token和表中的用户进行绑定,假如允许多终端绑定,使用外键绑定多个token
实践:添加1:1(身份证:人)
新建models:
from django.db import models
# 人
class Person(models.Model):
p_name = models.CharField(max_length=16)
p_sex = models.BooleanField(default=False)
# 身份证
class IDcard(models.Model):
# 身份证号码唯一
id_num = models.CharField(max_length=18, unique=True, on_delete=models.CASCADE)
# 将身份证1:1绑定到人
id_person = models.OneToOneField(Person)
接下来我们迁移文件
我们在用,manage.py 命令时,可以追加--help来查看使用指南
python mange.py makemigrations app名 可以指定为某一个项目单独生成迁移文件,而不是所有项目。
urls:
url('^addperson/', views.add_person, name='add_person'),
url('^addidcard/', views.add_idcard, name='add_idcard'),
views:
def add_person(request):
# 获取人的信息
username = request.GET.get('username')
person = Person()
person.p_name = username
person.save()
return HttpResponse('person创建成功 %d' % person.id)
def add_idcard(request):
# 获取身份证
id_num = request.GET.get('idnum')
idcard = IDcard()
idcard.id_num = id_num
idcard.save()
return HttpResponse('IDCard %d' % idcard.id)
但是目前idcard和人还没有产生关系
新建url:用来绑定idcard和人
url('^bindcard/', views.bind_card, name='band_card'),
views:
# 绑定思路是,拿最新的一个人绑定最新的IDCARD
def bind_card(request):
# 获取表中最新的人
person = Person.objects.last()
# 获取表中最新的事
idcard = IDcard.objects.last()
# 借用下idcard = IDcard(),是为了idcard.id_person = person中可以输入报提示,这样就能避免出错
# idcard = IDcard()
idcard.id_person = person
idcard.save()
return HttpResponse('绑定成功')
接下来访问测试:
我们在网址中输入参数/?username=jack
接着添加身份证:
这时候发现返回报错,说id_num不可以为空,也就是说,我们之前建立的模型不够完善,所以我们要修改模型
# 身份证
class IDcard(models.Model):
# 身份证号码唯一
id_num = models.CharField(max_length=18, unique=True)
# 将身份证1:1绑定到人
id_person = models.OneToOneField(Person, on_delete=models.CASCADE, null=True, blank=True)
我们在最后添加了null=True,blank=True
接着我们makemigrations,重新生成迁移文件进行迁移
成功后我们进行访问
接着我们绑定
现在我们来观察数据库:
我们可以看到id_num为111绑定到了人justin上
这时候我们再新建一个人,再绑定一下
发现依旧绑定成功,再来观察数据库绑定关系:
我们发现,关系还是一对一的关系,只不过原本id_num为111绑定到了新建的人syz身上。
这时候我们再次尝试,只新建idcard观察是否能绑定成功:
我们可以看到,报错,原因是,将120绑定到syz时检测到已经有idcard绑定仔syz身上,又因为是一对一关系所以失败。
可见在哪个模型声明一对一关系还是有细微区别的,在声明关系表中,不能拿表中已经确定了和谁绑定完毕的行再去绑定其他。
我们观察idcard的DDL
可以发现1:1的实现
- 使用外键实现的
- 对外键添加了唯一约束
实践:删除一对一关系
添加url
url('^removeperson/', views.remove_person, name='remove_person'),
url('^removeidcard/', views.remove_idcard, name='remove_idcard'),
添加views
# 删除最新添加进数据库的人
def remove_person(request):
person = Person.objects.last()
person.delete()
return HttpResponse('人移除成功')
# 产出最新idcard
def remove_idcard(request):
idcard = IDcard.objects.last()
idcard.delete()
return HttpResponse('身份证移除成功')
原来两张表中有一下数据
我们先删除idcard,访问,返回身份证移除成功,这时候我们查看数据库
idcard删除了一条,而person中没有改变
再接下来我们删除人
查看数据库
我们发现idcard没有了,而绑定的人syz也消失了,也就是说我们删除人的时候,级联的IDCARD也会删除
这也就是找淘宝京东注销账号不能把账号直接删除的原因,因为级联的数据太多,账号级联了各种评论,浏览历史,订单记录,直接删除可能会导致数据库崩溃,所以只能将账号的部分属性隐藏起来。
数据删除
级联表
- 主表
- 从表
- 谁声明关系谁就是从表,和男女关系一样,男的追女,男就是从
在开发中如何确认主从
- 当系统遭遇不可避免毁灭时,只能保留一张表,这个表就是你的主表
- 也就是你媳妇和你妈掉河里了,你该救谁(滑稽)
默认特性(CASECADE)
- 从表数据删除,主表不受影响
- 主表数据删除,从表数据直接删除
PROTECT 受保护
- 开发中为了防止误操作,我们通常会设置为此模式
- 主表如果存在级联数据,删除动作受保护,不能成功
- 主表不存在级联数据,可以删除成功
如果修改默认值为PROTECT,我们再删除绑定了IDCARD的人,就会删除不成功。
如果硬要删,要先删除级联数据idcard,再删除person
SET
- SET_NULL
- 允许为NULL
- SET_DEFAULT
- 存在默认值
- SET()
- 指定值
我们可以删除人,但是级联数据idcard级联persond的字段就变为null
不过要注意设置了SET_NULL 就要添加null=True
对应的SET_DEFAULT也要添加default=
linux系统中我们强行杀掉一个进程用kill指令,殊不知不是人为结束了这个进程,而是告诉进程你可以死了,然后进程自杀结束运行,kill指令是发送一条命令给进程,告诉进程你可以死了,然后进程自杀。(君叫臣死,臣不得不死)
实践:通过身份证获取到绑定身份证的人,通过人获取到绑定的身份证
添加url
url('^getperson/', views.get_person, name='get_person'), # 通过身份证来查人
url('^getidcard/', views.get_idcard, name='get_idcard'), # 通过人来查身份证
views:
# 逻辑是通过身份证来查人
def get_person(request):
# 取出最新的身份证,我们只是模拟场景,真实情况应该是根据条件来获取数据
idcard = IDcard.objects.last()
# 从idcard获取到对应的person对象
person = idcard.id_person
# 从person对象中获取p_name字段
return HttpResponse(person.p_name)
def get_idcard(request):
person = Person.objects.last()
# 提示Person的隐形属性,使用完注释
person = Person()
idcard = Person.idcard
return HttpResponse(idcard.id_num)
测试:
我们通过级联的身份证查人的时候成功返回
但是在我们用人调取对应身份证的时候发现没有Person.idcard这个隐形属性,有坑(裂开来)
- 级联数据获取
- 主获取从 隐性属性 默认就是级联模型的名字(实验失败,隐性属性不存在)
- 从获取主,显性属性,就是属性的名字
此处评论已关闭