Thursday, October 29, 2015

Re: formset concurrency control

Hi all,

continuing my previous post, I've implemented most of the ideas mentioned in this thread
now, and would like, for completeness and future reference, add some related findings
and thoughts:

As mentioned in my previous post, reconstructing the formset's initial data (the
queryset) in the POST request by the same parameters that were used to construct the
queryset for the initial formset data in the GET request, in *addition* to the primary
keys received in the POST request, turned out to work very well. That is, the code is
roughly like this:

# Inits is made from the view's appropriate queryset.
Inits = ...

# In the case of the GET request:
formset = TestFormSet(initial=Inits)

# In the case of the POST request:
formset = TestFormSet(request.POST, initial=Inits)


This is how the initial data is useful in the POST request:

- It is required for validation in order to be able to detect "high-level" kinds of
concurrency related mismatches (number of instances changed, deleted instances,
unexpected new or replaced instances).

- We also need the same data for "low-level" concurrency control, e.g. checking a
`VERSION` number as e.g. in [1] and [2].

- As a side effect of the same validation steps, tampering with the POST data is
detected as well.

- The initial data (`Inits` above) is also a convenient place for storing arbitrary
extra data, e.g. the actual model instances, that can be used for rendering additional
information in the template.

- In the case of successful formset validation, the readily available queryset
instances can be used for storing and saving the submitted data.


I still use custom Forms for all this, not ModelForms, because:

- As we need the model instances anyway and already got them via the queryset as
described above, using a ModelForm(-set) that implicitly instantiates them all again
would not be efficient.

- I have a lot of cases where I have a field flagged as "required" in the model, but
"not required" in the form. This helps with making entire forms optional in the formset
(won't get saved if not filled out), but makes clear what is required when a model is
eventually saved. This is especially helpful when forms are rendered for which not
necessarily a model instance exists and is not necessarily created, as e.g. in my
CalenderEntry example described in another post of this thread ([3]).

- Foreign keys are problematic. Although a ModelForm covers a FK just as it covers
the model's PK, if we wish to extend the concept of validating `VERSION` numbers (as
above) also to the related models, the form must be augmented with an appropriate
version number field for each FK. (I have not checked, but adding such extra fields is
probably possible with ModelForms as well.)


Well, so far my findings. Although this seems to work well, I'd very much appreciate any
further comments and thoughts.

Best regards,
Carsten


[1] https://github.com/saxix/django-concurrency
[2] https://github.com/gavinwahl/django-optimistic-lock
[3] https://groups.google.com/d/msg/django-users/R7wJBTlC8ZM/N3dNlMrGCwAJ


--
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/5632477F.4050107%40cafu.de.
For more options, visit https://groups.google.com/d/optout.

No comments:

Post a Comment