Formsets 返回为 valid=False,而且不保存

huangapple go评论88阅读模式
英文:

Formsets are returning as valid=False and not saving

问题

I'm trying to create a form that allows teachers to create lessons, which are linked to multiple activities (ManyToMany), which are in turn linked to many Items (ForeignKey). As such, I am using formsets for activities and items. However, activity_formset is coming back as invalid and not saving.

The View

class N_LessonEditAllView(LoginRequiredMixin, UpdateView):
    model = Lesson
    template_name = 'lessons/edit-lesson.html'
    form_class = LessonForm

    def get_object(self):
        return get_object_or_404(Lesson, pk=self.kwargs.get('pk'))

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        lesson = self.get_object()
        activity_formset = ActivityFormSet(queryset=lesson.activity.all())
        item_formsets = []

        for activity_form in activity_formset:
            item_formset = ItemFormSet(queryset=Item.objects.filter(activity_id=activity_form.instance.id))
            item_formsets.append(item_formset)

        context['activity_formset'] = activity_formset
        context['item_formsets'] = item_formsets

        return context

    def form_valid(self, form):
        
        context = self.get_context_data()
        activity_formset = context['activity_formset']
        item_formsets = context['item_formsets']

        print('activity_formset is valid:', activity_formset.is_valid())
        print('activity_formset errors:', activity_formset.errors)
        
        for activity_form in activity_formset:
            item_formset = ItemFormSet(queryset=Item.objects.filter(activity_id=activity_form.instance.id))
            item_formsets.append(item_formset)
            print(f'activity_formset for activity {activity_form.instance.id} is valid: {item_formset.is_valid()} --- data {activity_formset.data} --- errors: {activity_formset.errors} --- payload {self.request.POST}')
            
        if activity_formset.is_valid() and all(item_formset.is_valid() for item_formset in item_formsets):
            self.object = form.save()
            activity_formset.instance = self.object
            activity_formset.save()
            print('Form is valid:', form.is_valid())
            print('Activity formset is valid:', activity_formset.is_valid())
            for item_formset in item_formsets:
                print('Item formset is valid:', item_formset.is_valid())
                item_formset.instance = self.object
                item_formset.save()
                
            raise ValueError('Intentional error')
            print(form.cleaned_data)

            return super().form_valid(form)
        else:
            return self.form_invalid(form)

The Template

<div class="card">
    <form method="post">
    <div class="card-header">
        {% csrf_token %}
        {{ form|crispy }}
    </div>
    
    <div class="card-body">
        {% for activity_form in activity_formset %}
        {{ activity_formset.management_form }}
        <div class="card mb-3">
            <div class="card-header">
                <h5>Task {{ forloop.counter }}</h5>
                {{ activity_form|crispy }}
            </div>
            <!-- Start of Items_set -->
            <div class="card-body">
                {{ item_formsets.forloop.parentloop.counter0 }}
                {{ item_formsets.0.management_form }}
                {% for item_form in item_formsets.0 %}
                <div class="form-inline">
                {{ item_form|crispy }}
                </div>
                {% endfor %}
            <!-- End of Item_set -->
            </div>
        </div>
        {% endfor %}
        <button type="submit" class="btn btn-success">Save</button>
    </div>
    </form>
</div>

Returns:

activity_formset is valid: False
activity_formset errors: []
activity_formset for activity 8 is valid: False --- data {} --- errors: [] --- payload <QueryDict: {'csrfmiddlewaretoken': ['GJ3z6OnViO1GGavFmnzF9ihcT4QdNeznwpZ0qNjbpkwMMk8igWGFHPIQHeFyCktm'], 'title': ['Lesson 12'], 'form-TOTAL_FORMS': ['2', '4', '2', '4'], 'form-INITIAL_FORMS': ['2', '4', '2', '4'], 'form-MIN_NUM_FORMS': ['0', '0', '0', '0'], 'form-MAX_NUM_FORMS': ['1000', '1000', '1000', '1000'], 'form-0-title': ['When was Canada founded?'], 'form-0-answer': ['1867'], 'form-0-level': ['1'], 'form-0-act_type': ['1'], 'form-0-id': ['8', '22', '22'], 'form-0-item_text': ['1562', '1562'], 'form-0-item_type': ['1', '1'], 'form-1-item_text': ['897', '897'], 'form-1-item_type': ['1', '1'], 'form-1-id': ['23', '9', '23'], 'form-2-item_text': ['1912', '1912'], 'form-2-item_type': ['1', '1'], 'form-2-id': ['24', '24'], 'form-3-item_text': ['1812', '1812'], 'form-3-item_type': ['1', '1'], 'form-3-id': ['25', '25'], 'form-1-title': ["What is Canada's Capital?"], 'form-1-answer': ['Ottawa'], 'form-1-level': ['1'], 'form-1-act_type': ['1']}>

What I've Tried
I've been hacking at this for the last couple of days, would greatly appreciate some insight.

I have tried printing the errors, which come back empty and tried to print them on the page, which also come back empty. I just want it to save.

I also tried reading other solutions, but I'm not sure I am able to relate those situations to this one (maybe I'm not seeing/understanding it).

英文:

I'm trying to create a form that allows teachers to create lessons, which are linked to multiple activities (ManyToMany), which are in turn linked to many Items (ForeignKey). As such, I am using formsets for activities and items. However, activity_formset is coming back as invalid and not saving.

The View

class N_LessonEditAllView(LoginRequiredMixin, UpdateView):
model = Lesson
template_name = &#39;lessons/edit-lesson.html&#39;
form_class = LessonForm
def get_object(self):
return get_object_or_404(Lesson, pk=self.kwargs.get(&#39;pk&#39;))
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
lesson = self.get_object()
activity_formset = ActivityFormSet(queryset=lesson.activity.all())
item_formsets = []
for activity_form in activity_formset:
item_formset = ItemFormSet(queryset=Item.objects.filter(activity_id=activity_form.instance.id))
item_formsets.append(item_formset)
context[&#39;activity_formset&#39;] = activity_formset
context[&#39;item_formsets&#39;] = item_formsets
return context
def form_valid(self, form):
context = self.get_context_data()
activity_formset = context[&#39;activity_formset&#39;]
item_formsets = context[&#39;item_formsets&#39;]
print(&#39;activity_formset is valid:&#39;, activity_formset.is_valid())
print(&#39;activity_formset errors:&#39;, activity_formset.errors)
for activity_form in activity_formset:
item_formset = ItemFormSet(queryset=Item.objects.filter(activity_id=activity_form.instance.id))
item_formsets.append(item_formset)
print(f&#39;activity_formset for activity {activity_form.instance.id} is valid: {item_formset.is_valid()} --- data {activity_formset.data} --- errors: {activity_formset.errors} --- payload {self.request.POST}&#39;)
if activity_formset.is_valid() and all(item_formset.is_valid() for item_formset in item_formsets):
self.object = form.save()
activity_formset.instance = self.object
activity_formset.save()
print(&#39;Form is valid:&#39;, form.is_valid())
print(&#39;Activity formset is valid:&#39;, activity_formset.is_valid())
for item_formset in item_formsets:
print(&#39;Item formset is valid:&#39;, item_formset.is_valid())
item_formset.instance = self.object
item_formset.save()
raise ValueError(&#39;Intentional error&#39;)
print(form.cleaned_data)
return super().form_valid(form)
else:
return self.form_invalid(form)

The Template

&lt;div class=&quot;card&quot;&gt;
&lt;form method=&quot;post&quot;&gt;
&lt;div class=&quot;card-header&quot;&gt;
{% csrf_token %}
{{ form|crispy }}
&lt;/div&gt;
&lt;div class=&quot;card-body&quot;&gt;
{% for activity_form in activity_formset %}
{{ activity_formset.management_form }}
&lt;div class=&quot;card mb-3&quot;&gt;
&lt;div class=&quot;card-header&quot;&gt;
&lt;h5&gt;Task {{ forloop.counter }}&lt;/h5&gt;
{{ activity_form|crispy }}
&lt;/div&gt;
&lt;!--Start of Items_set--&gt;
&lt;div class=&quot;card-body&quot;&gt;
{{ item_formsets.forloop.parentloop.counter0 }}
{{ item_formsets.0.management_form }}
{% for item_form in item_formsets.0 %}
&lt;div class=&quot;form-inline&quot;&gt;
{{ item_form|crispy }}
&lt;/div&gt;
{% endfor %}
&lt;!--End of Item_set--&gt;
&lt;/div&gt;
&lt;/div&gt;
{% endfor %}
&lt;button type=&quot;submit&quot; class=&quot;btn btn-success&quot;&gt;Save&lt;/button&gt;
&lt;/div&gt;
&lt;/form&gt;
&lt;/div&gt;

Returns:
> activity_formset is valid: False
activity_formset errors: []
activity_formset for activity 8 is valid: False --- data {} --- errors: [] --- payload <QueryDict: {'csrfmiddlewaretoken': ['GJ3z6OnViO1GGavFmnzF9ihcT4QdNeznwpZ0qNjbpkwMMk8igWGFHPIQHeFyCktm'], 'title': ['Lesson 12'], 'form-TOTAL_FORMS': ['2', '4', '2', '4'], 'form-INITIAL_FORMS': ['2', '4', '2', '4'], 'form-MIN_NUM_FORMS': ['0', '0', '0', '0'], 'form-MAX_NUM_FORMS': ['1000', '1000', '1000', '1000'], 'form-0-title': ['When was Canada founded?'], 'form-0-answer': ['1867'], 'form-0-level': ['1'], 'form-0-act_type': ['1'], 'form-0-id': ['8', '22', '22'], 'form-0-item_text': ['1562', '1562'], 'form-0-item_type': ['1', '1'], 'form-1-item_text': ['897', '897'], 'form-1-item_type': ['1', '1'], 'form-1-id': ['23', '9', '23'], 'form-2-item_text': ['1912', '1912'], 'form-2-item_type': ['1', '1'], 'form-2-id': ['24', '24'], 'form-3-item_text': ['1812', '1812'], 'form-3-item_type': ['1', '1'], 'form-3-id': ['25', '25'], 'form-1-title': ["What is Canada's Capital?"], 'form-1-answer': ['Ottawa'], 'form-1-level': ['1'], 'form-1-act_type': ['1']}>

What I've Tried
I've been hacking at this for the last couple of days, would great appreciate some insight.

I have have tried printing the errors, which come back empty and tried to print them on the page, which also come back empty. I just want it to save.

I also tried reading other solutions, but I'm not sure I am able to relate those situations to this one (maybe I'm not seeing/understanding it).

答案1

得分: 1

您正在错误地实现了form_valid功能。您在此处调用了get_context_data,这是用于在GET请求期间从视图发送信息到模板的方法。但是,form_valid函数应该从request.POST中获取数据,因为此方法在通过POST请求提交表单后调用,因此数据应该来自POST请求。要访问POST数据,您可以尝试像这样:

def form_valid(self, form):
    activity_formset = ActivityFormset(self.request.POST)
    item_formset = ItemFormSet(self.request.POST)
    if activity_formset.is_valid() and item_formset is_valid():
       act_instances = activity_formset.save()
       item_instances = item_formset.save()
       return super().form_valid(form)
    return super().form_invalid(form)

但我怀疑ItemFormSet可能无法正常工作。通常在这种情况下,最好事先定义一个嵌套的FormSet并使用它(而不是使用两个单独的FormSets)。我建议查看此GitHub存储库的示例或阅读有关如何实现嵌套表单集的博客

英文:

You are implementing the form_valid functionality in wrong way. You are calling the get_context_data here, which is for sending information from view to template during GET requests. But, the form_valid function should get the data from request.POST as this method is called after the form is submitted by POST request, hence the data should be coming from a post request. To access the post data, you can try like this:

def form_valid(self, form):
activity_formset = ActivityFormset(self.request.POST)
item_formset = ItemFormSet(self.request.POST)
if activity_formset.is_valid() and item_formset.is_valid():
act_instances = activity_formset.save()
item_instances = item_formset.save()
return super().form_valid(form)
return super().form_invalid(form)

But I doubt the ItemFormSet will work appropriately. Usually in that case, it is better to define a Nested FormSet beforehand and use it (rather than having two separate FormSets). I would recommend looking into this GitHub repository for example or read this blog about how to implement nested formsets.

答案2

得分: 1

不确定这是否是唯一的方法。只是一个解决方法。

当我遇到这个问题时,我没有在表单中包括"ModelField"字段,而是在Model表单中创建了一个表单字段,并在视图中处理了该值。

假设在你的模型中有一个名为"ModelField"的字段,我会在我的forms.py中忽略"ModelField",然后创建一个名为"FormModelField"的表单字段。这样,当你验证表单时,它仍然有效。

然后,在验证我的表单后,我会保存我的表单,然后获取"FormModelField"的值,然后更新"ModelField"为"FormModelField"的值。

如果我错了,请指导我使用正确的方法。 Formsets 返回为 valid=False,而且不保存

英文:

Not sure if this is the only approach. Just as a workaround i did.

What I did when I had this issue was, I did not include the Model field in the form but I created a form field in the Model form and handled the value in the Views.

Lets say in your model you have a field named "ModelField" I will ignore that "ModelField" in my forms.py and create a form field called "FormModelField". This way when you validate your form it is still Valid.

Then after validating my form i will save my form then get my "FormModelField" value and then update my "ModelField" with "FormModelField" value.

If i'm wrong then please guide me also with the correct approach. Formsets 返回为 valid=False,而且不保存

huangapple
  • 本文由 发表于 2023年3月4日 07:36:59
  • 转载请务必保留本文链接:https://go.coder-hub.com/75632717.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定