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
Post a Comment