问题描述
我想在 ModelForm 中使用一个简单的外键关系,但没有 ModelChoiceField。
class Sample(models.Model):
alt = IntegerField(db_index=True,unique=True)
class Assignment(models.Model):
sample = models.ForeignKey(Sample,on_delete=models.CASCADE)
我想让 AssignmentForm 根据样本的 alt 字段的内容选择样本。使用 ModelChoiceField 会是这样:
class SampleSelect(ModelChoiceField):
def label_from_instance(self,obj):
return obj.alt
class AssignmentForm(ModelForm):
sample = SampleSelect(queryset=Sample.objects.all())
class Meta:
model = Assignment
fields = ['sample']
ModelChoiceField documentation 表示如果选择的数量很大,请使用其他内容。
允许选择单个模型对象,适合表示外键。请注意,当条目数量增加时,ModelChoiceField 的默认小部件变得不切实际。您应该避免将它用于超过 100 个项目。
class SampleBAltField(IntegerField):
def clean(self,value):
try:
return Sample.objects.get(alt=value)
except Sample.DoesNotExist:
raise ValidationError(f'Sample with alt {value} does not exist')
此现有代码应从表单中获取一个整数并将其映射回外键,但我无法弄清楚要覆盖哪些内容以填充来自 Sample 实例的绑定表单的字段。
ModelForm 中的 FormFields 有没有比较简单的方法可以解决这个问题,还是我需要从头开始编写 Form?
解决方法
ModelChoiceField 文档说,如果选择的数量很大,请使用其他东西。
文档建议使用不同的小部件(否则用户将不得不从包含过多项目的下拉列表中进行选择),但您不一定需要完全不同的字段。
如果你希望绑定表单的字段是 Sample
的 n 个实例,那么 ModelChoiceField
仍然是合适的。
为避免文档中预期的问题,您只需更改该字段的小部件即可。您可能需要确切地决定那是什么。一个简单的选择是使用 NumberInput
小部件,用户只需在其中输入一个整数作为外键。
from django.forms.widgets import NumberInput
class AssignmentForm(ModelForm):
sample = ModelChoiceField(queryset=Sample.objects.all(),widget=NumberInput)
class Meta:
model = Assignment
fields = ['sample']
根据样本的alt字段内容选择样本
您在这里想要的是与您从文档中引用的内容不同的问题。您可以选择在更改或不更改小部件的情况下实现此功能。
如果您希望用户提供 alt
值而不是示例的主键,您可以使用 to_field_name
参数作为 ModelChoiceField
(注意这仅适用于此处,因为您的 alt
字段是唯一的)
class AssignmentForm(ModelForm):
sample = ModelChoiceField(
queryset=Sample.objects.all(),widget=NumberInput,help_text="Enter the alt of the sample",to_field_name='alt'
)
class Meta:
model = Assignment
fields = ["sample"]
为了在呈现绑定表单时正确呈现初始值,您可以在实例化绑定表单时提供 initial
关键字参数:
form = AssignmentForm(instance=inst,initial={'sample': inst.sample.alt})
或者,您可以覆盖表单的 __init__
方法以在表单实例化时自动执行此操作:
class AssignmentForm(ModelForm):
...
def __init__(self,*args,**kwargs):
super().__init__(*args,**kwargs)
if self.instance.pk:
self.initial.update({'sample': self.instance.sample.alt})
,
您要寻找的是自定义小部件,而不是字段本身。
Django Admin 有类似的东西。它被称为“原始 ID 字段”小部件。您可以通过在注册的 admin.py
上设置 raw_id_fields = ['field_name']
在 admin.ModelAdmin
中进行配置来尝试。
您可以在 admin 之外使用它,但这需要向您的应用程序添加一些 Django Admin 的 JS。
或者,如果你需要更简单的东西,你可以复制这个想法。
,如果您没有在其他实例中使用示例模型,其中示例通过模板中的 id 或名称显示,您可以只向模型添加一个 str 方法,以允许它在自定义庄园中显示.
class Sample(models.Model):
alt = IntegerField(db_index=True,unique=True)
def __str__(self):
return f"{self.alt}"
现在,每当您调用模型实例时,它都会显示在 alt 字段中。这也适用于 Django 管理中的模型视图。