为模型中的外键字段设置默认值的最佳方法是什么?假设我有两个模型,学生和考试,学生将考试作为外键。我如何理想地为它设置默认值?这是我的努力记录
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开始,但在同步时失败并且表格不存在错误。
任何帮助将不胜感激。
答案 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]]
这也允许我使用当前用户。
答案 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')