8. 数据库表的关联

Django 专栏收录该内容
21 篇文章 0 订阅

操作系统: windows
IDE: Pycharm

后端系统开发中, 数据库设计是重中之重。特别是前后端分离的系统, 后端的职责基本就是数据管理, 开发的代码几乎都是围绕数据操作的。

因此,常用的数据库表和表之间的关系的设计就很重要。
目前使用的数据库系统主要还是关系型数据库(建立在关系模型基础上的数据库) 。

  • 常见的关系型数据库:mysql、oracle、 sqlserver、SQLite ;
  • 常见的非关系型数据库:mongodb;

而关系型数据库,设计的一个难点就是各种表之间的关联关系 。
常见的3种关联关系就是: 一对多一对一多对多


一对多:

表之间一对多的关系,就是外键关联关系

现在系统中, 已经定义了客户(Customer)这张表 :

class Customer(models.Model):
    # 客户名称
    name = models.CharField(max_length=200)

    # 联系电话
    phonenumber = models.CharField(max_length=200)

    # 地址
    address = models.CharField(max_length=200)

还需要定义药品(Medicine)这张表,包括药品名称、编号和描述 这些信息。
添加如下的类定义:

class Medicine(models.Model):
    # 药品名
    name = models.CharField(max_length=200)
    # 药品编号
    sn = models.CharField(max_length=200)
    # 描述
    desc = models.CharField(max_length=200)

接下来我们还要定义 订单(Order)这张表,这个Order表包括创建日期、客户、药品、数量。

其中:
客户字段对应的客户 只能是 Customer 中的某个客户记录,也就是说Order表里面的一条订单记录的客户对应 Customer表里面的一条客户记录。

而且一个客户记录可以对应多条订单记录。

这就是一对多的关系:
在这里插入图片描述
像这种一对多的关系,数据库中是用外键来表示的。

如果一个表中的某个字段是外键,那就意味着这个外键字段的记录的取值,只能是它关联表的某个记录的主键的值。

我们定义表的 Model类的时候,如果没有指定主键字段,migrate 的时候 Django 会为该Model对应的数据库表自动生成一个id字段,作为主键

比如,我们这里,Customer、Medicine表均没有主键,但是在migrate之后,查看数据库记录就可以发现有一个id字段,且该字段是主键 (primary key)。

Django中用models.ForeignKey()方法实现一对一的关系,如下:

common/models.py:

from django.db import models
import datetime

# Create your models here.

#存放数据库表对象

class Customer(models.Model):
    #客户名称
    name = models.CharField(max_length=200) #对应数据库中的varchar

    #联系电话
    phonenumber = models.CharField(max_length=200)

    #地址
    address = models.CharField(max_length=200)

class Medicine(models.Model):
    # 药品名
    name = models.CharField(max_length=200)

    # 药品编号
    sn = models.CharField(max_length=200)

    # 描述
    desc = models.CharField(max_length=200)


class Order(models.Model):
    # 订单名
    name = models.CharField(max_length=200, null=True, blank=True)

    # 创建日期
    create_date = models.DateTimeField(default=datetime.datetime.now)

    # 客户 customer字段是外键, 和Customer类的主键关联
    customer = models.ForeignKey(Customer, on_delete=models.PROTECT)

补充:
on_delete 参数指定了当我们删除外键指向的主键记录时, 系统的行为。
on_delete 不同取值对应不同的做法,常见的做法如下:

  • CASCADE:删除主键记录和相应的外键表记录

比如,我们要删除客户张三,在删除了客户表中张三记录同时,也删除Order表中所有这个张三的订单记录。

  • PROTECT:禁止删除记录

比如,我们要删除客户张三,如果Order表中还有张三的订单记录,Django系统就会抛出ProtectedError类型的异常,当然也就禁止删除客户记录和相关的订单记录了。
除非我们将Order表中所有张三的订单记录都先删除掉,才能删除该客户表中的张三记录。

  • SET_NULL:删除主键记录,并且将外键记录中外键字段的值置为null

前提是外键字段要设置为值允许是null。

比如,我们要删除客户张三时,在删除了客户张三记录同时,会将Order表里面所有的 张三记录里面的customer字段值置为 null。 但是上面我们并没有设置 customer 字段有 null=True 的参数设置,所以,是不能取值为 SET_NULL的。


定义完之后,还是老方法,在根目录cmd执行:

  1. python manage.py makemigrations common
  2. python manage.py migrate
    在这里插入图片描述
    数据库中就多了两个表,

注意:

  • 定义的时候如果没有声明主键,Django会自动多一个id字段作为主键
  • 可以看到common_order表中的customer_id字段,比定义的时候多了一个_id,因为这是作为外键和common_customer表的主键id关联了

一对一:

外键是一对多的关系,有的时候,表之间是一对一的关系。

比如,某个学校的学生表和学生的地址表,就形成一对一的关系,即一条主键所在表的记录只能对应一条外键所在表的记录

Django中用OneToOneField()方法实现一对一的关系,如下:

class Student(models.Model):
    # 姓名
    name = models.CharField(max_length=200)
    # 班级
    classname = models.CharField(max_length=200)
    # 描述
    desc = models.CharField(max_length=200)


class ContactAddress(models.Model):
    # 一对一 对应学生表 
    student = models.OneToOneField(Student, on_delete=models.PROTECT)
    # 家庭
    homeaddress = models.CharField(max_length=200)
    # 电话号码
    phone = models.CharField(max_length=200)

Django发现这样一对一定定义,它会在migrate的时候,在数据库中定义该字段为外键的同时, 加上unique=True约束(在底层数据库实现),表示在此表中,所有记录的该字段取值必须唯一,不能重复。


多对多:

数据库表还有一种多对多的关系。

在当前系统中, 一个订单可以采购多种药品,就对应Medicine表里面的多种药品;
而一种药品也可以被多个订单采购, 那么Order表Medicine表 之间就形成了多对多的关系。

Django是通过 ManyToManyField 对象 表示 多对多的关系。
修改models.py中的Order类:

class Order(models.Model):
    # 订单名
    name = models.CharField(max_length=200, null=True, blank=True)

    # 创建日期
    create_date = models.DateTimeField(default=datetime.datetime.now)

    # 客户
    customer = models.ForeignKey(Customer, on_delete=models.PROTECT)

    # 订单购买的药品,和Medicine表是多对多的关系
    medicines = models.ManyToManyField(Medicine, through='OrderMedicine')


class OrderMedicine(models.Model):
    order = models.ForeignKey(Order, on_delete=models.PROTECT)
    medicine = models.ForeignKey(Medicine, on_delete=models.PROTECT)

    # 订单中药品的数量
    amount = models.PositiveIntegerField()

像这样:

medicines = models.ManyToManyField(Medicine, through='OrderMedicine')

指定Order表和 Medicine 表的多对多关系, 其实Order表中并不会产生一个叫 medicines 的字段。

Order表和 Medicine 表的多对多关系是通过另外一张表, 也就是through参数 指定的 OrderMedicine表来确定的,

migrate的时候,Django会自动产生一张新表 (这里就是 common_ordermedicine)来 实现 order 表 和 medicine 表之间的多对多的关系。

同样的,在根目录cmd执行:

  1. python manage.py makemigrations common
  2. python manage.py migrate

可以看到数据库中多了一张common_ordermedicine表,用来实现订单表和药品表的多对多关系。

外键medicine_id关联了common_medicine表,外键order_id关联了common_order表
在这里插入图片描述
比如一个order表的订单id 为 1, 如果该订单中对应的药品有3种,它们的id分别 为 3,4,5。 那么就会有类似这样的这样3条记录在 common_order_medicine 表中。

order_idmedicine_id
13
14
15

因为要多一个字段,例如药品的数量,所以要用through='OrderMedicine',也就是说如果不需要额外字段的话,through可以省略

  • 0
    点赞
  • 0
    评论
  • 0
    收藏
  • 打赏
    打赏
  • 扫一扫,分享海报

参与评论 您还未登录,请先 登录 后发表或查看评论
©️2022 CSDN 皮肤主题:终极编程指南 设计师:CSDN官方博客 返回首页

打赏作者

cwtnice

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值