I'm creating a T-shirt shop with variants/colors. In adding to cart, I get the error: Field 'id' expected a number but got 'woodstock'. Woodstock is the name of the shirt, but I'm passing the cartid to the function. cartid is actually being used in the remove_from_cart function, but I'm not sure why the error is thrown when I push add to cart. Here's the cart model:
class Cart(models.Model):
#user = models.ForeignKey(User, null=True, blank=True, on_delete=models.CASCADE)
total = models.DecimalField(max_digits=100, decimal_places=2, default=0.00)
timestamp = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
active = models.BooleanField(default=True)
def __str__(self):
return "Cart id: %s" %(self.id)
class CartItem(models.Model):
cart = models.ForeignKey(Cart, on_delete=models.CASCADE, null=True, blank=True)
product = models.ForeignKey(Product,on_delete=models.CASCADE)
variations = models.ManyToManyField(Variation, blank=True)
quantity = models.IntegerField(default=1)
line_total = models.DecimalField(default=10.99, max_digits=100, decimal_places=2)
timestamp = models.DateTimeField(auto_now_add=True, auto_now=False)
updated = models.DateTimeField(auto_now=True)
def __str__(self):
try:
return str(self.cart.id)
except:
return self.product.name
views.py
def view(request):
print("viewing cart")
try:
the_id = request.session['cart_id']
except:
the_id = None
if the_id:
cart = Cart.objects.get(id=the_id)
new_total = 0.00
for item in cart.cartitem_set.all():
line_total = float(item.product.price) * item.quantity
new_total = line_total
request.session['items_total'] = cart.cartitem_set.count()
cart.total = new_total
cart.save()
context = {"cart":cart}
else:
empty_message = "Your cart is empty, please keep shopping"
context = {"empty": True, "empty_message": empty_message}
template = "cart/view.html"
return render(request, template, context)
def add_to_cart(request, slug):
print("add_to_cart pressed")
request.session.set_expiry(100000000)
try:
the_id = request.session['cart_id']
except:
#create cart id
new_cart = Cart()
new_cart.save()
request.session['cart_id'] = new_cart.id
the_id = new_cart.id
cart = Cart.objects.get(id=the_id)
try:
product = Product.objects.get(slug=slug)
except Product.DoesNotExist:
pass
except:
pass
product_variations = []
if request.method == 'POST':
qty = request.POST['qty']
for item in request.POST:
key = item
val = request.POST[key]
try:
v = Variation.objects.get(product=product, category__iexact=key, title__iexact=val)
product_variations.append(v)
except:
pass
cart_item = CartItem.objects.create(cart=cart, product=product)
if len(product_variations) > 0:
cart_item.variations.add(*product_variations)
cart_item.quantity = qty
cart_item.save()
print("cart item added")
return HttpResponseRedirect(reverse("carts:cart"))
return HttpResponseRedirect(reverse("carts:cart"))
def remove_from_cart(request, id):
print(id) #this is showing the item name, the reason it's not working. Either traced back to html view or urls.py
try:
the_id = request.session['cart_id']
cart = Cart.objects.get(id=the_id)
except:
return HttpResponseRedirect(reverse("carts:cart"))
cartitem = CartItem.objects.get(id=id)#error is traced to this line
cartitem.cart = None
cartitem.save()
return HttpResponseRedirect(reverse("carts:cart"))
html
{% block content %}
<div class="col-sm-8 offset-sm-2">
{% if empty %}
<h1 style="text-align:center;">{{ empty_message }}</h1>
{% else %}
<table class="table">
<thead>
<th>Item</th>
<th>Price</th>
<th>Qty</th>
<th></th>
</thead>
<tfoot>
<tr>
<td></td>
<td></td>
<td>Total: {{ cart.total }}</td>
</tr>
</tfoot>
{% for item in cart.cartitem_set.all %}
<tr>
<td>{{ item.product }}
{% if item.variations.all %}
<ul>
{% for var in item.variations.all %}
<li> {{ var.category|capfirst }} : {{ var.title|capfirst }}</li>
{% endfor %}
</ul>
{% endif %}
</td>
<td>{{ item.product.price }}</td>
<td>{{ item.quantity }}</td>
<td><a href='{% url "carts:remove_from_cart" item.id %}'>Remove</a></td>
</tr>
{% endfor %}
</table>
{% endif %}
</div>
{% endblock %}
urls.py for cart
app_name='carts'
urlpatterns =[
path('cart/', views.view, name='cart'),
path('cart/<id>/', views.remove_from_cart, name='remove_from_cart'),
path('cart/<slug:slug>/', views.add_to_cart, name='add_to_cart'),
]
One suggestion in researching this problem was to delete migrations and run makemigrations/migrate, but that didn't work for me. Any other suggestions or why item.id is passing the name and not the id?
CodePudding user response:
Your remove_from_cart
url path matches paths like cart/<id>/
where id
does not have a type so matches anything, the add_to_cart
path uses cart/<slug:slug>/
but because any slug will also match id
from the previous path this view will ever be used. You need to make these paths unique so that they never match the same path.
One way is to make id
an integer so that slugs will not match it
path('cart/<int:id>/', views.remove_from_cart, name='remove_from_cart'),
path('cart/<slug:slug>/', views.add_to_cart, name='add_to_cart'),
The better way is to add prefixes to these paths so that they never clash
path('cart/remove/<int:id>/', views.remove_from_cart, name='remove_from_cart'),
path('cart/add/<slug:slug>/', views.add_to_cart, name='add_to_cart'),