添加不属于模型的自定义表单字段(Django)。

140 人关注

我有一个在管理网站上注册的模型。它的一个字段是一个长字符串表达式。我想在管理员中为这个模型的添加/更新页面添加自定义表单字段。基于这些字段的值,我将建立长字符串表达式并将其保存在相关的模型字段中。

我怎样才能做到这一点呢?

我正在从符号中建立一个数学或字符串表达式。用户选择符号(这些是不属于模型的自定义字段),当他们点击保存时,我从符号列表中创建一个字符串表达式,并将其存储在DB中。我不希望符号成为模型和数据库的一部分,只希望最后的表达式。

python
python-3.x
django
django-models
django-admin
michalv82
michalv82
发布于 2013-07-30
8 个回答
Vishnu
Vishnu
发布于 2021-10-29
已采纳
0 人赞同

无论是在你的 admin.py 中,还是在单独的 forms.py 中,你都可以添加一个 ModelForm 类,然后像通常那样在其中声明你的额外字段。我还给出了一个例子,说明你如何在 form.save() 中使用这些值。

from django import forms
from yourapp.models import YourModel
class YourModelForm(forms.ModelForm):
    extra_field = forms.CharField()
    def save(self, commit=True):
        extra_field = self.cleaned_data.get('extra_field', None)
        # ...do something with extra_field here...
        return super(YourModelForm, self).save(commit=commit)
    class Meta:
        model = YourModel

要让额外的字段出现在管理中,只需。

  • Edit your admin.py and set the form property to refer to the form you created above.
  • Include your new fields in your fields or fieldsets declaration.
  • Like this:

    class YourModelAdmin(admin.ModelAdmin):
        form = YourModelForm
        fieldsets = (
            (None, {
                'fields': ('name', 'description', 'extra_field',),
    

    在Django 1.8中,你需要将fields = '__all__'添加到YourModelForm的元类中。

    我如何从保存方法中访问Meta类的模型属性?
    @sthzg, 因为它不正确。它给我的错误。【替换代码0
    alxs
    如果出于某种原因,你得到的是 AttributeError: Unable to lookup "extra_field"... ,可以试着在 extra_field 的定义中加入 label 。似乎django试图通过查看 Model ModelAdmin 来 "猜测 "它的标签,对于这样一个属性定义。
    如果extra_field是CharField()的话,这个方法很好用。但如果它是一个hiddenField[在Django 1.11],就会产生一个错误 Unknown field(s) (extra_field) specified for YourModel. Check fields/fieldsets/exclude attributes of class YourModelAdmin. 。解决这个问题的方法是 extra_field = forms.CharField(widget=forms.HiddenInput())
    为了回答@Cerin的评论,为了防止这个错误,我在管理员中创建了一个方法。 def extra_field(self, obj): pass ,只是为了让管理员不抱怨,然后表单就能自己实际呈现 extra_field
    gitaarik
    gitaarik
    发布于 2021-10-29
    0 人赞同

    在管理中可以做到这一点,但没有一个非常直接的方法。另外,我建议把大部分业务逻辑放在你的模型中,这样你就不会依赖Django管理。

    如果你在你的模型上有两个独立的字段,也许会更容易(甚至更好)。然后在你的模型上添加一个方法来合并它们。

    class MyModel(models.model):
        field1 = models.CharField(max_length=10)
        field2 = models.CharField(max_length=10)
        def combined_fields(self):
            return '{} {}'.format(self.field1, self.field2)
    

    然后在管理中,你可以将combined_fields()作为一个只读字段添加。

    class MyModelAdmin(models.ModelAdmin):
        list_display = ('field1', 'field2', 'combined_fields')
        readonly_fields = ('combined_fields',)
        def combined_fields(self, obj):
            return obj.combined_fields()
    

    如果你想把combined_fields存储在数据库中,你也可以在保存模型时保存它。

    def save(self, *args, **kwargs):
        self.field3 = self.combined_fields()
        super(MyModel, self).save(*args, **kwargs)
        
    谢谢你的回答,但这并不是我想要的。我不希望自定义字段被保存在数据库中,只希望计算的字符串。基本上,我正在做的是从符号中建立一个数学或字符串表达式,用户选择符号(这些是不属于模型的自定义字段),当他点击保存时,我从符号列表中创建一个字符串表达式,并将其存储在DB中。
    @michalv82 你也可以在模型的 save() 方法中把它保存到数据库中,查看我答案的更新。
    再次感谢,但问题是,我不想存储结合最终字段的字段(即符号),我只想保存最终的字符串
    保存这两个字段有问题吗?如果你想知道组合字段是如何产生的,也许它可以派上用场。
    再次感谢,但这不是两个字段,可能会有更多。同样,我不想把它们存储在数据库中,所以这个解决方案对我来说是不可行的。
    MangoLassi
    MangoLassi
    发布于 2021-10-29
    0 人赞同

    Django 2.1.1 主要的答案让我对我的问题回答了一半。它并没有帮助我把结果保存到实际模型中的一个字段。 在我的案例中,我想要一个用户可以输入数据的文本字段,然后当保存发生时,数据将被处理,结果被放入模型中的一个字段并保存。 虽然最初的答案显示了如何从额外的字段中获取数值,但它并没有显示如何将其保存到模型中,至少在Django 2.1.1中是如此。

    这是从一个非绑定的自定义字段中获取数值,进行处理,并将其保存到我的真实描述字段中。

    class WidgetForm(forms.ModelForm):
        extra_field = forms.CharField(required=False)
        def processData(self, input):
            # example of error handling
            if False:
                raise forms.ValidationError('Processing failed!')
            return input + " has been processed"
        def save(self, commit=True):
            extra_field = self.cleaned_data.get('extra_field', None)
            # self.description = "my result" note that this does not work
            # Get the form instance so I can write to its fields
            instance = super(WidgetForm, self).save(commit=commit)
            # this writes the processed data to the description field
            instance.description = self.processData(extra_field)
            if commit:
                instance.save()
            return instance
        class Meta:
            model = Widget
            fields = "__all__"
        
    Eyal Ch
    Eyal Ch
    发布于 2021-10-29
    0 人赞同

    你可以随时创建新的管理模板,并在你的 admin_view 中做你需要的事情(覆盖管理添加URL到你的 admin_view )。

    url(r'^admin/mymodel/mymodel/add/$','admin_views.add_my_special_model')
        
    gitaarik
    gitaarik
    发布于 2021-10-29
    0 人赞同

    如果你绝对只想在模型上存储组合字段,而不是两个单独的字段,你可以做这样的事情。

  • Create a custom form using the form attribute on your ModelAdmin . ModelAdmin.form
  • Parse the custom fields in the save_formset method on your ModelAdmin . ModelAdmin.save_model(request, obj, form, change)
  • I never done something like this so I'm not completely sure how it will work out.

    Ali
    Ali
    发布于 2021-10-29
    0 人赞同

    The first (highest score) solution ( https://stackoverflow.com/a/23337009/10843740 )是准确的,但我有更多。

    如果你通过代码声明字段,这个解决方案完全可行,但如果你想动态地建立这些字段,该怎么办?

    在这种情况下,在 __init__ 函数中为 ModelForm 创建字段是不行的。你将需要传递一个自定义的 metaclass ,并在 __new__ 函数中覆盖 declared_fields !

    下面是一个例子。

    class YourCustomMetaClass(forms.models.ModelFormMetaclass):
        For dynamically creating fields in ModelForm to be shown on the admin panel,
            you must override the `declared_fields` property of the metaclass.
        def __new__(mcs, name, bases, attrs):
            new_class = super(NamedTimingMetaClass, mcs).__new__(
                mcs, name, bases, attrs)
            # Adding fields dynamically.
            new_class.declared_fields.update(...)
            return new_class
    # don't forget to pass the metaclass
    class YourModelForm(forms.ModelForm, metaclass=YourCustomMetaClass):
        `metaclass=YourCustomMetaClass` is where the magic happens!
        # delcare static fields here
        class Meta:
            model = YourModel
            fields = '__all__'
        
    Kai - Kazuya Ito
    Kai - Kazuya Ito
    发布于 2021-10-29
    0 人赞同

    这是我所做的,以增加 自定义表单字段 "extra_field" 的一部分,而不是 the model "MyModel" 如下图所示。

    # "models.py"
    from django.contrib import admin
    from django import forms
    from .models import MyModel
    class MyModelForm(forms.ModelForm):
        extra_field = forms.CharField()
        def save(self, commit=True):
            extra_field = self.cleaned_data.get('extra_field', None)
            # Do something with extra_field here
            return super().save(commit=commit)
    @admin.register(MyModel)
    class MyModelAdmin(admin.ModelAdmin):
        form = MyModelForm
        
    G-2
    G-2
    发布于 2021-10-29
    0 人赞同

    你可能会从我的答案中得到帮助。 my response previous on multicheckchoice custom field

    你也可以扩展具有不同自定义字段的多个表单,然后将它们分配给你的inlines类,如stackedinline或tabularinline。

    form =

    这样你就可以避免表单集的复杂性,你需要从多个模型中添加多个自定义字段。

    所以你的模型管理员看起来像。

    inlines = [form1inline, form2inline,...]

    在我之前对这里的链接的回复中,你会发现init和save方法。

    当你查看页面时,init将加载,而save将把它发送到数据库。

    在这两个方法中,你可以做你的逻辑来添加字符串,然后保存,然后在Django管理的change_form或change_list中查看,这取决于你想要的地方。 list_display会在change_list上显示你的字段。 如果有帮助,请告诉我 ...

    class CohortDetailInline3(admin.StackedInline):
        model = CohortDetails
        form =  DisabilityTypesForm
    class CohortDetailInline2(admin.StackedInline):
        model = CohortDetails
        form =  StudentRPLForm
    
    @admin.register(Cohort)
    class CohortAdmin(admin.ModelAdmin):         
            form = CityInlineForm
            inlines = [uploadInline,   cohortDetailInline1,
            CohortDetailInline2, CohortDetailInline3]