模型迁移原理

迁移过程

  • 定义数据模型,在models.py中定义模型类。

  • 生成迁移文件

    1. python manage.py makemigrations [AppName]
  • 如不指定AppName则对整个项目生成迁移文件,指定AppName则只对指定的应用生成迁移文件。
  • 迁移文件实质上就是生成数据库SQL语句,存放在AppName下的migrations目录中。
  • 迁移文件就是根据models.py中的定义生成。
  • 如果models.py中有变化,则会检测并生成新的迁移文件
    • 执行迁移
      1. python manage.py migrate
  • 执行迁移脚本,既将迁移文件在数据库中执行。
  • 迁移过程则是先到django_migrations表中查看迁移记录,表中会记录上次迁移的AppName名称和迁移文件名称。
  • 执行未迁移的文件。
  • 执行完毕后,把执行的记录写入到django_migrations表中。

重新迁移

在某些时候需要重新迁移模型时,一般会有如下步骤

1、删除migrations目录下的迁移文件

2、删除数据库表,再次执行迁移命令。

  1. % python manage.py makemigrations
  2. Migrations for 'Apps':
  3. Apps/migrations/0001_initial.py
  4. - Create model Idcard
  5. - Create model Person
  6. % python manage.py migrate
  7. Operations to perform:
  8. Apply all migrations: Apps, admin, auth, contenttypes, sessions
  9. Running migrations:
  10. No migrations to apply.

这时会发现迁移文件可以正常生成,但执行迁移却并不会更新至数据库。这是因为Django会把上一次迁移记录到数据库的django_migrations表中。只需再执行第三步即可。

3、需要删除表中的这条记录就可以了。

  1. mysql> delete from django_migrations where app='Apps';
  2. Query OK, 1 row affected (0.00 sec)

再次执行迁移命令即可。

模型关系

一对一 OneToOneField

  • 应用场景

    • 用于复杂表的拆分,如将用户表和用户身份信息表拆分
    • 扩展新功能,如系统运行一段时间,数据量很大,增加新的字段
  • 模型创建
class Person(models.Model):
    p_name = models.CharField(max_length=32)
    p_age = models.IntegerField(default=18)


class Idcard(models.Model):
    idcard = models.CharField(max_length=18)
    # 建立一对一关系
    id_person = models.OneToOneField(Person, null=True, blank=True, on_delete=models.CASCADE
  • 实现原理

    • 在外键ForeignKey的基础上增加了唯一约束,就实现了一对一的关系。
    • 通过DDL语句可以看到实现原理
create table Apps_idcard
(
    id           int auto_increment
        primary key,
    idcard       varchar(18) not null,
    id_person_id int         null,
    constraint id_person_id
        unique (id_person_id),   -- 约束
    constraint Apps_idcard_id_person_id_1282a770_fk_Apps_person_id
        foreign key (id_person_id) references Apps_person (id)  -- 外键
);
  • 一对一模型的主从关系

    • 主表:上例种的Person既为主表
    • 从表:谁声明关系谁是从表,上例种的IdCard为从表
    • 一对一关系删除时的操作

      • 默认on_delete为models.CASCADE

        • 从表数据删除,主表不受影响
        • 主表数据删除,从表数据直接删除
      • models.PROTECT

        • 保护模式
        • 主表删除数据时,如果存在关联数据,会抛出保护异常。
        • 必须先删除主表的级联数据,才可以删除主表数据。
      • models.SET_NULL

        • 设置级联数据为空
        • 删除主表时,关联数据设置为空,要求关联数据字段允许为空。
      • models.SET_DEFAULT

        • 存在默认值
      • models.SET()

        • 指定值
    • 一对一模型查询

      • 通过主表查询从表数据

        • 隐性属性
        • QuerySet.从表字段
      • 通过从表查询主表数据

        • 显性属性直接调用
        • QuerySet.id_主表

一对多 ForeignKey

  • 模型的创建
class ClassName(models.Model):
    cname = models.CharField(max_length=18)

    def __str__(self):
        return self.cname

    class Meta:
        db_table = 'classname'

class Student(models.Model):
    s_name = models.CharField(max_length=18)
    # 通过ForeignKey的方式进行一对多关系创建
    cname = models.ForeignKey(ClassName, on_delete=models.DO_NOTHING)

    def __str__(self):
        return self.s_name

    class Meta:
        db_table = 'student'
  • 实现原理

    • 通过创建数据库外键的方式进行一对多关系创建
create table student
(
    id       int auto_increment
        primary key,
    s_name   varchar(18) not null,
    cname_id int         not null,
    constraint student_cname_id_3f032e9c_fk_classname_id
        foreign key (cname_id) references classname (id)
);
  • 一对多模型主从关系

    • 主表为ClassName
    • 从表为Student
  • 一对多模型的查询操作

    • 通过主查询从表数据

      • 主表对象.从表小写_set.过滤器方法
      • 同样支持all、filter、get、exclude等方法
        def getstudent(request):
        # 主查从,通过班级查询学生信息
        classname = ClassName.objects.get(cname='318')
        item = classname.student_set.all()
        print(item)
        return HttpResponse('get success {}'.format(item))
        
    • 通过从表查询主表数据

      • 从表对象.外键字段.属性
        def getclass(request):
        # 从查主,通过学生信息查询班级
        sname = Student.objects.last()
        classname = sname.cname.cname
        return HttpResponse('{}'.format(classname))
        
    • 跨关系查询

      • 从表查主表:从表类名.objects.过滤器(外键名小写属性比较符=’*‘)
      • 主表查从表:主表类名.objects.过滤器(从表类名小写属性比较符=’*‘) ```python def vlookup(request):

        主表查询从表数据

        cname = ClassName.objects.filter(student__s_name=’lisi’)

        从表查询主表数据

        student = Student.objects.filter(cname__cname=’318’)

      return HttpResponse(student) ```

多对多 ManyToManyField