I'm trying to create a simple modelformset to show multiple copies of the same form. My models.py table is called Driver and it has only one field, driver:
class Driver(models.Model):
driver = models.CharField(max_length=100, blank=True, null=True)
def __str__(self):
return self.driver if self.driver else ''
My urls.py:
urlpatterns = [
path('driver/', DriverCreateView.as_view(), name = "driver-create"),
]
My forms.py:
class DriverForm(forms.ModelForm):
class Meta:
model = Driver
fields = ['driver']
# exclude = ("id",)
DriverFormSet = modelformset_factory(Driver, form = DriverForm, fields = ['driver'], extra = 1, max_num=20, can_delete=True)
I'm fairly sure the issue is in views.py:
class DriverCreateView(LoginRequiredMixin, CreateView):
model = Driver
form_class = DriverForm
template_name = 'trucking/driver_form.html'
def get_context_data(self, **kwargs):
context = super(DriverCreateView, self).get_context_data(**kwargs)
context['formset'] = DriverFormSet()
return context
def post(self, request, *args, **kwargs):
formset = DriverFormSet(request.POST)
form= DriverForm(request.POST)
if formset.is_valid():
formset.save()
if self.request.POST.get('Save_Exit'):
return HttpResponseRedirect('/database/')
if self.request.POST.get('Save'):
return HttpResponseRedirect('/driver/')
return render(request, self.template_name, {'formset': formset})
Finally my template driver_form.html:
<form method = "POST">
<fieldset class = "form-group">
{% csrf_token %}
{{ formset.management_form }}
{{ formset.non_form_errors }}
{% for hidden in form.hidden_fields %} {{ hidden }} {% endfor %}
<table cellpadding="6" cellspacing="1" border="0">
<tbody>
{% for form in formset %}
{% for field in form.visible_fields %}
<tr>
<td class="column_name">{{field.label_tag}}{{field.auto_id}}</td>
<td class="column_data">{{field}}</td>
<td class="column_errors">{{field.errors|striptags}}</td>
</tr>
{% endfor %}
{% endfor %}
<tr>
<td colspan = "2" class="column_submit">
<input type="submit" name="Save_Exit" value="Save & Exit" class="submit">
<input type="submit" name="Save" value="Save" class="submit">
<a href="{% url 'database'%}"><input type="button" name="Cancel" value="Cancel" class="submit"></a>
</td>
</tr>
</tbody>
The reason I think it's the views.py file is that I cobbled it together from a number of different sources, and not really sure if the lines I have are really necessary or what am I missing to the form to actually update information. When I edited the post
function inside the class-based view to form.save()
instead of formset.save()
, I would be able to add new fields successfully, but not update or delete the existing fields. I have a feeling that the logic is not captured in the if statements somehow, or the formset is being reset to a default value, perhaps in this line:
context['formset'] = DriverFormSet()
Any advice is appreciated.
CodePudding user response:
You are missed adding formset
hidden inputs. Those inputs provide Driver.id
for each form, so django will know which instance to update with which driver
.
Instead of
{% for hidden in form.hidden_fields %} {{ hidden }} {% endfor %}
use
{% for form in formset %}
{% for hidden in form.hidden_fields %}
{{ hidden }}
{% endfor %}
{% endfor %}
CreateView
(and all class-based views) are not designed to handle formsets. They are supposed to handle 1 form and 1 instance of model. You will have multiple forms (via formset) and multiple instances of model. Your view will work, but it's not making best use of CreateView
: you are not using several methods which are supposed to be used, you will have self.object
and self.form
which you will never use in your view.
There is nothing wrong with function-based views. Refer to django docs. For example:
@login_required
def create_drivers(request):
if request.method == 'POST':
formset = DriverFormSet(request.POST, request.FILES)
if formset.is_valid():
formset.save()
if request.POST.get('Save_Exit'):
return HttpResponseRedirect('/database/')
if request.POST.get('Save'):
return HttpResponseRedirect('/driver/')
else:
formset = DriverFormSet()
return render(request, 'trucking/driver_form.html', {'formset': formset})