I remember running into similar situations in a different domain (collision
detection in physics engines). It was a pain in the butt :) I'd say first,
figure out what you want to do if the process does reach a point where
there isn't sufficient funds to perform the transaction. Then take multiple
steps to catch that state. Finally, implement it in a way that will leave
no side effects.

Possibly using a queue may be good for this. Let the user perform an action
which will attempt to use/move the funds. Then, actually try to perform the
operation behind the scenes. Finally, update the user's information so they
can see the change or any errors that occurred. This should
(hypothetically, assuming you only have one queue per user or account) make
sure that the transactions are performed in order and should also eliminate
race conditions. This would be similar in implementation to how many bank
web-sites let you transfer funds between accounts but it's typically not an
update you see immediately.

On Tue, Aug 21, 2012 at 4:18 AM, Sebastien Flory <sfl...@gmail.com> wrote:

> That was my idea too, but using this way, the pre_save / post_save signals
> wont be triggered.
> It seems strange to me to be using the objects manager instead of the
> model instance directly, no?
>
> Seb
>
> Le mardi 21 août 2012 02:11:42 UTC+2, Thomas Orozco a écrit :
>>
>> 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.orozc...@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" <sfl...@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<https://groups.google.com/d/msg/django-users/-/hr1fBuAcX3kJ>
>>>> .
>>>> To post to this group, send email to django...@googlegroups.com.
>>>> To unsubscribe from this group, send email to django-users...@**
>>>> googlegroups.com.
>>>>
>>>> For more options, visit this group at http://groups.google.com/**
>>>> group/django-users?hl=en<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 view this discussion on the web visit
> https://groups.google.com/d/msg/django-users/-/x8CZ3-F30PsJ.
>
> To post to this group, send email to django-users@googlegroups.com.
> To unsubscribe from this group, send email to
> django-users+unsubscr...@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+unsubscr...@googlegroups.com.
For more options, visit this group at 
http://groups.google.com/group/django-users?hl=en.

Reply via email to