Hi Nick, maybe this is a case for optimistic locking? Does the thread at https://groups.google.com/d/msg/django-users/R7wJBTlC8ZM/MIzvYkWyCwAJ help?
Best regards, Carsten Am 27.12.21 um 06:36 schrieb Nick Farrell: > Hi all. > > I've been using Django for quite a number of years now, in various ways. Most > of the time, I find myself needing to create custom solutions to solve what > appears to be a very common problem. > > During the Christmas downtime, I decided to scratch this itch, and am putting > together what will hopefully turn into a solution to what I'll describe > below. I'm writing this here to get a sense of what the Django community sees > in this: is this a niche problem, is it shared by a few others, or is the > lack of these features a fundamental overnight in the core Django product? > > *The problems *(from highest to lowest priority)*:* > * > * > *1)* a form is rendered, the data is changed by a different task/request, > then the form is submitted, overwriting the recent changes. > > Whenever models can be modified by multiple users (or even the same user in > different windows/tabs of their browser), this can happen. Also, if there are > any background processes which can modify the data (e.g. celery, or various > data synchronisation services), it's possible. > In some situations this is no big deal, as the users do not really care, or > you know that the latest data would overwrite the previous data anyway. But > in general, this is a major risk, particularly when dealing with any health > or financial data. > > *2)* Not being able to safely lock a model/queryset beyond the lifetime of > the request. > > This is related to problem 1, and solving problem 2 may in some circumstances > solve problem 1 - but not always. For example, depending on how the lock is > implemented, a "rogue" task/request may bypass the locking mechanism and > force a change to the underlying data. Also, if a lock is based on a session, > a user may have multiple tabs open in the same browser, using the same > session state (via shared cookies) > > Solving this problem will reduce the chance that when a person does post a > form update, that there is any conflict, meaning fewer tears. > > *3)* Not knowing that data has changed on the server until you submit a form. > > Ideally there would be a means for someone viewing/editing a form to > immediately be notified if data changes on the server, obsoleting the current > form. This reduces the amount of wasted time is spent completing a form which > is already known to be out of sync, and will need to be redone anyway (as > long as problem 1 is solved; otherwise, there'll be data loss) > > *4)* Smarter form validation > > There are three types of missing validation: > - the first is that the default widgets do not support even very simple > client-side validation. For example, a text field might need to match a > regular expression. > - the second type is an ability to provide (in the model definition) > arbitrary javascript which can be executed client-side to provide richer > realtime validation during data entry. > - the third type involves effectively providing provisional form data to the > server, and having Django validate() the form content without actually saving > the result. This would allow (for example) inter-field dependencies to be > evaluated without any custom code, providing near-realtime feedback to the > user that their form is invalid > > > *The solutions* > This is based on a day or so's experimentation, and I very much welcome any > feedback, both in terms of the usefulness of solving these problems in > general, as well as suggestion on better ways to solve the problems, before > I go too far down any rabbit holes. > > *Enhanced forms* > - when rendering a form (using e.g. as_p()), alongside the normal INPUT DOM > elements, include additional hidden fields which store a copy of each form > field's initial value. > - when a form is submitted, compare these hidden values against the current > value in the database. If any of these do not match, the clean() method can > raise a ValidationError, allowing the user to know what has happened, and > that they will need to reload the form and try again, with the new stored > values. > > This solution is minimally invasive. As well as modifying as_p() and friends, > a django template tag can also be exposed for those users who are rendering > their forms in a different way. > Note that there is no reliance on additional attributed in the models: the > CAS-like checking performed is explicitly on the rendered form fields; it > does not matter if other model fields' values have changed, as someone > editing the form can neither see these field values, nor will their POSTing > modify these other fields' values. > (I have implemented the above already, for generic model forms using a single > model) > > *Locking* > - provide a mixin which can be used on selected models. When used, a view > (usually some sort of form view) can attempt to lock() the model. If > successful (because it's not currently locked to someone else), only they can > perform writes to the model, until the lock expires. > - if the lock has expired, anyone (including the user who took out an expired > lock) may overate on the model instance. > - the lock can be configured to either use the standard database ORM, or > redis. Redis will be more performant, but should not be a hard requirement > - there will be pain points associated with using this without the websocket > solution, detailed below: there will not be a clean way to maintain the lock, > if the time between consecutive requests is greater than the timeout value > > *Websocket* > - provide a model mixin to enable websocket monitoring > - use Django Channels to expose a websocket consumer > - provide a templatetag which will include appropriate javascript into a web > page to initialise the client connection (if any forms are configured to be > monitored) > - when the client initialises, it detects the form fields (as per the > 'Enhanced Forms' solution) and registers the model instance(s) with the > server, via the websocket. > - whenever a monitored instance changes in Django, a signal is raised, > pushing notifications to any clients, along with the new values > - the client can immediately compare the new instance values to the original > values on the form (stored in the hidden fields) and can update the widgets > directly if required (e.g. setting a CSS class to indicate the input is > invalid, and updating the validation message shown alongside that. > > > A final aspect of the solution is the javascript widgets, but I feel my post > is already about 5 times too long. > > Any thoughts/comments are welcome. > > Thanks. > > -- > 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 [email protected] > <mailto:[email protected]>. > To view this discussion on the web visit > https://groups.google.com/d/msgid/django-users/e9d6ee80-19d2-4ca2-aa1b-10daf7217182n%40googlegroups.com > > <https://groups.google.com/d/msgid/django-users/e9d6ee80-19d2-4ca2-aa1b-10daf7217182n%40googlegroups.com?utm_medium=email&utm_source=footer>. -- 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 [email protected]. To view this discussion on the web visit https://groups.google.com/d/msgid/django-users/1a689b41-e86c-9127-e579-022c48e53c21%40cafu.de.

