英文:
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 = '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 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"的值。
如果我错了,请指导我使用正确的方法。
英文:
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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论