Django 模型:选择外键的默认值

Posted

技术标签:

【中文标题】Django 模型:选择外键的默认值【英文标题】:Django Models: Default for Choice Foreign Key 【发布时间】:2019-10-20 18:54:49 【问题描述】:

我有一个 Type 模型类如下:

class Type(models.Model):
    ENVIRONMENT = 'A'
    HUMANENV = 'B'
    HUMAN = 'C'
    ANIMAL = 'D'
    UNKNOWN = 'H'
    TYPE_CHOICES = [
        (ENVIRONMENT, 'Environment'),
        (HUMANENV, "Human-related Environment"),
        (HUMAN, 'Human'),
        (ANIMAL, 'Animal'),
        (UNKNOWN, 'Unknown'),
    ]
    code = models.CharField(max_length=1, choices=TYPE_CHOICES, unique=True)

    class Meta:
        ordering = ['code']

    def __str__(self):
        return self.get_code_display()

还有另一个 Sample 模型,其中一个字段是 Type 模型的外键,如下所示:

class Sample(models.Model):
    sample_id = models.CharField(max_length=20, unique=True)
    type = models.ForeignKey("Type", on_delete=models.CASCADE, blank=True, default=get_default_type())

    class Meta:
        ordering = ["sample_id"]

    def __str__(self):
        return self.sample_id

其中 get_default_type 是返回默认 Type 模型实例的 pk 的函数:

def get_default_type():
    return Type.objects.get(code="H").id

问题是当我运行 Sample.objects.create(sample_id="some_id") 时,它给了我错误

IntegrityError: null value in column "type_id" violates not-null constraint
DETAIL:  Failing row contains (28113, some_id, null).

正如您在错误消息的第二行中看到的,type_id 为空,而不是 get_default_type 函数返回的 pk。

我尝试为外键设置 null=True,当我这样做时,我能够创建 Sample 模型实例,但使用 None 类型而不是我想要的 Unknown 类型。我该如何解决这个问题?

【问题讨论】:

在我看来,您根本没有 Type object'H'。毕竟,并不是因为code 是一个选择,所以根本没有一个Type object 具有该值。 @WillemVanOnsem 不幸的是,我检查了一个值为“H”的 Type 对象。 尝试将default=get_default_type()) 更改为default=get_default_type ) 此处回答:***.com/a/12654998/842935 @daniherrera 我这样做时收到了相同的错误消息。 default=get_default_type 应该可以工作(不带括号)。进行更改后是否重新启动服务器(并重新运行makemigrations/migrate?) 【参考方案1】:

两种解决方案:

覆盖管理器

从这个response,您可以在您的经理中使用get_by_natural_key

managers.py

from django.db import models
class TypeManager(models.Manager):
    """Enable fixtures using self.sigla instead of `id`"""

    def get_by_natural_key(self, code):
        return self.get(code=code)
class Type(models.Model):
    #.... Declare your model here
    objects = Type()

或者...

改变你的PK!

class Type(models.Model):
    #.... Declare your model here
    code = models.CharField(max_length=1, choices=TYPE_CHOICES, unique=True, primary_key=True)

无论哪种方式,在您的相关模型声明中:

class Sample(models.Model):
    type = models.ForeignKey("Type", on_delete=models.CASCADE, blank=True, default='H')

    class Meta:
        ordering = ["sample_id"]

    def __str__(self):
        return self.sample_id

附带说明:请注意type。 它是受保护的关键字,不应使用。

【讨论】:

我收到 ValueError: invalid literal for int() with base 10: 'H' when I run migrate? 我也不明白我设置默认值有什么问题?它实际上适用于其他不涉及选择的外键,但由于原因在这种特殊情况下不起作用。

以上是关于Django 模型:选择外键的默认值的主要内容,如果未能解决你的问题,请参考以下文章

从没有外键的两个模型的 JOIN 中检索值(Django)

具有默认值的外键上的 Django 1.7 迁移错误

外键的默认值与 null

设置外键的默认值?

8. EF Core 外键的删除模式

主键约束,外键约束,空值约束,默认值约束,唯一约束,检查约束的各个作用是啥?