一、对象关系映射(ORM)

面向对象与关系型数据库中的数据存储方式并不匹配,因此为了在编程中实现对数据库的操作,需要 ORM 技术将数据库中数据映射到对象之中。通过对对象的操作,做到对数据库的操作,从而实现增删改查等功能。

面向对象编程中,“关系”体现在类与其属性之间以及同一类的不同属性之间;而在关系型数据库中,“关系”体现在同一表中的不同列字段中。

二、django 的 ORM 实现

django 通过 models.Model 类来对应数据库中的表结构,如

class User(models.Model):

即表示了 User 类,也对应了数据库中的 user 表。django 会根据类的声明自动在数据库中建表。

django 用类中的属性来表示表的列字段。如

class User(models.Model):
    id = models.IntegerField(primary_key=True)
    name = models.CharField(max_length=50)
    password = models.CharField(max_length=50)

定义了 user 表的主键、名字、密码信息。

表间关系通过类与其属性表示,如

class User(models.Model):
    id = models.IntegerField(primary_key=True)
    name = models.CharField(max_length=50)
    password = models.CharField(max_length=50)
    courses = models.ManyToManyField(to="Course")

courses 属性表示用户具有的课程,反映了用户与课程之间的关系。

三、类属性与数据库字段的对应

如上一节所述,django 建立了类属性与数据库字段之间的对应关系。常见的如

models.CharField
models.TextField
models.IntegerField
models.FloatField

就对应了数据库中的 char(n),text,int,float 类型。不一一详述。

四、主键与外键

在表中要定义主键,作为该表一条记录的唯一标识。相应的,在 django 中定义主键需要在对应属性上添加 primary_key=True 参数。例如

id = models.IntegerField(primary_key=True)

未定义主键时,django 也会自动创建一个主键。

通过外键可以设置表间的关系。外键通过属性 models.ForeignKey 定义。如

class Task(models.Model):
    id = models.IntegerField(primary_key=True, null=False)
    name = models.CharField(max_length=50, null=False)
    course = models.ForeignKey(to="Course", on_delete=models.CASCADE)

就定义了一个外键,对数据库来说,保存的是 course 表中的主键信息。但对 Task 类来说,则是定义了一个 Course 类型的属性。但这两种观点的本质是一样的,都是建立了 task 到 course 的关系。更准确地说,是多到一的关系(不同的 task 可以对应同一个 course)。

注意建立表间关系的时候,需要设置 on_delete 参数,表示外键所对应的记录被删除时,本记录的行为。在例子中 CASCADE 表示外键对应的删除后,本记录也删除。

五、表间关系的创建

外键是创建表间关系的基础。这一节将更深入讨论表间关系。表间关系可以分为三类,分别是一对一、多对一和多对多。可以通过如下例子进行理解:

  • 一对一:一个人对应唯一的身份证,一张身份证对应唯一的一个人。
  • 多对一:一个班级有许多学生,而一个学生只属于某个班级。
  • 多对多:一门课有许多的学生,一个学生会上不同的课。

一对一的创建

在数据库中,一对一的关系比较随意,可以在 A 表中包含 B 表的主键,也可以在 B 表中包含 A 表的主键。只需要保证外键的唯一性即可。

django 设置一对一关系,需要定义 models.OneToOneField 的属性。如在 Person 类中有

id_card = models.OneToOneField(to="IDCard", on_delete=models.RESTRICT)

这样就可以通过访问 Person 类的属性 id_card 来访问对应的表格数据了。

models.RESTRICT 表示如果存在关系,则删除会产生异常

多对一的创建

建立多对一关系,在数据库中需要让多的一方设置外键,包含一的一方的主键。

而在 django 中,则只需要添加一个外键即可实现。如第五节中的例子

course = models.ForeignKey(to="Course", on_delete=models.CASCADE)

多对多的创建

多对多关系不能通过在任何一方或两方中添加外键实现。需要建立第三个表,称中间表或关系表,在该表中同时包含原来两个表的主键。

django 通过添加 models.ManyToManyField 创建多对多关系。如

class Course(models.Model):
    id = models.CharField(max_length=10, primary_key=True, null=False)
    name = models.CharField(max_length=50, null=False)
    users = models.ManyToManyField(to=User)

这样将自动创建一个中间表 course_user,包含 course 表和 user 表的主键。

但是这样只能创建由 Course 到 User 的单向联系。通过 Course 可以访问 User,但从 User 却不能访问 Course。可以自行定义中间表来实现双向多对多关系。

注意,如果在 User 表中添加属性 courses = models.ManyToManyField(to=Courses) 并不能解决问题。这只会创建另一个表 user_course,最终形成两个单向多对多关系。

正确的方式是通过 through= 参数指定中间表。

class Course(models.Model):
    id = models.CharField(max_length=10, primary_key=True, null=False)
    name = models.CharField(max_length=50, null=False)
    users = models.ManyToManyField(to=User, through="Course2User")


class Course2User(models.Model):
    id = models.IntegerField(primary_key=True, null=False)
    course = models.ForeignKey(to="Course", on_delete=models.CASCADE)
    user = models.ForeignKey(to=User, on_delete=models.CASCADE)

这样就可以通过中间表查询某个 User 对应的 Course 了。