【发布时间】:2011-10-13 18:49:42
【问题描述】:
如何在选择ForeignKey 字段时更改<select> 字段中的显示文本?
我不仅需要显示ForeignKey 的名称,还需要显示其父级的名称。
【问题讨论】:
标签: django django-admin
如何在选择ForeignKey 字段时更改<select> 字段中的显示文本?
我不仅需要显示ForeignKey 的名称,还需要显示其父级的名称。
【问题讨论】:
标签: django django-admin
如果您希望它仅在管理中生效,而不是全局生效,那么您可以创建一个自定义 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')
【讨论】:
您也可以使用label_from_instance 直接从您的admin.ModelAdmin 实例完成此操作。例如:
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 方法中定义要显示的内容(ForeignKey 在哪里)。
问候,
【讨论】:
在其他答案的基础上,这里有一个小例子,供那些处理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。此方法将接收一个模型对象,并应返回一个适合表示它的字符串。
【讨论】:
第一个答案的替代方案:
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)
【讨论】:
我刚刚发现您可以用另一个查询集替换查询集,甚至可以删除查询集并用选择列表替换它。我在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
【讨论】: