Reputation: 2423
I have a formset which has some initial data provided - it's a data cloned from other model which contains 2 fields 'group' and 'requested'.
When initial data is provided the forms in formset do not get saved, they only get saved when I'll modify the form with the data a little bit.
When no initial data is provided forms do get saved.
Why adding intial data to a formset makes it impossible to save formsets data to the database?
This is my formset existing in get_context_data dictionary:
initial = ProcedureActionGroup.objects.filter(procedure__id=self.kwargs.get('pk', None))
initial_values = initial.values('group', 'requested')
print initial_values
initial_count = initial.count()
ActionGroupFormset = inlineformset_factory(self.model, TaskGroup, extra=initial_count,
form=TaskActionGroupForm,
can_delete=False,
)
data['formset'] = ActionGroupFormset(self.request.POST or None, initial=initial_values,
**self.get_formset_kwargs())
This is my form_valid method where I save all data
def form_valid(self, form):
context = self.get_context_data()
forms = []
forms.append(form.is_valid())
if self.get_procedure_obj():
formset = context['formset']
forms.append(formset.is_valid())
if all(forms):
self.object = form.save(commit=False)
form.save()
if self.get_procedure_obj():
formset = formset.save(commit=False)
for obj in formset:
obj.task = self.object
obj.save()
self.object.extract_users()
return HttpResponseRedirect(self.object.get_absolute_url())
Model:
class TaskGroup(models.Model):
task = models.ForeignKey(Task, null=True, blank=False)
group = models.ForeignKey(ActionGroup, null=True, blank=True)
requested = models.PositiveIntegerField(u'Requested', null=True, blank=True)
form template:
<form method="post" action="" class="span6 offset2 form form-horizontal">
{% crispy form%}
{{formset.management_form}}
{% if formset %}
<div>
<fieldset>
<table class="table table-striped">
{% for form in formset%}
<tr>
{% for field in form %}
<td> {{field}} </td>
{% endfor %}
</tr>
{% endfor %}
</table>
</fieldset>
</div>
{% endif %}
<div class="form-actions">
<button class="btn btn-primary btn-large" type="submit">
Save
</button>
</div>
</form>
Upvotes: 6
Views: 2948
Reputation: 2706
TLDR; Remove initial
on POST
.
I have myself faced this issue in Django admin with the formsets generated by using InlineModelAdmin
. What I was trying to achieve was to prefill the inline forms using get_formset_kwars
to reduce the need for data entry by the user.
However, it took me a while to discover why the prefilled data was never saved. It seemed random and buggy, but after a lot of Googling, I did find out that initial
data is never saved if it has not changed. This is a feature not a bug although a confusing one.
Long story short, the simplest method for me was to remove the initial
property when the request.method
is POST
. That way, Django cannot establish that the initial data has not changed, and will save the related models as intended.
Upvotes: 0
Reputation: 11891
I had a similar issue using django extra views.
I solved it bay modifying the form class that is passed to the inlineformset_factory. the ModelForm.has_changed method was returning false.
class TaskActionGroupForm(ModelForm):
model = YourModel
def has_changed(self):
"""
Overriding this, as the initial data passed to the form does not get noticed,
and so does not get saved, unless it actually changes
"""
changed_data = super(ModelForm, self).has_changed()
return bool(self.initial or changed_data)
Upvotes: 7
Reputation: 2423
In exmaple above I used inlineformset_factory which treaded values passed to initial as already existing objects.
So when I looped over my formset with:
for form in formset:
form.has_changed()
form.has_changed() returned False
I've repleaced inlineformset_factory to formset_factory which treats intial data as new data which get's saved correctly. Code example will be posted tomorrow.
Upvotes: 1