代码之家  ›  专栏  ›  技术社区  ›  mattm

如何将ModelForm重新用于具有附加字段的直通模型?

  •  0
  • mattm  · 技术社区  · 4 年前

    我有一个模型 Example 以及相应的ModelForm ExampleModelForm 我在ModelFormset中使用的

    ExampleModelFormset = modelformset_factory(Example, form=ExampleModelForm)

    我有一个多对多通过模型和一个额外的属性。

    class ExampleThrough(models.Model):
        batch = models.ForeignKey(Batch, on_delete=models.CASCADE)
        example = models.ForeignKey(Example, on_delete=models.CASCADE)
        property = models.FloatField()
    

    有没有一种方法可以重用ExampleModelForm来创建ExampleThroughModelForm,我也可以在ExampleThraughModelForm集中使用它?我想要所有与ExampleModelForm相同的字段加上一个新属性。

    我认为我无法从ExampleModelForm继承:

    class ExampleThroughModelForm(ExampleModelForm):
        class Meta:
            model = ExampleThrough
            fields = '__all__'
    

    因为 documentation 有以下注释:

    适用普通Python名称解析规则。如果有多个基类声明一个Meta内部类,则只使用第一个基类。这意味着子代的Meta,如果它存在,否则是第一个父代的Meta,等等。

    我无法应用中使用的ModelForm和Meta继承 Django ModelForm inheritance and Meta inheritance 因为我的ExampleThrough模型不是从Example继承的。

    0 回复  |  直到 4 年前
        1
  •  0
  •   vinkomlacic    4 年前

    在这种情况下,我通常会创建ExampleTroughModelForm并使ExampleModelForm成为其属性。然后在HTML中,我在不带表单标记的情况下呈现这两个文件,并将它们保存在一起。记得覆盖 save() , clean() is_valid() 方法,以便正确保存和验证ExampleModelForm。

    ExampleThroughModelForm的建议代码(未测试):

    class ExampleThroughModelForm(ModelForm):
    
      class Meta:
        model = ExampleThrough
        fields = '__all__'
    
      def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
    
        # Add custom prefix to avoid name clashes in case of same
        # attribute name in ExampleModelForm and ExampleThroughModelForm
        self.example_form = ExampleModelForm(data=kwargs.get('data'), instance=getattr(self.instance, 'example', None), prefix='example')
    
        self.helper = FormHelper()
        self.helper.form_tags = False
    
      def save(self, **kwargs):
         # The ExampleModelForm needs to be saved first in case you are just
         # creating it. We can't have the through Model without this
         saved_example_instance = self.example_form.save()
    
         # Set the Example model on the ExampleThrough instance before saving
         self.instance.example = saved_example_instance
    
         # Save the ExampleThrough model
         saved_example_through_instance = super().save(**kwargs)
    
         return saved_example_through_instance
    
     def clean(self):
        # Here we also hook the calls to the clean method of the example form
        # This is not necessary but if you have a custom clean method on the
        # ExampleModelForm, you will want to call it when this form is cleaned
        self.example_form.clean()
        super().clean()
    
        return self.cleaned_data
    
     def is_valid(self)
        # This is necessary because is_valid method sets the cleaned_data attribute
        return self.example_form.is_valid() and super().is_valid()
    

    请记住,在ExampleForm中,您还需要删除表单标记。

    在具有用于呈现的脆格式的HTML中,它看起来像这样:

    <form id="example-through-form" method="post" actions="{% url 'save-example-through' %}">
      {% crispy form %}
    
      {% crispy form.example_form %}
    
      <!-- Submit input buttons omitted for brevity -->
    
    </form>