设置外键属性的默认值

时间:2012-02-16 13:20:44

标签: django foreign-keys default

为模型中的外键字段设置默认值的最佳方法是什么?假设我有两个模型,学生和考试,学生将考试作为外键。我如何理想地为它设置默认值?这是我的努力记录

class Student(models.Model):
   ....
   .....
   exam_taken = models.ForeignKey("Exam", default=1)

有效,但预感有更好的方法。

def get_exam():
    return Exam.objects.get(id=1)

class Student(models.Model):
    ....
    .....
    exam_taken = models.ForeignKey("Exam", default=get_exam)

here开始,但在同步时失败并且表格不存在错误。

任何帮助将不胜感激。

9 个答案:

答案 0 :(得分:27)

在两个示例中,您都是对默认实例的ID进行硬编码。如果这是不可避免的,我只需设置一个常量。

DEFAULT_EXAM_ID = 1
class Student(models.Model):
    ...
    exam_taken = models.ForeignKey("Exam", default=DEFAULT_EXAM_ID)

更少的代码,并命名常量使其更具可读性。

答案 1 :(得分:8)

我使用自然键采用更自然的方法:

<app>/models.py

from django.db import models

class CountryManager(models.Manager):
    """Enable fixtures using self.sigla instead of `id`"""

    def get_by_natural_key(self, sigla):
        return self.get(sigla=sigla)

class Country(models.Model):
    objects = CountryManager()
    sigla   = models.CharField(max_length=5, unique=True)

    def __unicode__(self):
        return u'%s' % self.sigla

class City(models.Model):
    nome   = models.CharField(max_length=64, unique=True)
    nation = models.ForeignKey(Country, default='IT')

答案 2 :(得分:5)

就我而言,我想将默认设置为相关模型的任何现有实例。由于ID Exam的{​​{1}}可能已被删除,我已完成以下操作:

1

如果class Student(models.Model): exam_taken = models.ForeignKey("Exam", blank=True) def save(self, *args, **kwargs): try: self.exam_taken except: self.exam_taken = Exam.objects.first() super().save(*args, **kwargs) 不存在,则在尝试访问时会引发exam_taken

答案 3 :(得分:4)

您可以使用此模式:

class Other(models.Model):
    DEFAULT_PK=1
    name=models.CharField(max_length=1024)

class FooModel(models.Model):
    other=models.ForeignKey(Other, default=Other.DEFAULT_PK)

当然,您需要确保Other表格中有一行。您应该使用数据迁移来确保它存在。

答案 4 :(得分:4)

@gareth的answer中已经暗示,对默认的id值进行硬编码可能并不总是最好的主意:

如果数据库中不存在id值,则您有麻烦。即使确实存在特定的id值,相应的对象也可能会更改。无论如何,在使用硬编码的id值时,您都不得不求助于数据迁移或手动编辑现有数据库内容的事情。

为防止这种情况,可以将get_or_create()unique字段(id除外)结合使用。

这是我要怎么做:

from django.db import models

class Exam(models.Model):
    title = models.CharField(max_length=255, unique=True)
    description = models.CharField(max_length=255)

    @classmethod
    def get_default_pk(cls):
        exam, created = cls.objects.get_or_create(
            title='default exam', defaults=dict(description='this is not an exam'))
        return exam.pk


class Student(models.Model):
    exam_taken = models.ForeignKey(to=Exam, on_delete=models.CASCADE,
                                   default=Exam.get_default_pk)

这里有一个Exam.title字段用于获取唯一对象,而Exam.description字段说明了如何使用defaults参数(对于get_or_create)来完全指定默认的Exam对象。

请注意,我们根据docs的建议返回了pk

对于像ForeignKey这样的映射到模型实例的字段,默认值应该是它们引用的字段的值(除非设置了pk,否则应为to_field)而不是模型实例。

还要注意,default可调用项在Model.__init__()source)中求值。因此,如果您的默认值取决于同一模型的另一个字段,请求上下文或客户端表单的状态,则可能应该查看elsewhere

答案 5 :(得分:3)

我会在上面略微修改@vault的答案(这可能是一个新功能)。绝对希望使用自然名称来引用该字段。但是,我不会使用Manager的{​​{1}}参数,而不是覆盖to_field

ForeignKey

答案 6 :(得分:1)

大多数这些方法的问题是它们在模型中使用了硬编码值或 lambda 方法,这些方法自 Django 1.7 版起不再受支持。

在我看来,这里最好的方法是使用 sentinel 方法,该方法也可用于 on_delete 参数。

所以,在你的情况下,我会这样做

# Create or retrieve a placeholder
def get_sentinel_exam():
    return Exam.objects.get_or_create(name="deleted",grade="N/A")[0]

# Create an additional method to return only the id - default expects an id and not a Model object
def get_sentinel_exam_id():
    return get_sentinel_exam().id

class Exam(models.Model):
    ....
    # Making some madeup values
    name=models.CharField(max_length=200) # "English", "Chemistry",...
    year=models.CharField(max_length=200) # "2012", "2022",...

class Student(models.Model):
    ....
    .....
    exam_taken = models.ForeignKey("Exam",    
                       on_delete=models.SET(get_sentinel_exam),
                       default=get_sentinel_exam_id
                 )

现在,当您刚刚添加 exam_taken 字段时,会使用有保证的现有值,同时,在删除考试时,学生本身不会被删除,并且具有指向 deleted 值的外键。< /p>

答案 7 :(得分:0)

我正在 Django Admin 中寻找解决方案,然后发现了这一点:

donor = Y
flags = {paidFinesEligible=false, hasRealId=false, suspensionEligible=false, acaaEligible=false, eligibleIgnoreRenewalDate=false, eligibleDocuments=false, cardStatusEligible=false, expirationDateEligible=false, eligible=false, citizenEligible=false, pointSystemEligible=false, ageEligible=false, gravamenesEligible=false, categoryEligible=false, eligibleMedical=false}
failedDocuments = [FailedDocument[type=CERTIFICATE_CITIZENSHIP, reason=MISSING]]

这也允许我使用当前用户。

see django docs

答案 8 :(得分:0)

我知道的最佳方法是使用lambdas

class TblSearchCase(models.Model):
    weights = models.ForeignKey('TblSearchWeights', models.DO_NOTHING, default=lambda: TblSearchWeights.objects.get(weight_name='value_you_want'))

因此您可以指定默认行。

default=lambda: TblSearchWeights.objects.get(weight_name='value_you_want')