Monday, August 20, 2012

Re: Updating a model instance with extra checks.

As a followup to the suggestion of MyModel.objects.filter(money__gte = value, pk = self.pk).update(F...)

Here's an example:

We have testapp/models.py:

from django.db import models
class TestModel(models.Model):
    balance =  models.IntegerField()


>>> from django.db.models import F
>>> TestModel.objects.create(balance = 5) #Pk will be 1 I just create one. 
>>> import logging
>>> l = logging.getLogger('django.db.backends')
>>> l.setLevel(logging.DEBUG)
>>> l.addHandler(logging.StreamHandler())
>>> TestModel.objects.filter(balance__gte = 4, pk = 1).update(balance = F('balance') - 4)
(0.001) UPDATE "testapp_testmodel" SET "balance" = "testapp_testmodel"."balance" - 4 WHERE ("testapp_testmodel"."balance" >= 4  AND "testapp_testmodel"."id" = 1 ); args=(4, 4, 1)
1
>>> TestModel.objects.filter(balance__gte = 4, pk = 1).update(balance = F('balance') - 4)
(0.000) UPDATE "testapp_testmodel" SET "balance" = "testapp_testmodel"."balance" - 4 WHERE ("testapp_testmodel"."balance" >= 4  AND "testapp_testmodel"."id" = 1 ); args=(4, 4, 1)
0



So this seems to generate a single SQL statement.

I'm not totally familiar with database administration though, so as Melvyn rightly pointed out, it's always better if you can get the extra security of having an SQL constraint into your dabatase and wrap your queries in a transaction (as you'll probably be adding a line to the statement if the withdrawal succeeds).


2012/8/20 Thomas Orozco <g.orozco.thomas@gmail.com>

A few suggestions :

Circumvent the problem with smarter design: don't store the money on the object, make the user's money the sum of all their transactions (credit - debit).
You get lesser performance, but you also get history!

Maybe you could try (not sure about that):

MyModel.objects.filter(money__gte = value, pk = self.pk).update(F...)

and inspect the return value (number of updated rows!).
Now, you'd need to make sure django does that in a single statement.

If that doesn't work, I think update is the way to go anyway, but it might get a bit messy.

F... is an F object whose syntax I don't have off the top of my head.

Le 20 août 2012 18:54, "Sebastien Flory" <sflory@gmail.com> a écrit :

Hi everyone,

I'm looking for the proper django way to do an update of an attribute on my model instance, but only if the attribute current value is checked agains't a condition, in an atomic way, something like this:

def use_money(self, value):
  begin_transaction()
  real_money = F('money')
  if real_money >= value:
    self.money = F('money') - value
    self.save()
  end_transaction()

I want to make sure that I avoid race condition so money never goes below 0.

Can you help me out?

Thanks,

Sebastien

--
You received this message because you are subscribed to the Google Groups "Django users" group.
To view this discussion on the web visit https://groups.google.com/d/msg/django-users/-/hr1fBuAcX3kJ.
To post to this group, send email to django-users@googlegroups.com.
To unsubscribe from this group, send email to django-users+unsubscribe@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/django-users?hl=en.

--
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 django-users+unsubscribe@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/django-users?hl=en.

0 Comments:

Post a Comment

Subscribe to Post Comments [Atom]

<< Home


Real Estate