Thursday, October 8, 2015

Re: formset concurrency control

Hi Daniel,

Am 08.10.2015 um 18:48 schrieb Daniel Roseman:
> Can you explain further why you think the pk is not sufficient? Ordering by name, or
> adding and removing entities, does not change the pk of other entities; indeed that's
> the whole point of a pk. What exactly are you concerned about?

Please consider this example:


from django.db import models

class Calendar(models.Model):
pass

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

class Meta:
unique_together = ('cal', 'ref_date')


We would like to present the user a formset (for a specific Calendar instance) where
e.g. October 2015 is shown in tabular form: each day of the month is shown with a static
date string, and next to it is an input field for the "entry" text.

The key issue is of course that for days that don't have an entry, no CalenderEntry
instance exists.

Now, the GET request is relatively easily dealt with (as explained in the other thread
https://groups.google.com/d/msg/django-users/jA2KUdp1MUE/pceQZPYHBgAJ, I intentionally
use a plain Form, not a ModelForm, both for better understanding and, to me, unclear
performance implications):


from django import forms

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


The form's ref_date member is needed for all cases where a CalenderEntry instance does
not (yet) exist. In the view's part where the GET request is processed, the formset
would be constructed e.g. like this:


# Example is specific to October:
from datetime import date
october_inits = [{"ref_date": date(2015, 10, i + 1)} for i in range(31)]

for ce in CalendarEntry.objects.filter(cal=MyCal, ref_date__year=2015,
ref_date__month=10):
init = october_inits[ce.ref_date.day - 1]

init["CalEntr_id"] = ce.id
init["entry"] = ce.entry

CalenderEntryFormSet = formset_factory(CalenderEntryForm)
formset = CalendarEntryFormSet(initial=october_inits)


So far, all is good, but problems start to occur when we get the form back in the POST
request. When comparing the data from the POST request to those of a newly created
october_inits list, CalenderEntry instances mentioned in the POST request may have been
deleted in the meanwhile or been replaced by an entirely different CalenderEntry
instance. On other days on which the POST data assumed that a CalenderEntry did not yet
exist, an instance may be existing now, etc.

Obviously, the number of days in October is fixed at 31, but if this was something else,
such as a list of persons to which CalenderEntry objects are attached, more or fewer
persons could exist when the POST request is processed.

While some of these cases can be detected with the pk alone, and others possibly with
the help of the unique-constraint and the related exception, I'm not sure if this can
really cover all the cases.

For example, if someone tampered with the POST request and submitted a wrong pk – from
the pk and the related instance alone we can probably not learn that there is a problem,
and what it is.

Well, I readily admit that this hits the limits of my skills and thoroughly confuses me,
thus my question how all this is best or typically dealt with. :-)

Best regards,
Carsten

--
You received this message because you are subscribed to the Google Groups "Django users" group.
To unsubscribe from this group and stop receiving emails from it, send an email to django-users+unsubscribe@googlegroups.com.
To post to this group, send email to django-users@googlegroups.com.
Visit this group at http://groups.google.com/group/django-users.
To view this discussion on the web visit https://groups.google.com/d/msgid/django-users/5616CD6D.7050605%40cafu.de.
For more options, visit https://groups.google.com/d/optout.

No comments:

Post a Comment