【问题标题】:Add custom form fields that are not part of the model添加不属于模型的自定义表单字段
【发布时间】:2013-07-30 15:48:20
【问题描述】:

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

我该怎么做?

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

【问题讨论】:

    标签: django django-admin


    【解决方案1】:

    在您的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
    

    要让额外的字段出现在管理员中:

    1. 编辑您的admin.py 并将表单属性设置为引用您在上面创建的表单。
    2. 在您的字段或字段集声明中包含您的新字段。

    像这样:

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

    更新:

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

    【讨论】:

    • 如何从 save 方法中访问 Meta 类的模型属性?
    • @sthzg,因为它不正确。它给了我错误:YourModelAdmin.list_display[0], 'extra_field' is not a callable or an attribute of 'YourModelAdmin' or found in the model 'YourModel'.
    • 如果由于某种原因得到AttributeError: Unable to lookup "extra_field"...,请尝试将label 添加到extra_field 定义中。似乎 django 试图通过查看 ModelModelAdmin 来“猜测”它的标签以获取此类属性定义。
    • 如果 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
    【解决方案2】:

    可以在管理员中进行,但没有一种非常简单的方法。另外,我想建议您将大多数业务逻辑保留在您的模型中,这样您就不会依赖 Django Admin。

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

    例如:

    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)
    

    【讨论】:

    • 感谢您的回答,但这不是我要找的。我不希望将自定义字段保存在数据库中,只保存计算的字符串。基本上我正在做的是从符号构建数学或字符串表达式,用户选择符号(这些是不属于模型的自定义字段),当他点击保存时,我从列表中创建一个字符串表达式表示符号并将其存储在数据库中。
    • @michalv82 也可以在模型的save()方法中保存到数据库中,查看我的答案更新。
    • 再次感谢,但问题是我不想存储组合最终字段的字段(即符号),我只想保存最终字符串
    • 保存2个字段有问题吗?如果您想知道组合字段是如何生成的,也许它会派上用场。
    • 再次感谢,但这不是 2 个字段,它可能会更多。同样,我不想将它们存储在数据库中,所以这个解决方案对我不起作用。
    【解决方案3】:

    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__"
    

    【讨论】:

      【解决方案4】:

      您始终可以创建新的管理员模板,并在您的admin_view 中执行您需要的操作(覆盖管理员将 URL 添加到您的admin_view):

      url(r'^admin/mymodel/mymodel/add/$','admin_views.add_my_special_model')
      

      【讨论】:

        【解决方案5】:

        如果您绝对只想在模型上存储组合字段而不是两个单独的字段,您可以执行以下操作:

        我从来没有做过这样的事情,所以我不完全确定它会如何工作。

        【讨论】:

          【解决方案6】:

          您可能会从我的回答中获得帮助: my response previous on multicheckchoice custom field

          您还可以扩展具有不同自定义字段的多个表单,然后将它们分配给您的内联类,如stackedinline 或tabularinline:

          表格 =

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

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

          内联 = [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]
              
                  list_select_related = True
              
                  list_display = ['rto_student_code', 'first_name', 'family_name',]
          

          ...

          【讨论】:

          • 虽然此链接可能会回答问题,但最好在此处包含答案的基本部分并提供链接以供参考。如果链接页面发生更改,仅链接答案可能会失效。 - From Review
          猜你喜欢
          • 2019-01-28
          • 2020-10-25
          • 2015-09-06
          • 2012-12-11
          • 2012-10-13
          • 1970-01-01
          • 2013-01-07
          • 1970-01-01
          • 2022-01-06
          相关资源
          最近更新 更多