Saman
Saman

Reputation: 145

Model methods stop working correctly after first call

I have defined two models for retailers and items in their stock. Product is defined in another app. I defined two model methods to get and add stock items. Here is the relevant part of the code:

class Retailer(models.Model):
    name = models.CharField(max_length=100)
    @property
    def stock_items(self):
        return StockItem.objects.filter(retailer__id=F('id'))

    def add_stock_item(self, product_id):
        try:
            print "Checking if it's already in stock"
            self.stock_items.get(product__id=product_id)
            print "It's already in stock"
        except ObjectDoesNotExist:
            try:
                print "Adding item to stock"
                product = Product.objects.get(pk=product_id)
                StockItem.objects.create(retailer=self, product=product)
                print "Added to stock"
            except Product.DoesNotExist:
                print "Such product doesn't exist"
    def __unicode__(self):
        return self.name


StockItem(models.Model):
    retailer = models.ForeignKey(Retailer)
    product = models.ForeignKey(Product)
    def __unicode__(self):
        return "%s - %s %s" % (self.retailer, self.product.brand, self.product)

When I want to use these model methods, something weird happens. They stop working correctly after adding the first item (In these examples, product 1 is 16 GB iPhone and Product 2 is 32 GB iPhone).

First let's add some products to our stock:

>>> r = Retailer.objects.get(pk=1)
>>> r.stock_items
[]
>>> r.add_stock_item(1)
Checking if it's already in stock
Adding item to stock
Added to stock
>>> r.add_stock_item(2)
Checking if it's already in stock
Adding item to stock
Added to stock

So far so good. Now let's try adding products again to see how it handles errors:

>>> r.add_stock_item(1)
Checking if it's already in stock
It's already in stock
>>> r.add_stock_item(2)
Checking if it's already in stock
Adding item to stock
Added to stock

What? Why did it add product 2 again. It was supposed to show a message similar to product 1. Let's see our stock:

>>> r.stock_items
[<StockItem: hh - Apple iPhone 4S 16GB>]

What happened to product 2? Did it fail to add it to database?

[<StockItem: hh - Apple iPhone 4S 16GB>, <StockItem: hh - Apple iPhone 4S 32GB>, <StockItem: hh - Apple iPhone 4S 32GB>]

Apparently not. It was added to database, but somehow our program fails to check it correctly. Only the first product added to stock is shown by calling r.stock_items. Restarting shell doesn't change the situation either, so I guess the reason couldn't be because of when the function is evaluated.

Why does this happen and how can I fix it?

Upvotes: 0

Views: 82

Answers (2)

Daniel Roseman
Daniel Roseman

Reputation: 599610

The reason why your method was failing is because that expression doesn't do what you think it does. The F() syntax is for comparing values within the same row: so that expression was checking that the related retailer ID was the same as the current row's ID. It so happened that this was true for the first item, but it would never be true of any subsequent items, because the row ID has incremented with the new items but the retailer ID remains the same.

Upvotes: 1

user1724345
user1724345

Reputation:

Try to remove @property and change the method to this:

def stock_items(self):
    return self.stockitem_set.all()

Upvotes: 1

Related Questions