Django:在管理界面中伪造字段?

18 投票
4 回答
12768 浏览
提问于 2025-04-16 11:11

我有一个模型,叫做Foo。这个模型里面有一些数据库属性,还有一些是根据不同因素计算出来的属性。我想把这些计算出来的属性展示给用户,就像它们是数据库里的属性一样。(这些计算的因素会根据用户的输入而改变。)请问在Django的管理界面中,有办法做到这一点吗?

4 个回答

1

在编辑表单中,把属性名称放到 readonly_fields 里(这个只适用于1.2版本及以上)。

在更改列表中,把它放到 list_display 里。

7

想要在更改列表中显示任意数据或者让某个字段出现在表单里其实很简单。你只需要用 list_display 来指定,里面可以放实际的模型属性,或者在模型或模型管理器上定义的方法。而且,你还可以通过继承 forms.ModelForm 来添加你想要的任何字段类型到更改表单中。

不过,结合这两者就要难得多,甚至有时候是不可能的。也就是说,如果你想在更改列表中显示某个任意数据,并且能够直接在列表中编辑这个数据,就需要用到 list_editable。但是,Django 似乎只接受真正的模型属性,也就是和数据库字段对应的属性。即使你在模型定义的方法上使用 @property 也不够。

有没有人找到过一种方法,可以直接在更改列表页面编辑那些实际上并不存在于模型中的字段呢?

34

我建议你为Foo创建一个子类的表单(叫做FooAdminForm),这样你就可以添加一些不需要数据库支持的自定义字段。你可以把自定义的验证逻辑放在ModelForm的clean_*方法里。

FooAdminsave_model方法里,你可以获取请求、Foo的实例和表单数据,这样你就可以在保存实例之前或之后处理这些数据。

下面是一个在django admin中注册的模型和自定义表单的例子:

from django import forms
from django.db import models
from django.contrib import admin


class Foo(models.Model):
    name = models.CharField(max_length=30)


class FooAdminForm(forms.ModelForm):
    # custom field not backed by database
    calculated = forms.IntegerField()

    class Meta:
        model = Foo 


class FooAdmin(admin.ModelAdmin):
    # use the custom form instead of a generic modelform
    form = FooAdminForm

    # your own processing
    def save_model(self, request, obj, form, change):
        # for example:
        obj.name = 'Foo #%d' % form.cleaned_data['calculated'] 
        obj.save()


admin.site.register(Foo, FooAdmin)

根据实例数据为自定义字段提供初始值

(我不确定这是否是最好的解决方案,但应该是可行的。)

当为数据库中已有的模型实例创建表单时,会将这个实例传递给它。因此,在FooAdminForm的__init__方法中,可以根据实例数据来修改字段的属性。

    def __init__(self, *args, **kwargs):
        super(FooAdminForm, self).__init__(*args, **kwargs)
        # only change attributes if an instance is passed            
        instance = kwargs.get('instance')
        if instance:
            self.fields['calculated'].initial = (instance.bar == 42)

撰写回答