3

Below is my models that are used in view where errors occur and error occurs in StockQuantity model particularly, when i try to filter or use get to retreive query it says expected number but got 'stringvalue'

models.py

# Item
class Item(models.Model):
    title = models.CharField(max_length=100)
    price = models.FloatField()
    discount_price = models.FloatField(blank=True, null=True)
    category = models.ForeignKey(
        'Category', on_delete=models.CASCADE, null=True)
    label = models.CharField(choices=LABEL_CHOICES, max_length=1)
    slug = models.SlugField()
    description = models.TextField()
    # stock_quantity = models.IntegerField(blank=True, null=True)
    cover_image = models.ImageField(blank=True, null=True,
                                    upload_to=item_cover_upload_location, default='no-product-image.jpg')

    is_footwear = models.BooleanField(default=False)
    upload_date = models.DateTimeField(default=timezone.now)

# StockQuantity
class StockQuantity(models.Model):
    item = models.ForeignKey('Item', on_delete=models.CASCADE, null=True)
    color = models.ForeignKey(
        'ItemColor', on_delete=models.CASCADE)
    cloth_size = models.ForeignKey(
        'ClothSize', on_delete=models.CASCADE, blank=True, null=True)
    footwear_size = models.ForeignKey(
        'FootwearSize', on_delete=models.CASCADE, blank=True, null=True)
    stock_quantity = models.IntegerField(null=True, default=10)

#OrderItem
class OrderItem(models.Model):
    user = models.ForeignKey(
        settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
    item = models.ForeignKey(Item, on_delete=models.CASCADE)
    quantity = models.IntegerField(default=1)
    footwear_size = models.CharField(max_length=50, null=True)
    cloth_size = models.CharField(max_length=50, null=True)
    color = models.CharField(max_length=50, null=True)
    ordered = models.BooleanField(default=False)

#Order
class Order(models.Model):
    user = models.ForeignKey(
        settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
    ref_code = models.CharField(max_length=20, blank=True, null=True)
    items = models.ManyToManyField(OrderItem)
    start_date = models.DateTimeField(auto_now_add=True)
    ordered_date = models.DateTimeField()
    ordered = models.BooleanField(default=False)
    shipping_address = models.ForeignKey(
        'Address', related_name='shipping_address', on_delete=models.SET_NULL, blank=True, null=True)
    billing_address = models.ForeignKey(
        'Address', related_name='billing_address', on_delete=models.SET_NULL, blank=True, null=True)
    payment = models.ForeignKey(
        'Payment', on_delete=models.SET_NULL, blank=True, null=True)
    coupon = models.ForeignKey(
        'Coupon', on_delete=models.SET_NULL, blank=True, null=True)
    being_delivered = models.BooleanField(default=False)
    received = models.BooleanField(default=False)
    requested_refund = models.BooleanField(default=False)
    refund_status = models.BooleanField(default=False)

#Payment
class Payment(models.Model):
    user = models.ForeignKey(settings.AUTH_USER_MODEL,
                             on_delete=models.SET_NULL, blank=True, null=True)
    paypal_transaction_id = models.CharField(max_length=50)
    amount = models.FloatField()
    timestamp = models.DateTimeField(default=timezone.now)

# ItemColor
class ItemColor(models.Model):
    item_color = models.CharField(
        max_length=25, null=True, blank=True)
    item = models.ForeignKey('Item', on_delete=models.CASCADE, null=True)

    def __str__(self):
        return self.item_color

# ClothSize
class ClothSize(models.Model):
    cloth_size = models.CharField(
        max_length=25, null=True, blank=True)
    item = models.ForeignKey('Item', on_delete=models.CASCADE, null=True)

    def __str__(self):
        return self.cloth_size

# FootwearSize
class FootwearSize(models.Model):
    footwear_size = models.CharField(
        max_length=25, null=True, blank=True)
    item = models.ForeignKey('Item', on_delete=models.CASCADE, null=True)

Below is my views.py and specifically the view where the error is thrown, I have placed a comment for further help to acknowledge where error occurs particularly

Views.py

# View where the error occurs
def payment_complete(request):
    body = json.loads(request.body)

    order = Order.objects.get(
        user=request.user, ordered=False, ref_code=body['orderid'])
    payment = Payment(
        user=request.user,
        paypal_transaction_id=body['payid'],
        amount=order.get_total()
    )
    payment.save()
    order.ordered = True
    order.payment = payment
    order.received = True
    order.save()
    order_items = order.items.all()
    order_items.update(ordered=True)

    for item in order_items:
        if item.item.is_footwear:\
            # ERROR OCCURS BELOW ---------
            st = StockQuantity.objects.get(
                item=item.item, color=item.color, footwear_size=str(item.footwear_size))
        else:
            # ERROR OCCURS BELOW ---------
            st = StockQuantity.objects.get(
                item=item.item, color=item.color, cloth_size=str(item.cloth_size))
        if st.stock_quantity > 0:
            st.stock_quantity -= item.quantity
            st.save()
            item.save()
            item.item.save()
        else:
            messages.warning("Item out of stock!!")
        print("Quantity " + str(st.stock_quantity))
    # order_item = OrderItem.objects.get(user=request.user, ordered=True)

    messages.success(request, "Order placed successfully!!")
    return redirect("/")

Traceback and Errors

Django version 3.1.1, using settings 'djecommerce.settings'
Starting development server at http://127.0.0.1:8000/      
Quit the server with CTRL-BREAK.
[25/Nov/2020 11:23:42] "GET /product/dummy-item-1/ HTTP/1.1" 200 15564
[25/Nov/2020 11:23:45] "POST /add-to-cart/dummy-item-1 HTTP/1.1" 302 0
[25/Nov/2020 11:23:45] "GET /order-summary/ HTTP/1.1" 200 9206
[25/Nov/2020 11:23:46] "GET /checkout/ HTTP/1.1" 200 37908
Using the defualt shipping address
Using the defualt billing address
[25/Nov/2020 11:23:51] "POST /checkout/ HTTP/1.1" 302 0
[25/Nov/2020 11:23:51] "GET /payment/paypal/ HTTP/1.1" 200 11866
Internal Server Error: /payment-complete/
Traceback (most recent call last):
  File "C:\Users\halfs\Desktop\DJango\Django-ecommerce\env\lib\site-packages\django\db\models\fields\__init__.py", line 1774, in get_prep_value
    return int(value)
ValueError: invalid literal for int() with base 10: 'S'

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "C:\Users\halfs\Desktop\DJango\Django-ecommerce\env\lib\site-packages\django\core\handlers\exception.py", line 47, in inner
    response = get_response(request)
  File "C:\Users\halfs\Desktop\DJango\Django-ecommerce\env\lib\site-packages\django\core\handlers\base.py", line 179, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "C:\Users\halfs\Desktop\DJango\Django-ecommerce\core\views.py", line 446, in payment_complete
    st = StockQuantity.objects.get(
  File "C:\Users\halfs\Desktop\DJango\Django-ecommerce\env\lib\site-packages\django\db\models\manager.py", line 85, in manager_method
    return getattr(self.get_queryset(), name)(*args, **kwargs)
  File "C:\Users\halfs\Desktop\DJango\Django-ecommerce\env\lib\site-packages\django\db\models\query.py", line 418, in get
    clone = self._chain() if self.query.combinator else self.filter(*args, **kwargs)
  File "C:\Users\halfs\Desktop\DJango\Django-ecommerce\env\lib\site-packages\django\db\models\query.py", line 942, in filter
    return self._filter_or_exclude(False, *args, **kwargs)
  File "C:\Users\halfs\Desktop\DJango\Django-ecommerce\env\lib\site-packages\django\db\models\query.py", line 962, in _filter_or_exclude
    clone._filter_or_exclude_inplace(negate, *args, **kwargs)
  File "C:\Users\halfs\Desktop\DJango\Django-ecommerce\env\lib\site-packages\django\db\models\query.py", line 969, in _filter_or_exclude_inplace
    self._query.add_q(Q(*args, **kwargs))
  File "C:\Users\halfs\Desktop\DJango\Django-ecommerce\env\lib\site-packages\django\db\models\sql\query.py", line 1358, in add_q
    clause, _ = self._add_q(q_object, self.used_aliases)
  File "C:\Users\halfs\Desktop\DJango\Django-ecommerce\env\lib\site-packages\django\db\models\sql\query.py", line 1377, in _add_q
    child_clause, needed_inner = self.build_filter(
  File "C:\Users\halfs\Desktop\DJango\Django-ecommerce\env\lib\site-packages\django\db\models\sql\query.py", line 1319, in build_filter
    condition = self.build_lookup(lookups, col, value)
  File "C:\Users\halfs\Desktop\DJango\Django-ecommerce\env\lib\site-packages\django\db\models\sql\query.py", line 1165, in build_lookup
    lookup = lookup_class(lhs, rhs)
  File "C:\Users\halfs\Desktop\DJango\Django-ecommerce\env\lib\site-packages\django\db\models\lookups.py", line 24, in __init__
    self.rhs = self.get_prep_lookup()
  File "C:\Users\halfs\Desktop\DJango\Django-ecommerce\env\lib\site-packages\django\db\models\fields\related_lookups.py", line 115, in get_prep_lookup
    self.rhs = target_field.get_prep_value(self.rhs)
  File "C:\Users\halfs\Desktop\DJango\Django-ecommerce\env\lib\site-packages\django\db\models\fields\__init__.py", line 1776, in get_prep_value
    raise e.__class__(
ValueError: Field 'id' expected a number but got 'S'.
[25/Nov/2020 11:24:18] "POST /payment-complete/ HTTP/1.1" 500 129792
[25/Nov/2020 11:24:19] "GET / HTTP/1.1" 200 15371

I understand what the problem is Django saves the foreign key by assigning ID and not the actual value but I don't know how to filter the objects, if I don't know object's id.. Thanks, any help is appreciated!!

Uzair Saiyed
  • 575
  • 1
  • 6
  • 16
  • What does your `Item` model look like? – Selcuk Nov 25 '20 at 06:08
  • I updated my models in question as per your request, i added item model!! – Uzair Saiyed Nov 25 '20 at 06:29
  • I don't immediately see anything wrong. Is it possible that there is an unmigrated change? Was the `item` field of the `OrderItem` class previously a `CharField` by any chance? – Selcuk Nov 25 '20 at 06:43
  • No it wasn't, it is a foreign key and I think that foreign keys are stored as integer value and not actual string value that causes the problem in stockquantity in my case, I opened the database and tried to look for some problem and I found the same, I tried to play around with database schema but I wasn't sure it would have been successfull so I let it be for a while – Uzair Saiyed Nov 25 '20 at 06:47
  • Foreign keys **are** stored as integer but Django automatically creates a different column name (such as `item_id`) for the integer value. When you write `item=item.item` in a filter/get clause it should automatically do the necessary conversion. You may try something like `st = item.item.stockquantity_set.get(color=..., footwear_size=...)` to see if it makes a difference. – Selcuk Nov 25 '20 at 07:00
  • I tried to incorporate your suggestions but the error still persists – Uzair Saiyed Nov 25 '20 at 08:09
  • What are 'ItemColor', 'ClothSize' and 'FootwearSize'? I assume they are models, can we see them? – Karl Nov 25 '20 at 09:17
  • 1
    Ok, i added three models as per your request – Uzair Saiyed Nov 25 '20 at 09:21

2 Answers2

6

When querying a ForeignKey field, you 'normally' pass an instance of the model. For example:

# Example models
class AnotherModel(models.Model):
    name = models.CharField(...)


class MyModel(models.Model):
    another_model = models.ForeignKey(AnotherModel,...)


# Get the instance
another_model_instance = AnotherModel.objects.get(id=1)
# Use the instance in the query
my_model = MyModel.objects.get(another_model=another_model_instance)

You can however, use __ (double underscore) to 'hop' across and query a specific field. For example:

my_model = MyModel.objects.get(another_model__name='Some name')

With the above example, we are querying using the name field on the AnotherModel model. We can use this to fix the query in your view.

# Taken from your payment_complete view
for item in order_items:
    if item.item.is_footwear:
        st = StockQuantity.objects.get(
            item=item.item,
            color__item_color=item.color,
            footwear_size__footwear_size=str(item.footwear_size)
        )
    else:
        st = StockQuantity.objects.get(
            item=item.item,
            color__item_color=item.color,
            cloth_size__cloth_size=str(item.cloth_size)
        )    

Further reading: https://docs.djangoproject.com/en/3.1/topics/db/queries/.

Karl
  • 733
  • 1
  • 14
  • 28
  • I tried this after you told me but now the following error arises: `ValueError: Field 'id' expected a number but got 'Blue'` which i think is because color in stockquantity is integer too! – Uzair Saiyed Nov 25 '20 at 09:49
  • Have you added '__footwear_size' to the query? You are still passing a string to a FK field somewhere. – Karl Nov 25 '20 at 09:51
  • yes i added exactly what you suggested..... Code:`st = StockQuantity.objects.get( item=item.item, color=item.color, footwear_size__footwear_size=str(item.footwear_size) )` and same for cloth size.. and i get following error: `ValueError: Field 'id' expected a number but got 'Blue'.` – Uzair Saiyed Nov 25 '20 at 09:55
  • It may also be worth thinking about where 'Blue' is coming from. i.e. which field has the value 'Blue'. This may then help to narrow down where the issue is. – Karl Nov 25 '20 at 09:55
  • Arguement 'color' in the query is where 'Blue' is coming from – Uzair Saiyed Nov 25 '20 at 09:57
  • I have updated my answer - note the `item=item` lines on both of the queries. – Karl Nov 25 '20 at 09:58
  • i didn't quite get what you said could you please elaborate?? Do you mean replace item=item.item in query to item=item?? – Uzair Saiyed Nov 25 '20 at 09:59
  • Yes, `item` in the for loop is an instance of `Item`. – Karl Nov 25 '20 at 10:02
  • Nope, its an instance of orderitem, Though i gave it a shot once but still the error persists!!! – Uzair Saiyed Nov 25 '20 at 10:03
  • Sorry, you're right. Thought it read `Item` not `OrderItem` – Karl Nov 25 '20 at 10:07
  • It's a little bit messy but i'll clean it once it functions correctly. Anyways thanks for help – Uzair Saiyed Nov 25 '20 at 10:08
  • I have just updated the answer again - note the `color__item_color=item.color` line. – Karl Nov 25 '20 at 10:12
  • Thats great, no problem. If you can accept this answer it may helps others in the future. – Karl Nov 25 '20 at 10:17
  • i have another query, if i want to access stockquantity model from item model is it possible to do so?? If so, pls can you illustrate?? – Uzair Saiyed Nov 25 '20 at 10:19
  • It is possible. You need to do a reverse lookup. See https://stackoverflow.com/questions/15306897/django-reverse-lookup-of-foreign-keys – Karl Nov 25 '20 at 10:22
2

For footwear_size and cloth_size you are sending the actual value to the view (S, M, L, etc), but your field is a foreign key, which is an integer.

You need to either pass the foreign key value instead of the string value, or change your lookup to search for the string value (something like footwear_size__name=str(item.footwear_size)).

Eduard Luca
  • 6,514
  • 16
  • 85
  • 137
  • How do I change my lookup, I am new to django and do not have much of experience regarding the same!! – Uzair Saiyed Nov 25 '20 at 08:09
  • I tried doing something like: `st = StockQuantity.objects.get( item=item.item, color__name=str(item.color), cloth_size__name=str(item.cloth_size))` and it still isnt working, it gives me an error like: `raise FieldError('Related Field got invalid lookup: {}'.format(lookup_name)) django.core.exceptions.FieldError: Related Field got invalid lookup: name` – Uzair Saiyed Nov 25 '20 at 08:23