Hi Michael, On Jan 30, 6:25 pm, Michael Hipp <[EMAIL PROTECTED]> wrote: > James Bennett wrote: > > On Jan 30, 2008 9:18 AM, Michael Hipp <[EMAIL PROTECTED]> wrote: > >> Does Django have any built-in way to handle or prevent simultaneous, > >> incompatible edits to a database record? > > > No, that's what your database's concurrency handling is for. > > Ok, I probably didn't explain well enough... > > I'm using PostgreSQL so concurrency isn't a problem. > > What is a problem is that user A loads a record and begins making changes. > Then user B loads the same record and begins making different changes. One of > them saves it first followed by the other and the only trace that's left is > what was saved by the one that got there last. > > No real issue at the database level. PG doesn't care. But possibly a very big > issue at the application level (especially if each of the saves also modified > related tables based on the different inputs of each user) and the application > level of each could not know about the other so opportunities to prevent this > disaster are few at the app level.
The way in which you handle the application-level transactions that span across a set of requests it entirely up to you :-) Thinking of typical scenarios you could either synchronize data in real time (something like google docs), synchronize on save (some kind of diff and merge), warn users before they start editing a record which is already opened for edit by another user (like in MoinMoin) or just overwrite or ignore changes (like in all bad-user-experience-apps). The third option seems quite reasonable and its also not very hard to implement. All what you will need is a (generic) registry that will hold information about checked out objects (rows, records, documents - whatever the naming convention is) and some locking logic. The registry could be simply a model with three fields: [generic] model foreign key, user foreign key, and a checkout time, with unique constraint on the model foreign key. If you don't need to track different models then a "classic" foreign key instead of a generic contenttypes one should be just fine. The functions that will provide a locking interface should be atomic (that's the place where the database level transactions come into play). A typical pattern is to have at least an acquire (lock) and a release (unlock) routines, plus a couple of additional helpers for the typical use cases: * acquire_lock(object, user) - creates an entry/replaces expired one in the registry, allows for re-entrant locking (the same lock can be obtained many times by the user that already possess it without raising an error), raises an exception if an object is already locked by another user and the lock period hasn't expired. * release_lock(object, user) - removes lock from the registry * is_locked(object, user) - checks if a valid object lock entry exists Cyclic removal of expired entries from the registry should not be necessary unless you count for every byte or need to reduce number of queries (in a typical case they will consume considerably less space than the other models' data). After setting this up you should have a pretty robust mechanism for controlling parallel access attempts to database records/objects. Just lock the object on edit, release the lock on save and issue a warning on concurrent edit attempts. > I've seen some table layouts that included a column like "last_edit_serial" so > that a steadily incrementing serial could provide a hint to the app level that > something had changed since the record was loaded. Only when you want to notify someone that his effort has just been cancelled because someone else has corrected a typo in the meantime and saving changes is not permitted ;-) > But I don't see Django adding any such columns and was wondering if a > different mechanism was in effect. Or if some other technique was widely used > among Django users. What makes me wonder even more is the fact how fantastically people seem to get away without all this stuff :-) Hope that helps, f -- http://filipwasilewski.pl --~--~---------~--~----~------------~-------~--~----~ You received this message because you are subscribed to the Google Groups "Django users" group. To post to this group, send email to django-users@googlegroups.com To unsubscribe from this group, send email to [EMAIL PROTECTED] For more options, visit this group at http://groups.google.com/group/django-users?hl=en -~----------~----~----~----~------~----~------~--~---