Formset questions: data vs. initial values

146 views
Skip to first unread message

Carsten Fuchs

unread,
Sep 7, 2015, 3:20:38 PM9/7/15
to django...@googlegroups.com
Dear Django group,

after having read all of the Django docs that I could find, I still have two questions
about the proper use of formsets. Summary first:

1. What is a good way to make sure the data received in the POST request agrees with
the "initial" data (number of items and items order is as expected)?

2. What is a good way to augment each form with additional data that is only used for
decoration (rendering additional information) in the template, but is not a form field?


More details:

In a single view, I would like to let the user edit calendar entries, i.e. one CharField
value for each day in a month. The related model is:


from django.db import models

class CalendarEntry(models.Model):
ref_date = models.DateField()
entry = models.CharField(max_length=40, blank=True)


An important issue is that we don't create an instance of CalendarEntry for each day in
a month, but only for those that actually have an entry! However, the formset that is
presented to the user should of course have one "entry" field for each day in the month
(properly initialized if we have a related CalenderEntry instance, blank otherwise).

Thus, it seems that a ModelForm for CalendarEntry cannot be used, because we need some
forms for which a CalendarEntry instance does not exist, but the form instance must
still exist and be initialized with a date (and an empty "entry" field).

Therefore, I made a simple form like this, mimicing the model:


from django import forms

class ExampleForm(forms.Form):
ref_date = forms.DateField(widget=forms.HiddenInput())
entry = forms.CharField(required=False, max_length=40)


The date field is hidden, because the user is not supposed to change it. But we need it
for reference later, when the POST data is processed. (It could possibly be omitted in
this example, but if this was not the days in a month but the students in a class, it
would be the only reliable reference connecting the value to the student.)

Now, let's suppose I came up with a list of initial values, both when the formset is
first created in the GET request, and also later when the data is submitted in the POST
request. For example like this:


# Example is specific to September:
from datetime import date
september_inits = [{"ref_date": date(2015, 9, i + 1)} for i in range(30)]

for ce in CalendarEntry.objects.filter(ref_date__year=2015, ref_date__month=9)
september_inits[ce.ref_date.day-1]["entry"] = ce.entry


Now the view is implemented like this:


from django.forms.formsets import formset_factory
from django.shortcuts import render_to_response
from myapp.forms import ExampleForm

def example_view(request):
september_inits = [...] # as above
ExampleFormSet = formset_factory(ExampleForm)

if request.method == 'POST':
formset = ExampleFormSet(request.POST)

# ~~~~~ Question 1 ~~~~~
# What is the best way (or: a proper way) to check if formset
# agrees to september_inits? Can I use
# formset = ExampleFormSet(request.POST, initial=september_inits)
# then follow
https://docs.djangoproject.com/en/1.8/topics/forms/formsets/#custom-formset-validation ?
# If so, how to access the "initial" data??

if formset.is_valid():
# ...
pass
else:
formset = ExampleFormSet(initial=september_inits)

# ~~~~~ Question 2 ~~~~~
# What is the best way (or: a proper way) to add per-form information,
# e.g. "September 24th is a holiday"?

return render_to_response('manage_articles.html', {'formset': formset})


The two questions have been reformulated as comments above.
Any help would very much be appreciated!

Many thanks and best regards,
Carsten

Carsten Fuchs

unread,
Oct 6, 2015, 1:03:31 PM10/6/15
to django...@googlegroups.com
Anyone please?

Best regards,
Carsten

Shawn Milochik

unread,
Oct 7, 2015, 9:55:49 AM10/7/15
to django...@googlegroups.com
You can use ModelForms for this. You don't need an active instance.

Iterate over the days in the month, and if you have an instance in your database you instantiate a ModelForm where "instance=thing." If not, you instantiate a ModelForm that has no instance, and you send as data "" (empty string) for the entry and the proper date for the ref_date. You can use the data kwarg when creating the ModelForm -- just as you'd send "request.POST" into it.

When you receive the forms back in POST, you validate and save the forms. Override the save() method of your ModelForm so that, if "entry" is blank you don't save it. Incidentally, you should also check if it already exists for that date and delete it in that case.

You can have multiple forms using "prefix," which could just be the date as a string.


Carsten Fuchs

unread,
Oct 8, 2015, 9:44:18 AM10/8/15
to django...@googlegroups.com
Hi Shawn,

many thanks for your reply.

Am 07.10.2015 um 15:54 schrieb Shawn Milochik:
> You /can/ use ModelForms for this. You don't need an active instance.

Thanks, I'll definitively check this out!

However, using a ModelForm wastes performance when the POST request is processed,
doesn't it?

This is because both in the GET and the POST request, I have to construct the queryset
in order to have the instances that the formset is about:

- in the GET request, the queryset is needed in order to construct the formset,
- in the POST request, the queryset is needed for comparison, in order to detect
unexpected changes (e.g. a calender entry that didn't exist at the time of the GET
request suddenly exists at the time of the POST request, or vice versa, etc.)

If I used ModelForms in such a formset, all objects were instantiated both in the
original queryset as well as in all the ModelForms constructed from request.POST,
essentially duplicating the database queries, wouldn't they?

> You can have multiple forms using "prefix," which could just be the date as a string.

Why would I need "prefix" if a formset is used already?

Best regards,
Carsten
Reply all
Reply to author
Forward
0 new messages