Reputation: 172
So I have been having trouble trying to update a Foreign Key Model's fields. I'm trying to update the field through another model's method but it doesn't seem to be working correctly as I'm getting incorrect values when I run my tests.py. I'm trying to update the value for "total_purchased". Here's an example of what I'm essentially trying to accomplish:
I'm using Django 1.6.4
If the question is too long to sum things up, are the following 2 lines allowed? I'm doing it within a django class model method:
Ex: Model.ForeignKeyModel.field = some_value
Model.ForeignKeyModel.save()
With the following 3 models:
class Meal (models.Model):
name = models.CharField(max_length=255)
total_purchased = models.IntegerField(default=0, validators=[MaxValueValidator(capacity)])
capacity = models.IntegerField(default=500)
class Item (models.Model):
cart = models.ForeignKey(Cart)
meal = models.ForeignKey(Meal)
quantity = models.IntegerField(validators=[MinValueValidator(0)], default=0)
class Cart (models.Model):
delivery = models.ForeignKey(DeliveryInfo)
subscription = models.BooleanField(default=False)
customer = models.ForeignKey(User)
def add(self, meal, quantity):
""" Dual Add/Remove from cart method, (quantity replaces current quantity) """
item = Item.objects.get_or_create(cart=self, meal=meal)[0]
remaining_capacity = item.meal.capacity - item.meal.total_purchased
# Meal exists on our cart, so proceed with adding or removing
if quantity <= remaining_capacity:
if quantity == 0:
# Add back whatever quantity the user didn't buy
item.meal.total_purchased = 0
item.meal.save()
# Remove entire item from cart (quantity = 0)
item.delete()
elif quantity == item.quantity:
# Do nothing if cart quantity equals current quantity
pass
else:
if quantity < item.quantity:
# Decrement total purchased
item.meal.total_purchased -= (item.quantity - quantity)
item.meal.save()
else:
# Increment total_purchased by the quantity we're adding to the cart
item.meal.total_purchased += (quantity - item.quantity)
item.meal.save()
# Update the Cart
item.quantity = quantity
item.save()
remaining_capacity = item.meal.capacity - item.meal.total_purchased
else:
exceeded = abs(remaining_capacity - quantity)
raise ValidationError("Your cart exceeds the total remaining quantity by {0}!".format(exceeded))
Upon updating it works the first time around but seems to ignore the next add to cart method call and I end up with the following in my tests.py: self.assertEqual(item.total_purchased, 2) AssertionError: 1 != 2
I'm not sure if it's because the cart updating is happening in tests.py or if I am somehow saving data incorrectly. Any help would be appreciated thank you.
As requested here is my tests.py where I'm running the test from:
customer = User.objects.filter(pk=1)[0]
delivery_info = DeliveryInfo.objects.filter(owner=customer)[0]
cart_order = CartOrder.objects.create(owner=customer, delivery=delivery_info, subscription=False)
# Query the meal_items
curry_item = Meal.objects.get(pk=1)
burger_item = Meal.objects.get(pk=2)
# Add some meal items to our cart, the cart_order add method creates Items
cart_order.add(curry_item, 2)
cart_order.add(burger_item, 1)
self.assertEqual(burger_item.total_purchased, 1)
# Removing one item from the cart
cart_order.add(curry_item, 1)
# Doing nothing to the cart
cart_order.add(curry_item, 1)
** This is where it fails to correctly update the value: **
# Adding an item to the cart
cart_order.add(burger_item, 2)
self.assertEqual(burger_item.total_purchased, 2)
Upvotes: 4
Views: 4651
Reputation:
This is happening because the model instance you are using is not being updated.
The first time you call get_or_create
it creates the Item
and uses the Meal
from meal=meal
while the second time it gets the Item
and does not use the same instance of Meal
.
Since you already have an instance of meal
as a argument, you can replace item.meal
with meal
and you should see the results you want. Alternatively, you could reload burger_item
or any meal after adding.
You really should look into using F() Expressions to do you increments though since these execute solely on the database side and can help prevent race conditions. Be aware that using the F() Expression would require you to reload the Meal
to see it's new value.
On a side note, watch out for
# Update the Cart
item.quantity = quantity
item.save()
being called after the case where quantity == 0
and you delete the Item
since this would recreate the Item
.
Upvotes: 5