Django关系类型字段

作者: 世外大帝 | 来源:发表于2018-01-17 14:41 被阅读5次

    简述

    本文一共三大类,分别是

    • 多对一(外键ForeignKey)
    • 多对多(ManyToManyField)
    • 一对一(OneToOneField)

    其中用的最多的一般是外键,也是本文重点,如果你正在学习Django或者准备学习Django,本文将对你至关重要,请仔细阅读,没有需要略过的地方

    外键ForeignKey

    class ForeignKey(to, on_delete, **options)

    • to:关联的模型
    • on_delete: 关联删除(2.0后必填)

    常规使用

    from django.db import models
    
    # 如果要关联的对象在另外一个app中,可以显式的指出
    # 如CreateCar在appone中,则appone.CreateCar
    class Car(models.Model):
        create_car = models.ForeignKey(
            'CreateCar',  # here
            on_delete=models.CASCADE,
        )
    
    class CreateCar(models.Model):
        pass
    

    自身递归

    # 自身递归外键可以用self (评论系统常用)
    class Comment(models.Model):
        title = models.CharField(max_length=128)
        text = models.TextField()
        parent_comment = models.ForeignKey('self', on_delete=models.CASCADE)
    

    在实际的数据库后台,Django会为每一个外键添加_id后缀,并以此创建数据表里的一列。

    重要参数

    这块之所以要单独列一栏,是因为在关系类型的字段中,大多是一样的,这列主要是外键参数,在另外的多对多和一对一中,有很多相同的参数,所以在后续的两栏中只列举与此参数组不同的部分

    on_delete

    当外键关联对象被删除时,Django将按照on_delete的约束执行相应操作。

    """
    你有一个可为空的外键
    你想让它在关联的对象被删除时,自动设为null
    其实较常用的还是on_delete=models.CASCADE
    """
    user = models.ForeignKey(
        User,
        models.SET_NULL,
        blank=True,
        null=True,
    )
    

    该参数常量可以在django.db.models.deletion.py中看到所有常量

    • CASCADE:模拟SQL语言中的ON DELETE CASCADE约束,将定义有外键的模型对象同时删除!(默认操作,且最常用)
    • PROTECT:阻止删除操作,执行删除时,会抛出ProtectedError异常
    • SET_NULL:将外键字段设为null只有当字段设置了null=True时,方可使用该值
    • SET_DEFAULT:将外键字段设为默认值。只有当字段设置了default参数时,方可使用
    • DO_NOTHING:啥都不干
    • SET():设置为一个传递给SET()的值或者一个回调函数的返回值。
    from django.conf import settings
    from django.contrib.auth import get_user_model
    from django.db import models
    
    def get_sentinel_user():
        return get_user_model().objects.get_or_create(username='deleted')[0]
    
    class MyModel(models.Model):
        user = models.ForeignKey(
            settings.AUTH_USER_MODEL,
            on_delete=models.SET(get_sentinel_user),
        )
    

    limit_choices_to

    该参数用于限制外键所能关联的对象,只能用于DjangoModelForm(表单模块)和admin后台,对其它场合无限制功能。

    其值可以是一个dictQ对象(参见Q以及F)或者一个返回dictQ()的函数调用,如下例所示:

    # employee列中只显示is_employee=True的Users对象
    # 这一功能对于admin后台非常有用。
    employee = models.ForeignKey(
        User,
        on_delete=models.CASCADE,
        limit_choices_to={'is_employee': True},
    )
    

    related_name

    用于关联对象反向引用模型的名称。默认为None

    用Car和CreateCar的例子解释,就是从CreateCar反向关联Car的关系名称。

    这个参数可以不设置,通常情况下,Django会默认以模型的小写作为反向关联名

    如果你不想为外键设置一个反向关联名称,可以将这个参数设置以+结尾

    user = models.ForeignKey(
        User,
        on_delete=models.CASCADE,
        related_name='+',
    )
    

    related_query_name

    反向关联查询。

    用于从目标模型反向过滤模型对象的名称。

    class Tag(models.Model):
        article = models.ForeignKey(
            Article,
            on_delete=models.CASCADE,
            related_name="tags",
            related_query_name="tag",
        )
        name = models.CharField(max_length=255)
    
    # 现在可以使用‘tag’作为查询名了
    Article.objects.filter(tag_name="important")
    

    to_field

    默认情况下,外键都是关联到被关联对象的主键上。

    如果指定to_field参数,可以关联到指定的字段上,但是该字段必须具有唯一性(unique=True属性)

    db_constraint

    遵从数据库约束,默认值为True

    如果设为False,那么将无法保证数据的完整性和合法性。

    只有在特殊情况下,才可能需要将它设置为False

    • 有历史遗留的不合法数据
    • 你正在分割数据表

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

    多对多ManyToManyField

    多对多关系在数据库中也是非常常见的关系类型。

    比如一本书可以有好几个作者,一个作者也可以写好几本书。

    多对多的字段可以定义在任何的一方,请尽量定义在符合人们思维习惯的一方,但不要同时都定义。

    多对多关系需要一个位置参数,用来关联的对象模型。它的用法和外键差不多

    class ManyToManyField(to, **options)

    在数据库后台,Django实际上会额外创建一张用于体现多对多关系的中间表

    • 默认情况下,该表的名称是字段名 + 关联对象模型 + hashCode,如author_books_9cdf4
    • 可以通过db_table选项,自定义表名。

    多对多参数说明

    参考外键参数部分。

    related_name
    related_query_name
    db_constraint
    

    特别说明

    • ManyToManyField不支持Django内置的validators验证功能。
    • null参数对ManyToManyField无效!设置null=True毫无意义
    • swappable 如果当前外键指向一个可交换的模型,可以控制迁移框架的动作。默认为True,几乎用不到
    • limit_choices_to对于使用through参数自定义中间表的多对多字段无效, 其他参考外键参数

    symmetrical

    默认情况下,Django中的多对多关系是对称的,从这个参数的名称就能看出来,意为“对称”。

    from django.db import models
    
    class Person(models.Model):
        friends = models.ManyToManyField("self")
    

    Django认为,如果我是你的朋友,那么你也是我的朋友,这是一种对称关系

    Django不会为Person模型添加person_set属性用于反向关联。如果你不想使用这种对称关系,可以将symmetrical设置为False(强制Django为反向关联添加描述符)。

    through

    定义中间表,特别说明中的limit_choices_to
    参数,除了这个,其他都参照外键参数,就说明through参数是多对多特有的,而且在多对多关系中至关重要。

    如果你想自定义多对多关系中额外的关联表,可以使用这个参数!参数值为中间模型。

    # 该表在数据库中的结构
    中间表的id列...模型对象的id列...被关联对象的id列
    
    
    # 中间表添加字段的结构
    中间表的id列...模型对象的id列...被关联对象的id列...定义的字段对象列
    

    demo

    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'),  # 参数说明见下方
        )
        
        
    # 定义中间表模型
    # 用来保存Person和Group模型的多对多关系
    # 同时增加了‘邀请人’和‘邀请原因’的字段。
    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)
    

    through_fields

    Membership模型中包含两个关联Person的外键,Django无法确定到底使用哪个作为和Group关联的对象。所以,在这个例子中,必须显式的指定through_fields参数,用于定义关系。

    through_fields参数接收一个元组('field1', 'field2')

    • field1:指向定义有多对多关系的模型的外键字段的名称,这里是Membership中的group字段,
    • field2指向目标模型的外键字段的名称,这里是Membership中的person,而不是inviter

    再通俗的说,就是through_fields参数指定从中间表模型Membership中选择哪两个字段,作为关系连接字段。

    db_table

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

    一对一OneToOneField

    class OneToOneField(to, on_delete, parent_link=False, **options)[source]

    从概念上讲,一对一关系非常类似具有unique=True属性的外键关系,但是反向关联对象只有一个。

    这种关系类型多数用于当一个模型需要从别的模型扩展而来的情况。比如,Django自带auth模块的User用户表,如果你想在自己的项目里创建用户模型,又想方便的使用Django的认证功能,那么一个比较好的方案就是在你的用户模型里,使用一对一关系,添加一个与auth模块User模型的关联字段。

    该关系的to参数为关联的模型,其用法和前面的多对一外键一样。

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

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

    from django.conf import settings
    from django.db import models
    
    # 两个字段都使用一对一关联到了Django内置的auth模块中的User模型
    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',
        )
    

    这样下来,你的User模型将拥有下面的属性

    >>> user = User.objects.get(pk=1)
    >>> hasattr(user, 'myspecialuser')
    True
    >>> hasattr(user, 'supervisor_of')
    True
    

    跨模块的模型

    对于跨模块的模型,导入即可使用

    相关文章

      网友评论

        本文标题:Django关系类型字段

        本文链接:https://www.haomeiwen.com/subject/rthuoxtx.html