select - Where should I lock a model to prevent race conditions while validating it on a Django admin change page? -


the simplified case:

models.py:

class candidate(model):     first_name = charfield(max_length=64)     last_name = charfield(max_length=64)  class ward(model):     no = positiveintegerfield(unique=true)     address = charfield(max_length=1024)     population = positiveintegerfield()  class votes(model):     candidate = foreignkey(candidate, on_delete=cascade)     ward = foreignkey(ward, on_delete=cascade)     amount = positiveintegerfield() 

in each ward each candidate may receive votes.amount votes , winner of election candidate receives votes aggregated on wards. these votes editable in django admin:

admin.py:

class votesinline(stackedinline):     model = votes  @register(ward) class wardadmin(modeladmin):     inlines = (votesinline,) 

now validation issues kick in. sum of votes.amount candidates must not exceed ward's total ward.population. however, such validation meaningless unless ward remains locked duration of validation of related votes! else race condition may kick in if user modifies ward.population while user b modifies votes.amount related ward.

i know should lock ward select_for_update, not know should lock lock lasts validation of related votes. thinking doing in get_queryset, ofc horrible idea, lock while browsing, , not modyfying.

ps1: no, assuming such race condition rare , therefore unworthy of consideration out-of-the-question. ps2: yes, need lock ward, because votes not meant edited independently. ps3: prefer locking maintaining versions.

edit: exact kind of race condition need prevent: john doe has received 15 votes in ward has population of 40. user lowers population 20 while user b raises j.doe's result 30 votes. should lead fail validation because of breakage of invariant sum of votes must <= ward's population; however, if 2 django threads / processes process these request simultaneously, may happen a's requests gets processed , saved database after b's request has validated before b's request has saved; , result internal inconsistency in database introduced j.doe has received more votes number of people eligible vote.

however, of now, don't care race condition of kind: user tries withdraw 5 votes doe's result while user b adds 3 votes; both simultaneously load view , first sets number of votes 10, while b, unaware of a's change, sets number of votes 15+3=18, a's change lost.

as result not want call select_for_update each time change view being loaded; duration of validation methods of forms on view when these forms submitted.

i thinking if can find other options solving issue. still don't have alternative. when assume update vote in admin page , want lock it, write below.

from django.db import transaction  class exampleadmin(admin.modeladmin):     def save_model(self, request, obj, form, change):         transaction.atomic():             vote = (obj.__class__.objects.select_for_update()get(id=obj.id))             # select_for_update() locks ward because of fk relationship             if vote.amount > vote.ward.population:                 vote.amount = obj.amount                 vote.save()             else:                 pass                  # if amount not beigger population,                 # pass without saving.          super().save_model(request, obj, form, change)  admin.site.register(votes, exampleadmin) 

Comments

Popular posts from this blog

Is there a better way to structure post methods in Class Based Views -

performance - Why is XCHG reg, reg a 3 micro-op instruction on modern Intel architectures? -

jquery - Responsive Navbar with Sub Navbar -