【问题标题】:django ModelForms - am I doing it right?django ModelForms - 我做对了吗?
【发布时间】:2017-12-26 21:38:51
【问题描述】:

我有一个使用 6 字段或 8 字段答案的投票应用扩展程序。我已经非常简单地定义了这些 ModelForms。

class expert_judgement_three_form(ModelForm):
"""From Burgman "Trusting Judgements" 2016 p95. Values are probabilities """
class Meta:
    model = EJ_three_field
    exclude = ['question','user']

[表格似乎需要在模板中进行大量手动配置才能使它们进入估算:基本原理对]

为了预填充字段,我创建了模型的一个对象实例,然后将其传递给表单,然后从表单中取回它。 the docs 中的示例逐项遍历cleaned_data 字典 - 它似乎并没有减少样板代码。我注意到this reply 并且我已经看到this answer 并且可能会以这种方式工作。

我无法摆脱我误解或误读了应该如何使用表单,尤其是模型表单的关键特性的感觉,因为仍然有很多东西是可见的(这给我带来了麻烦)。

这个问题的一个好的答案是尽可能稀疏的模型、ModelForm 和 View 用于一个多字段表单,它总是预先填写用户之前对问题的回答。

【问题讨论】:

    标签: django django-forms


    【解决方案1】:

    基本上,FormModelForm 是不同的。表单由您手动配置,ModelForm 将自动创建。

    如果您创建了模型,您通常希望允许用户通过表单创建这些模型。 Django 无需复制所有字段名称并自己创建表单,而是为此提供了一个快捷方式,即 ModelForm:

    使用 Modelform 创建的模型表单示例:

    forms.py

    class expert_judgement_three_form(ModelForm):
    """From Burgman "Trusting Judgements" 2016 p95. Values are probabilities """
         class Meta:
               model = EJ_three_field
               exclude = ['question','user']
    

    此表单自动具有与创建它的 EJ_three_field 模型相同的所有字段类型。

    views.py

    def create(request):
        form = expert_judgement_three_form(request.POST)
        if request.method == 'POST':
            form = expert_judgement_three_form(request.POST)
            print form.errors
            if form.is_valid :
                print "all validation passed"
                # commit=False tells Django that "Don't send this to database yet.
                expert_judgement_three_form = form.save(commit=False)
                expert_judgement_three_form.save()
                return (request, "")
            else:
                 print form.errors
        else:
            form = expert_judgement_three_form(request.POST)
        return render(request, "",{"form":form})
    
    def edit(request,pk):
        EJ_three_field_object = EJ_three_field.objects.get(pk=pk)
        form = expert_judgement_three_form(request.POST,instance=EJ_three_field_object)
        if request.method == 'POST':
            form = expert_judgement_three_form(request.POST,instance=EJ_three_field_object)
            print form.errors
            if form.is_valid :
                print "all validation passed"
                # commit=False tells Django that "Don't send this to database yet.
                expert_judgement_three_form = form.save(commit=False)
                expert_judgement_three_form.save()
                return (request, "")
            else:
                 print form.errors
        else:
            form = expert_judgement_three_form(request.POST,instance=EJ_three_field_object)enter code here
        return render(request, "",{"form":form})
    

    urls.py

    url(r'^create/', views.create, name='create'),
    #edit
    url(r'^edit/(?P<pk>\d+)/$',views.edit,name='edit'),
    

    文档:Creating forms from ModelForm

    【讨论】:

    • 谢谢埃米尔。你能解释一下为什么推荐这种两态过程吗? codeexpert_judgement_three_form = form.save(commit=False) Expert_judgement_three_form.save() code
    • 你可以给任何变量而不是expert_judgement_three_form。它只是一个变量。commit=False 告诉 Django “不要将它发送到数据库。我还有更多事情要做它。”如果你愿意,你可以在这里设置用户对象。
    【解决方案2】:

    我会这样解释。

    1

    不使用{{form}}模板标签,你应该自己写&lt;form action="." method="post"&gt;{% csrf token %} &lt;label ...&gt; &lt;input type ...&gt; .. &lt;/form&gt;。因此,通过使用表单,您可以减少编写所有这些的时间。

    2

    如果没有 modelForm,您需要指定每个输入。

    class Example(forms.Form):
        example1 = forms.CharField(label="label name", max_length=100)
        example2 = forms.IntergerField()
        ... what if you have 10 inputs?, you should write all 10 inputs
    

    但是如果你已经定义了你的模型(在很多情况下,你会在你的数据库中保存或更新表单。这意味着你已经定义了你的模型。),你可以简单地使用 ModelForm 而无需花时间自己编写 FormField。

    class Example(ModelForm):
        class Meta:
            Model : yourModel
            fields : ['example1', 'example2', ...]
    

    我想我会很安全。如果你定义你的模型元素像

    #model.py
    class ExampleModelCalss(models.Model):
        example1 = models.CharField(max_length=50)
    
    #forms.py
    class ExampleFormClass(forms.Form):
        example1 = forms.CharField(max_length=100)
    

    您可以输入长度为 70 的文本,因此它通过了表单验证,但是当它保存在您的数据库中时会发生什么?

    所以我的想法是这样的。我很好奇其他人是怎么想的。

    此外,您还可以混合使用。 例如,

    #models.py
    class ExampleModelCalss(models.Model):
        example1 = models.CharField(max_length=50)
        example2 = models.IntegerField(default=0)
    

    你想添加一个不在你的模型中。

    class ExampleMixForm(forms.ModelForm)
        example3 = forms.CharField(max_length=50) # adding
        
        class Meta:
            Model : ExampleModelCalss
            fields = ['example1', 'example2', 'example3'] # adding
    

    【讨论】:

      【解决方案3】:

      我现在可以工作了!我想我会提出这个工作代码,以防有人发现它有用。可能使这变得有趣的困难来源之一是,我仅将表单用于多字段答案,而不将它们用于单字段答案。任何对此的评论都将受到欢迎。我对此很陌生。

      models.py

      class Question(models.Model):
      ...
      class Answer(models.Model):
          question = models.ForeignKey(Question, on_delete=models.CASCADE)
          user = models.ForeignKey(User, on_delete=models.CASCADE, default = 1)
          class Meta:
              abstract = True
      
      # What follows is a class method that gets existing answers, creates new default entries or updates answers depending on when it is called. It assumes that each question is answered only once.
          @classmethod
          def get_or_update_answer(self, user, question, submitted_value={None}):
              if question.answer_type == 'CH':
                  if submitted_value == {None}:
                      selected_choice = question.choice_set.first() # by default, select the top of a set of radio buttons
                      answer, _created = Vote.objects.get_or_create(user = user, question = question, defaults={'choice':selected_choice})
      
                  else:
                      selected_choice = question.choice_set.get(pk=submitted_value)
                      answer = Vote.objects.get(user = user, question = question)
                      answer.choice = selected_choice
      
              if question.answer_type == 'SA':
                  if submitted_value == {None}:
                      submitted_value = "brief response"
                      answer, _created = Short_Answer.objects.get_or_create(user = user, question = question, defaults={'short_answer': submitted_value})
      
                  else:
                      answer = Short_Answer.objects.get(user = user, question = question)
                      answer.short_answer = submitted_value
      
              if question.answer_type == 'LA':
                  if submitted_value == {None}:
                      submitted_value = "full response"
                      answer, _created = Long_Answer.objects.get_or_create(user = user, question = question, defaults={'long_answer':submitted_value})
      
                  else:
                      answer = Long_Answer.objects.get(user = user, question = question)
                      answer.long_answer = submitted_value
              if question.answer_type == 'E3':
                  if submitted_value == {None}:
                      submitted_value = {'lowest_value': 0, 'lv_rationale': "low estimate reasons",
                                        'highest_value':0,  'hv_rationale': "high estimate reasons",
                                        'best_value':0, 'bv_rationale': "best estimate reasons",}
                      answer, _created = EJ_three_field.objects.get_or_create(user= user, question = question, defaults=submitted_value)
                  else:
                      answer = EJ_three_field.objects.get(user= user, question = question)
                      for key, value in submitted_value.items():
                          setattr(answer,key,value)
              if question.answer_type == 'E4':
                  if submitted_value == {None}:
                      submitted_value = {'lowest_value': 0, 'lv_rationale': "low estimate reasons",
                                        'highest_value':0,  'hv_rationale': "high estimate reasons",
                                        'best_value':0, 'bv_rationale': "best estimate reasons",
                                        'confidence': 0, 'conf_rationale': "reasons for confidence score",}
                      answer, _created = EJ_four_field.objects.get_or_create(user = user, question = question, defaults=submitted_value)
                  else:
                      answer = EJ_four_field.objects.get(user= user, question = question)
                      for key, value in submitted_value.items():
                          setattr(answer,key,value)
              # answer.save() - not required for create / update funcs
              answer.save()
              return answer
      
      class Short_Answer(Answer):
      class Long_Answer(Answer):
      class Vote(Answer):
      class EJ_three_field(Answer):
      class EJ_four_field(Answer):
      

      Views.py

      def pageView(request, user_id, page_num):
          question_set = Question.objects.filter(man_page = page_num)
          user = User.objects.get(username = user_id)
          template = 'polls/page_question.html'
          context = {'question_set' : question_set, }
          forms = {}
          answers = {}
          for question in question_set:
              answers[question] = Answer.get_or_update_answer(user, question)
              form_instance = form_instantiator(question, instance = answers[question])
              forms[question] = form_instance
      
          context['forms']=forms
          context['answers']=answers
          return render(request, template, context)
      
      @login_required
      def answer(request, user_id, man_index):
          #attempt to standardise saving of answers (votes, shorts, longs, E3, vE4 etc)
          user = User.objects.get(username = user_id)
          question = Question.objects.get(man_index = man_index)
          form_instance = form_instantiator(question, request = request)
          if form_instance == None:
              answer_value = request.POST['answer_value']
              Answer.get_or_update_answer(user = user, question = question, submitted_value = answer_value)
          else:
              form = form_instantiator(question, request = request)
              if form.is_valid():
                  print(form.cleaned_data)
                  Answer.get_or_update_answer(user = user, question = question, submitted_value = form.cleaned_data)
          return HttpResponseRedirect(reverse('polls:page', args=(user_id, question.man_page)))
      

      回答我最初的问题:可以使用 (instance=) 或 (request.POST) 或未绑定创建 ModelForm,两者都不绑定。提交数据时,会丢弃与上下文一起传递的对象。一个ModelForm可以用form.save()保存,效率很高。

      【讨论】: