如何在 Django Admin 中更改 ForeignKey 显示文本?

Posted

技术标签:

【中文标题】如何在 Django Admin 中更改 ForeignKey 显示文本?【英文标题】:How to change ForeignKey display text in the Django Admin? 【发布时间】:2011-10-13 18:49:42 【问题描述】:

如何在选择ForeignKey 字段时更改<select> 字段中的显示文本?

我不仅需要显示ForeignKey 的名称,还需要显示其父级的名称。

【问题讨论】:

【参考方案1】:

如果您希望它仅在管理中生效,而不是全局生效,那么您可以创建一个自定义 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)

【讨论】:

【参考方案2】:

另一种方法(在更改查询集时很有用):

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 2.1.9,但所选答案有效。【参考方案3】:

较新版本的django支持这个,可以用gettext翻译:

models.ForeignKey(ForeignStufg, verbose_name='your text')

【讨论】:

记录在docs.djangoproject.com/en/dev/topics/db/models/…。 这实际上解决了与被问到的问题不同的问题。【参考方案4】:

您也可以使用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)

【讨论】:

这很好用。唯一的问题是,在使用管理员“+”选项添加值后,它仍然显示“field_name(id)”值。重新加载页面后,它显示正确。知道如何让它正确显示而无需重新加载?【参考方案5】:

见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 在哪里)。

问候,

【讨论】:

我知道,但我想在其他地方的 admin 上只更改 1 个位置,不应该更改,因为这种方法对我不利。 @robos85:检查我的答案,了解您要查找的内容。您还应该更新您的问题。【参考方案6】:

在其他答案的基础上,这里有一个小例子,供那些处理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()。

来自ModelChoiceField docs:

将调用模型的__str__() 方法来生成对象的字符串表示形式,以用于字段的选择。要提供自定义表示,请继承 ModelChoiceField 并覆盖 label_from_instance。此方法将接收一个模型对象,并应返回一个适合表示它的字符串。

【讨论】:

【参考方案7】:

第一个答案的替代方案:

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)

【讨论】:

【参考方案8】:

我刚刚发现您可以用另一个查询集替换查询集,甚至可以删除查询集并用选择列表替换它。我在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

【讨论】:

以上是关于如何在 Django Admin 中更改 ForeignKey 显示文本?的主要内容,如果未能解决你的问题,请参考以下文章

如何在 django admin 中显示多个模型的更改列表?

如何在 Django Admin 中更改 OneToOne 模型字段默认消息?

在Django Admin中更改密码

Django Admin Cookbook-36如何更改下拉菜单中的ForeignKey显示文本

如何安全地更改 Django 模型中主键字段的值?

在 Django admin 中更改日期字段的默认小部件