Pri*_*han 57 django default foreign-keys
为模型中的外键字段设置默认值的最佳方法是什么?假设我有两个模型,学生和考试,学生将考试作为外键.我如何理想地为它设置默认值?这是我的努力记录
class Student(models.Model):
....
.....
exam_taken = models.ForeignKey("Exam", default=1)
Run Code Online (Sandbox Code Playgroud)
工作,但有一个预感有一个更好的方法.
def get_exam():
return Exam.objects.get(id=1)
class Student(models.Model):
....
.....
exam_taken = models.ForeignKey("Exam", default=get_exam)
Run Code Online (Sandbox Code Playgroud)
从这里开始,但在同步时失败并且表不存在错误.
任何帮助,将不胜感激.
Gar*_*eth 35
在这两个示例中,您都是对默认实例的id进行硬编码.如果这是不可避免的,我只会设定一个常数.
DEFAULT_EXAM_ID = 1
class Student(models.Model):
...
exam_taken = models.ForeignKey("Exam", default=DEFAULT_EXAM_ID)
Run Code Online (Sandbox Code Playgroud)
更少的代码,并命名常量使其更具可读性.
vau*_*ult 11
我使用自然键采用更自然的方法:
<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')
Run Code Online (Sandbox Code Playgroud)
Tan*_*kom 11
大多数这些方法的问题是它们在模型中使用硬编码值或 lambda 方法,自 Django 版本 1.7 以来不再支持这些方法。
在我看来,这里最好的方法是使用哨兵方法,该方法也可以用于论证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
)
Run Code Online (Sandbox Code Playgroud)
现在,当您刚刚添加该exam_taken字段时,该字段将使用有保证的现有值,而在删除考试时,学生本身不会被删除,并且具有指向值的外键deleted。
我会在上面略微修改@vault的答案(这可能是一个新功能)。绝对希望使用自然名称来引用该字段。但是,除了覆盖ManagerI之外,我还只是使用以下to_field参数ForeignKey:
class Country(models.Model):
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, to_field='sigla', default='IT')
Run Code Online (Sandbox Code Playgroud)
在我的例子中,我想将默认设置为相关模型的任何现有实例.因为可能已删除Examwith id 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)
Run Code Online (Sandbox Code Playgroud)
如果exam_taken不存在,django.db.models.fields.related_descriptors.RelatedObjectDoesNotExist则在尝试访问它时将引发.
正如@gareth 的回答中已经暗示的那样,硬编码默认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)
Run Code Online (Sandbox Code Playgroud)
这里一个Exam.title字段用于获取唯一对象,一个Exam.description字段说明了我们如何使用defaults参数 (for get_or_create) 来完全指定默认Exam对象。
请注意,我们pk按照文档的建议返回 a :
对于
ForeignKey映射到模型实例的字段,默认值应该是它们引用的字段的值(pk除非to_field设置)而不是模型实例。
另请注意,default可调用对象在Model.__init__()( source )中进行评估。因此,如果您的默认值取决于同一模型的另一个字段,或者取决于请求上下文,或者取决于客户端表单的状态,那么您可能应该查看其他地方。
| 归档时间: |
|
| 查看次数: |
56978 次 |
| 最近记录: |