我如何在选择一个ForeignKey
字段时改变<select>
字段中的显示文本?
我需要显示不仅是ForeignKey
的名称,还需要其父项的名称。
我如何在选择一个ForeignKey
字段时改变<select>
字段中的显示文本?
我需要显示不仅是ForeignKey
的名称,还需要其父项的名称。
如果您想使它只在管理员中生效,而不是全局生效,那么您可以创建一个自定义的 ModelChoiceField
子类,在自定义的 ModelForm
中使用它,然后将相关的管理员类设置为使用您定制的表单。
使用一个具有对 @Enrique 使用的 Person
模型的 ForeignKey 的示例:
class Invoice(models.Model):
person = models.ForeignKey(Person)
....
class InvoiceAdmin(admin.ModelAdmin):
form = MyInvoiceAdminForm
class MyInvoiceAdminForm(forms.ModelForm):
person = CustomModelChoiceField(queryset=Person.objects.all())
class Meta:
model = Invoice
class CustomModelChoiceField(forms.ModelChoiceField):
def label_from_instance(self, obj):
return "%s %s" % (obj.first_name, obj.last_name)
另一种做法(当您更改查询集时很有用):
class MyForm(forms.Form):
def __init__(self, *args, **kwargs):
super(MyForm, self).__init__(*args, **kwargs)
self.fields['user'].queryset = User.objects.all()
self.fields['user'].label_from_instance = lambda obj: "%s %s" % (obj.last_name, obj.first_name)
'user'
是您想要覆盖的字段名称。这种解决方案有一个优点:您还可以覆盖查询集(例如,您想要 User.objects.filter(username__startswith='a')
)。
免责声明:该解决方案来源于http://markmail.org/message/t6lp7iqnpzvlt6qp并经过测试。
较新版本的Django支持此功能,可以使用gettext进行翻译:
models.ForeignKey(ForeignStufg, verbose_name='your text')
您还可以直接从admin.ModelAdmin
实例中使用label_from_instance
来完成此操作。例如:
class InvoiceAdmin(admin.ModelAdmin):
list_display = ['person', 'id']
def get_form(self, request, obj=None, **kwargs):
form = super(InvoiceAdmin, self).get_form(request, obj, **kwargs)
form.base_fields['person'].label_from_instance = lambda inst: "{} {}".format(inst.id, inst.first_name)
return form
admin.site.register(Invoice, InvoiceAdmin)
请参考https://docs.djangoproject.com/en/1.3/ref/models/instances/#unicode
class Person(models.Model):
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
def __unicode__(self):
return u'%s %s' % (self.first_name, self.last_name)
在您的模型的unicode方法中(外键在哪里),您需要定义要显示的内容。
祝好!
在其他答案的基础上,这里给那些处理ManyToManyField
的人提供一个小例子。
我们可以直接在formfield_for_manytomany()中覆盖label_from_instance
:
class MyAdmin(admin.ModelAdmin):
def formfield_for_manytomany(self, db_field, request, **kwargs):
formfield = super().formfield_for_manytomany(db_field, request, **kwargs)
if db_field.name == 'person':
# For example, we can add the instance id to the original string
formfield.label_from_instance = lambda obj: f'{obj} ({obj.id})'
return formfield
formfield_for_foreignkey()
或 formfield_for_dbfield()。__str__()
方法将被调用,以生成对象的字符串表示形式,以供字段的选项使用。要提供自定义表示形式,请子类化ModelChoiceField
并覆盖label_from_instance
。该方法将接收一个模型对象,并应返回一个适合表示它的字符串。@admin.register(MyModel)
class MyModelAdmin(admin.ModelAdmin):
def formfield_for_foreignkey(self, db_field, request, **kwargs):
formfield = super().formfield_for_foreignkey(db_field, request, **kwargs)
if db_field.name == "my_field":
formfield.label_from_instance = lambda obj: f'{obj} ({obj.my_parent})'
return formfield
class MyModelForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['my_field'].label_from_instance = lambda obj: f'{obj} ({obj.my_parent})'
@admin.register(MyModel)
class MyModelAdmin(admin.ModelAdmin):
form = MyModelForm
而且,下面的代码使用了自定义的"forms.ModelChoiceField"和"forms.ModelForm":
class CustomModelChoiceField(forms.ModelChoiceField):
def label_from_instance(self, obj):
return f'{obj} ({obj.my_parent})'
class MyModelForm(forms.ModelForm):
my_field = CustomModelChoiceField(queryset=MyField.objects.all())
@admin.register(MyModel)
class MyModelAdmin(admin.ModelAdmin):
form = MyModelForm
class InvoiceAdmin(admin.ModelAdmin):
class CustomModelChoiceField(forms.ModelChoiceField):
def label_from_instance(self, obj):
return "%s %s" % (obj.first_name, obj.last_name)
def formfield_for_foreignkey(self, db_field, request, **kwargs):
if db_field.name == 'person':
return self.CustomModelChoiceField(queryset=Person.objects)
return super(InvoiceAdmin, self).formfield_for_foreignkey(db_field, request, **kwargs)
我刚发现你可以用另一个queryset替换原有的queryset,甚至可以将其删除并替换为choices列表。我在change_view
中这样做。
在这个例子中,我让父项设置返回值,然后从其中获取特定字段并设置.choices
:
def change_view(self, request, object_id, form_url='', extra_context=None):
#get the return value which includes the form
ret = super().change_view(request, object_id, form_url, extra_context=extra_context)
# let's populate some stuff
form = ret.context_data['adminform'].form
#replace queryset with choices so that we can specify the "n/a" option
form.fields['blurb_location'].choices = [(None, 'Subscriber\'s Location')] + list(models.Location.objects.filter(is_corporate=False).values_list('id', 'name').order_by('name'))
form.fields['blurb_location'].queryset = None
return ret