zoukankan      html  css  js  c++  java
  • django模型中的关系对应

    显然,关系数据库的力量在于将表相互关联。Django提供了定义三种最常见的数据库关系类型的方法:多对一,多对多和一对一。

    在说明之前,首先来理解一下这三个概念:

    多对一: 两个集合a,b;集合a中的多个元素对应集合a中的一个元素。

    多对对:两个集合a,b;集合a中的多个元素对应集合b中的多个元素。

    一对一:两个集合a,b;集合a中的每个元素对应结合b中的一个元素,一一对应的关系。

    理解了这三个关系,下面通过例子来说明。没有具体的查询使用,只是通过实例来说明这三种关系。

    多对一

    符合多对一的一种常见形式是员工与部门。多个员工是一个部门的。

    class Department(models.Model):
        dep_id = models.AutoField(primary_key=True)
        dep_name = models.CharField(max_length=40)


    class Employee(models.Model):
        emp_id = models.IntegerField(primary_key=True)
        emp_name = models.CharField(max_length=60)
        emp_date = models.DateTimeField(auto_now_add=True)
        emp_dep = models.ForeignKey(to="Department", to_field="dep_id")

    这个实例中员工表通过外键关联到部门表中。to指向被关联的表(这里就是部门表),to_field指向被关联表的字段(这个字段必须unique),默认是指向被关联表的主键。

    执行如下两条命令:

    python manage.py makemigrations 
    python manage.py migrate

    进入数据库中,查看生成的表:

    MariaDB [webinfo]> desc mysite_department;
    +----------+-------------+------+-----+---------+----------------+
    | Field    | Type        | Null | Key | Default | Extra          |
    +----------+-------------+------+-----+---------+----------------+
    | dep_id   | int(11)     | NO   | PRI | NULL    | auto_increment |
    | dep_name | varchar(40) | NO   |     | NULL    |                |
    +----------+-------------+------+-----+---------+----------------+
    2 rows in set (0.01 sec)
    
    MariaDB [webinfo]> desc mysite_employee;
    +------------+-------------+------+-----+---------+-------+
    | Field      | Type        | Null | Key | Default | Extra |
    +------------+-------------+------+-----+---------+-------+
    | emp_id     | int(11)     | NO   | PRI | NULL    |       |
    | emp_name   | varchar(60) | NO   |     | NULL    |       |
    | emp_date   | datetime    | NO   |     | NULL    |       |
    | emp_dep_id | int(11)     | NO   | MUL | NULL    |       |          
    +------------+-------------+------+-----+---------+-------+
    4 rows in set (0.00 sec)
    #外键生成的默认字段名为,类属性名加上“_id”.在django中使用的时候我们可以仍然使用emp_dep,但是在写原生SQL的时候需要加上后面"_id"。
    MariaDB [webinfo]>

    外键的一些参数说明:

    on_delete: 当被关联的对象删除时,外键的值该如何变化,有以下几个选项:

    models.CASCADE 此为默认值,级联删除,会删除关联数据。
    models.PROTECT 只要存在关联数据就不能删除,但是会弹出ProtectedError异常。
    models.SET_NULL 删除数据后关联字段设置为NULL,仅在该字段允许为null时可用(null=True)
    models.SET_DEFAULT:将外键字段设为默认值。只有当字段设置了default参数时,方可使用
    models.DO_NOTHING:什么也不做.
    models.SET():设置为一个传递给SET()的值或者一个回调函数的返回值。注意大小写

    limit_choice_to: 类似一个过滤的功能。

    该参数用于限制外键所能关联的对象,只能用于Django的ModelForm(Django的表单模块)和admin后台,对其它场合无限制功能。其值可以是一个字典、Q对象或者一个返回字典或Q对象的函数调用,如下例所示:

    staff_member = models.ForeignKey(
        User,
        on_delete=models.CASCADE,
        limit_choices_to={'is_staff': True},
    )
    
    这样定义,则ModelForm的staff_member字段列表中,只会出现那些is_staff=True的Users对象,这一功能对于admin后台非常有用。

    related_name:用于关联对象反向引用模型的名称.上面的实例就是从部门到员工的引用。通常情况下,这个参数我们可以不设置,Django会默认以模型的小写作为反向关联名,上面的反向关联名称对于部门来说就是“employee”。如果不想定义这个名称,可以赋值为“+”。related_name=“+”。

    related_query_name:反向关联查询名。

    db_constraint:

    默认情况下,这个参数被设为True,表示遵循数据库约束,这也是大多数情况下你的选择。如果设为False,那么将无法保证数据的完整性和合法性。在下面的场景中,你可能需要将它设置为False:

    • 有历史遗留的不合法数据,没办法的选择
    • 你正在分割数据表

    当它为False,并且你试图访问一个不存在的关系对象时,会抛出DoesNotExist 异常。

    多对多的关系

    多对多关系在数据库中也是非常常见的关系类型。如一个应用对应多台主机,一个主机对应多个应用。

    多对多关系需要一个位置参数:关联的对象模型。它的用法和外键多对一基本类似。

    在数据库后台,Django实际上会额外创建一张用于体现多对多关系的中间表。默认情况下,该表的名称是“多对多字段名+关联对象模型名+一个独一无二的哈希码”,例如‘author_books_9cdf4’,当然你也可以通过db_table选项,自定义表名。

        def __init__(self, to, related_name=None, related_query_name=None,
                     limit_choices_to=None, symmetrical=None, through=None,
                     through_fields=None, db_constraint=True, db_table=None,
                     swappable=True, **kwargs)
    
    和外键重复的参数不再说明:

    symmetrical: 有点不太理解,把官方文档定义放这里。

    Only used in the definition of ManyToManyFields on self. Consider the following model:
    
    from django.db import models
    
    class Person(models.Model):
        friends = models.ManyToManyField("self")
    
    When Django processes this model, it identifies that it has a ManyToManyField on itself, and as a result, it doesn’t add a person_set attribute
    to the Person class. Instead, the ManyToManyField is assumed to be symmetrical – that is, if I am your friend, then you are my friend. If you do not want symmetry in many-to-many relationships with self, set symmetrical to False. This will force Django to add the descriptor
    for the reverse relationship, allowing ManyToManyField relationships to be non-symmetrical.

    through:定义中间表。django会自动生成一张表来管理多对多关系的表,但是,如果要手动指定中间表,可以使用该through选项指定表示要使用的中间表的Django模型。

    通常情况下这个表有三个字段如下:

    id: 中间表的id字段。
    模型对象的名+"_id",
    被关联模型的名+"_id",
    
    例如上面的中间表字段名为:
    MariaDB [webinfo]> desc  mysite_application_r;
    +----------------+---------+------+-----+---------+----------------+
    | Field          | Type    | Null | Key | Default | Extra          |
    +----------------+---------+------+-----+---------+----------------+
    | id             | int(11) | NO   | PRI | NULL    | auto_increment |
    | application_id | int(11) | NO   | MUL | NULL    |                |
    | host_id        | int(11) | NO   | MUL | NULL    |                |
    +----------------+---------+------+-----+---------+----------------+
    3 rows in set (0.00 sec)

    through_fields
    : 仅在指定自定义中间表模型时使用。通过官网中的一个实例来说明吧:
    from django.db import models
    
    class Person(models.Model):
        name = models.CharField(max_length=50)
    
    class Group(models.Model):
        name = models.CharField(max_length=128)
        members = models.ManyToManyField(
            Person,
            through='Membership',
            through_fields=('group', 'person'),
        )
    
    class Membership(models.Model):
        group = models.ForeignKey(Group, on_delete=models.CASCADE)
        person = models.ForeignKey(Person, on_delete=models.CASCADE)
        inviter = models.ForeignKey(
            Person,
            on_delete=models.CASCADE,
            related_name="membership_invites",
        )
        invite_reason = models.CharField(max_length=64)

    #这个实例中自定义了中间表Membership,但是因为有两个与person相关的外键,django无法判断使用哪一个,因此这时候就必须指定through_fields
    #through_fields是一个二维元组(field1, field2),其中field1表示发起多对对关系的模型(group),field2表示多对多关系的目标表(person)。

    db_table:设置中间表的名称。不指定的话,则使用默认值。

    db_constraint:参考外键的相同参数

    ManyToManyField多对多字段不支持Django内置的validators验证功能。

    null参数对ManyToManyField多对多字段无效!设置null=True毫无意义

    一对一

    A one-to-one relationship. Conceptually, this is similar to a ForeignKey with unique=True, but the “reverse” side of the relation will directly return a single object.

    一对一关系,最有用的就是扩展另一个模型。例如子表通过一对一关联到父表,达到对父表扩充的目的。

    from django.conf import settings
    from django.db import models
    
    class MySpecialUser(models.Model):
        user = models.OneToOneField(
            settings.AUTH_USER_MODEL,
            on_delete=models.CASCADE,
        )
        supervisor = models.OneToOneField(
            settings.AUTH_USER_MODEL,
            on_delete=models.CASCADE,
            related_name='supervisor_of',
        )

    #如果你没有给一对一关系设置related_name参数,Django将使用当前模型的小写名作为默认值。

    OneToOneField一对一关系拥有和多对一外键关系一样的额外可选参数,只是多了一个parent_link参数。

  • 相关阅读:
    mysql备份还原
    java-mysql(3) 读写image
    java-mysql(2) Prepared statement
    java-mysql(1)
    jmeter报告分析工具
    浏览器下载img标签Base64图片
    Java定时器TimeTask
    js倒计时
    h5语音播放(移动端)
    Linux环境下在Tomcat上部署JavaWeb工程
  • 原文地址:https://www.cnblogs.com/wxzhe/p/10304988.html
Copyright © 2011-2022 走看看