0xjustuzair
0xjustuzair

Reputation: 587

Django ValueError: Field 'id' expected a number but got 'S'

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!!

Upvotes: 3

Views: 14308

Answers (2)

Karl
Karl

Reputation: 743

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/.

Upvotes: 6

Eduard Luca
Eduard Luca

Reputation: 6602

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)).

Upvotes: 2

Related Questions