Rahul Sharma
Rahul Sharma

Reputation: 2495

update an object in django model

I have a model in my app named foo

class foo(models.Model):
    a = models.CharField(max_length=500, default=0,null=True)
    a_quantity = models.CharField(max_length=500, default=0, null=True)

and I am updating this model from another form like this:

 def form_valid (self, form):
        product = form.save(commit=False)
        product.save()
        data = form.cleaned_data
        for i in range(1, 9):
            foo.objects.update_or_create(a=data["a" + str(i)],
                                                     b=data["a_quantity" + str(i)])

But the problem here is that it creates a new entry if a is same but a_quantity. I want that when a is repeated its quantity should be summed to the existing quantity in the model.

For eg id my input is this:

bar     10

and the table is like this:

a   a_quantity

nar  27
bar  15
lar  10

then when the function is called it should do this:

a   a_quantity

nar  27
bar  25
lar  10

Currently it creates a new entry with the same name and different quantity. How can I do this ?

Upvotes: 1

Views: 148

Answers (2)

hundredrab
hundredrab

Reputation: 377

I don't understand why you need this:

for i in range(1, 9):
        foo.objects.update_or_create(a=data["a" + str(i)],
                                                     b=data["a_quantity" + str(i)])

Maybe we could better help you if you show us your forms.py.

Do not save() the product if you don't want to save it. Assuming that bar value should be 25 instead of 15 as you've shown in the example, you can use get_or_create instead:

def form_valid (self, form):
    data = form.cleaned_data
    product, created = foo.objects.get_or_create(
                         a=data['a'],
                         defaults={'a_quantity': 0}
                       )
    product.a_quantity += data['a_quantity']
    product.save()

First a product is created with quantity as 0 if it does not exist, otherwise it is retrieved. Next, the quantity is updated.

A better option would be to use F expressions here instead. This avoids race conditions and inconsistency when multiple people try to update the same object instance.

from django.db.models import F

def form_valid (self, form):
    data = form.cleaned_data
    product, created = foo.objects.get_or_create(
                         a=data['a'],
                         defaults={'a_quantity': 0}
                       )
    product.a_quantity = F('a_quantity') + data['a_quantity']
    product.save()

Upvotes: 0

willeM_ Van Onsem
willeM_ Van Onsem

Reputation: 477608

You set the update value in the defaults=… parameter of the .update_or_create(..) method [Django-doc]:

for i in range(1, 9):
    foo.objects.update_or_create(
        a=data['a' + str(i)],
        defaults={'a_quantity': data['a_quantity' + str(i)])}
    )

or you can increment the value with the given a_quantity with:

from django.db.models import F

for i in range(1, 9):
    foo.objects.filter(
        a=data['a' + str(i)]
    ).update(
        a_quantity=F('a_quantity')+data['a_quantity' + str(i)]
    )

I'm however not sure that this is a good idea, since one often aims to make requests idempotent. We can create a record in case no object to update exists:

from django.db.models import F

for i in range(1, 9):
    exists = foo.objects.filter(
        a=data['a' + str(i)]
    ).update(
        a_quantity=F('a_quantity')+data['a_quantity' + str(i)]
    )
    if not exists:
        foo.objects.create(a=data['a' + str(i)], a_quantity=data['a_quantity' + str(i)])

Some remarks:

  1. It might make more sense to define the a field as a non-NULLable unique field;
  2. normally a model is written in PerlCase, so Foo, not foo;
  3. you might want to use 'a_quantity{}'.format(i) over constructing strings;
  4. using a range(..) might not be the best option here, since if there are less than nine updates, it will raise an error, and if there are more, these will not be handled.

Upvotes: 2

Related Questions