老规矩,创项目
我们在创建模型时,需要迁移模型至数据库,而每迁移一次都会生成一条迁移记录在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这个隐形属性,有坑(裂开来)

  • 级联数据获取
    - 主获取从 隐性属性  默认就是级联模型的名字(实验失败,隐性属性不存在)
    - 从获取主,显性属性,就是属性的名字
最后修改:2024 年 03 月 13 日
如果觉得我的文章对你有用,请随意赞赏