Home > Net >  Django form update optimistic locking, version based
Django form update optimistic locking, version based

Time:01-13

I've got django model and view implemented like here: ( mysql db)


class MyModel(models.Model): 
    name = models.CharField(max_length=100)
    version = models.IntegerField(default=1, editable=False)

def updateModel(request, id): 
    toUpdate = MyModel.objects.get(pk=id)    
    if request.method=='POST':
        form = MyModelForm(request.POST,  instance=toUpdate)
        if form.is_valid(): 
        actual =  MyModel.objects.get(pk=id)    
        if (actual.version == form.instance.version):
            form.instance.version = form.instance.version 1
            form.save()
            return redirect('somewhere')
        else:
            #some error
            
    form = MyModelForm(instance=toUpdate)
    return render(request, 'somwhere2/createupdate.html', {'form':form})

The scenario is: - current model values: name="aaa", version=1,

2 users open edit form, first user changes name "aaa" to "bbb", and saves, second changes name "aaa" co "ccc" and saves. Result is "ccc", but I'd like to have some message/version conflict message... The problem is.. there is no conflict, because even if the second user can see still "aaa", while in DB there is "bbb" already... but after POST button click, the values are updated to bbb first, and version is updated, so the code is unable to see, that user2 works on old version... :(

I'd like that versioning mechanism to prevent such scenario, but I'm unable to achieve it...

How to implement it?

I've read everything I could about django optimistic locking etc, but unable to achieve it,

CodePudding user response:

I think using select_for_update() might solve your problem. Check out this article.

I was thinking of something like this:

def updateModel(request, id): 
    toUpdate = MyModel.objects.get(pk=id)    
    if request.method=='POST':
        form = MyModelForm(request.POST,  instance=toUpdate)
        if form.is_valid(): 
            with transaction.atomic():
                try:
                     actual =  MyModel.objects.filter(pk=id).select_for_update(nowait=True).get()
                except OperationalError:
                    # raise some error 
                if (actual.version == form.instance.version):
                    form.instance.version = form.instance.version 1
                    form.save()
                    return redirect('somewhere')
                else:
                    #some error
            
    form = MyModelForm(instance=toUpdate)
    return render(request, 'somwhere2/createupdate.html', {'form':form})

CodePudding user response:

I believe I've found a problem. It's here:

   in Model:  version =(...) editable=False

So when field is not editable - it is not placed in form, so you're loosing information about version number... And are unable to compare initially loaded version, with actual version.


it is still not thread-safe, but in general- blocks typical attempts to edit and save form by 2 users.

  • Related