Django ecommerce: Reverse for 'cart_add' with arguments '('',)' not found. 1 pattern(s) tried: ['cart/add/(?P<product_id>[0-9]+)/$']

This is my first ecommerce web-site with django and it’s a little tough to wrap my head around all the routing and cart function, and I fixed lots of stuff, but I just can’t fix this error. I tried doing some stuff in urls but it just didn’t work out.

onlineshop/models.py:

from django.db import models
from django.urls import reverse


class Category(models.Model):
    name = models.CharField(max_length=200, db_index=True)
    slug = models.SlugField(max_length=200, unique=True)

    class Meta:
        ordering = ('name',)
        verbose_name = 'category'
        verbose_name_plural = 'categories'

    def __str__(self):
        return self.name

    def get_absolute_url(self):
        return reverse('onlineshop:product_list_by_category', args=[self.slug])


class Product(models.Model):
    category = models.ForeignKey(
        Category, related_name='product', on_delete=models.CASCADE)
    name = models.CharField(max_length=200, db_index=True)
    slug = models.SlugField(max_length=200, db_index=True)
    image = models.ImageField(upload_to='products/%Y/%m/%d', blank=True)
    description = models.TextField(blank=True)
    price = models.DecimalField(max_digits=10, decimal_places=2)
    available = models.BooleanField(default=True)
    created = models.DateTimeField(auto_now_add=True)
    updated = models.DateTimeField(auto_now=True)

    class Meta:
        ordering = ('name',)
        index_together = (('id', 'slug'))

    def __str__(self):
        return self.name

    def get_absolute_url(self):
        return reverse('onlineshop:product_detail', args=[self.id, self.slug])

onlineshop/views.py:

from django.shortcuts import render, get_object_or_404
from .models import *
from cart.forms import CartAddProductForm

def product_list(request, category_slug=None):
    category = None
    categories = Category.objects.all()
    products = Product.objects.filter(available=True)
    if category_slug:
        category = get_object_or_404(Category, slug=category_slug)
        products = products.filter(category=category)
    context = {
        'categories': categories,
        'category': category,
        'products': products,
    }
    return render(request, 'onlineshop/product/list.html', context)


def product_detail(request, id, slug):
    product = get_object_or_404(Product, id=id, slug=slug, available=True)
    cart_product_form = CartAddProductForm()
    context = {
        'product': product,
        'cart_product_form':cart_product_form,
    }
    return render(request, 'onlineshop/product/detail.html')

onlineshop/base.html:

{% load static %}
<!DOCTYPE html>
<html lang="en" dir="ltr">
  <head>
    <meta charset="utf-8">
    <title>{% block title %}My Shop{% endblock %}</title>
    <link href="{% static "css/base.css" %}" rel="stylesheet">
  </head>
  <body>
    <div id="header">
      <a href="/" class="logo">My Shop</a>
    </div>
    <div id="subheader">
      <div class="cart">
        Your cart is empty
      </div>
    </div>
    <div id="content">
      {% block content %}
      
      {% endblock %}
    </div>
  </body>
</html>

onlineshop/detail.html:

{% extends "onlineshop/base.html" %}
{% load static %}

{% block title %}
  {{ product.title }}
{% endblock %}

{% block content %}
  <div class="product-detail">
    <img src="{% if product.image %}{{ product.image.url }}{% else %}{%static 'img/no_image.png' %}{% endif %}">
    <h1>
      {{ product.name}}
    </h1>
    <h2>
      <a href="{{ product.category.get_absolute_url }}">
        {{ product.category }}
      </a>
    </h2>
    <p class="price">
      {{ product.price }}GEL
    </p>
    <form action="{% url 'cart:cart_add' product.id %}" method="post">
      {{ cart_product_form }}
      {% csrf_token %}
      <input type="submit" value="Add to cart" />
    </form>

  </div>
{% endblock %}

onlineshop/views.py:

from django.urls import path
from . import views


app_name = 'onlineshop'

urlpatterns = [
    path('', views.product_list, name='product_list'),
    path('<slug:category_slug>/', views.product_list,
         name='product_list_by_category'),

    path('<int:id>/<slug:slug>/', views.product_detail, name='product_detail')
]

cart/cart.py:

from decimal import Decimal
from django.conf import settings
from onlineshop.models import Product

class Cart(object):

    def save(self):
        self.session.modified = True

    def __init__(self, request):
        self.session = request.session
        cart = self.sessions.get(settings.CART_SESSION_ID)
        if not cart:
            cart = self.session[settings.CART_SESSION_ID]
            if not cart:
                cart = self.session[settings.CART_SESSION_ID] = {}
            self.cart = cart

    def add(self, product, quantity=1, override_quantity=False):
        product_id = str(product.id)
        if product_id not in self.cart:
            self.cart[product_id]={'quantity':0, 'price':str(product.price),}

        if override_quantity:
            self.cart[product_id]['quantity'] = quantity
        else:
            self.cart[product_id]['quantity'] += quantity
        self.save()


    def remove(self, product):
        product_id = str(product.id)
        if product_id in self.cart:
            del self.cart[product_id]
            self.save()

    def __iter__(self):
        product_ids = self.cart.keys()
        products = Product.objects.filter(id__in=product_ids)
        cart = self.cart.copy()
        for product in products:
            cart[str(product.id)]['product'] = product

        for item in cart.values():
            item['price'] = Decimal(item['price'])
            item['total_price'] = item['price'] * item['quantity']
            yield item


    def __len__(self):
        return sum(item['quantity'] for item in self.cart.values())



    def get_total_price(self):
        return sum(Decimal(item['price'] * item['quantity'] for item in self.cart.values()))


    def clear(self):
        del self.session[settings.CART_SESSION_ID]
        self.save()

cart/views.py:

from django.shortcuts import render, get_object_or_404
from onlineshop.models import *
from .cart import Cart
from django.views.decorators.http import require_POST
from .forms import *


@require_POST
def cart_add(request, product_id):
    cart = Cart(request)
    product = get_object_or_404(Product, id=product_id)
    form = CardAddProductForm(request.POST)
    if form.is_valid():
        cd = form.cleaned_data
        card.add(product=product, quantity=cd['quantity'], override_quantity=cd['override'])
    return redirect('cart:cart_detail')


def cart_remove(request, product_id):
    cart = Cart(request)
    product = get_object_or_404(Product, id=product)
    cart.remove(product)
    return redirect('cart:cart_detail')


def cart_detail(request, product_id):
    cart = Cart(request)
    context = {
        'cart':cart,
    }
    return render(request, 'cart/detail.html', context)

cart/forms.py

from django import forms

PRODUCT_QUANTITY_CHOICES = [(i,str(i)) for i in range(1,21)]
class CartAddProductForm(forms.Form):
    quantity = forms.TypedChoiceField(choices=PRODUCT_QUANTITY_CHOICES, coerce=int)
    override = forms.BooleanField(required=False, initial=False, widget=forms.HiddenInput)

cart/detail.html:

{% extends onlineshop/base.html %}
{% load static %}

{% block title %}
  Shopping Cart
{% endblock %}

{% block content %}
<h1>Shopping Cart</h1>
<table class="cart">
  <thead>
    <tr>
      <th>
        Image
      </th>
      <th>
        Product
      </th>
      <th>
        Quantity
      </th>
      <th>
        Product
      </th>
    </tr>
  </thead>
  <tbody>
    {% for item in cart %}
    {% with product = item.product %}
    <tr>
      <td>
        <a href="{{product.get_absolute_url}}">
          <img src = "{% if product.image %}{{ product.image.url }}{% else %}{% static 'img/no_image.png' %}"{% endif %}>

        </a>
      </td>
      <td>
        {{ product.name }}
      </td>
      <td>
        {{ item.quantity }}
      </td>
      <td>
        <form action="{% url 'cart:cart_remove' product.id %}" method="post">
          <input type="submit" value="Remove" />
          {% csrf_token %}
        </form>
      </td>
      <td class="num">
        {{ item.price }}GEL
      </td>
      <td class="num">
        {{ item.total_price }}GEL
      </td>
    </tr>
    {% endwith %}
    {% endfor %}
    <tr>
      <td>
        Total
      </td>
      <td colspan="4">

      </td>
      <td class="num">
        {{ cart.get_total_price }}GEL
      </td>
    </tr>
  </tbody>
</table>
<p class="text-right">
  <a href="{% url 'onlineshop:product_list' %}" class="buttonlight">Continue Shopping</a>
  <a href="#" class="button">Checkout</a>
</p>
{% endblock %}

cart/urls.py:

from django.urls import path
from . import views

app_name = 'cart'

urlpatterns = [
    path('', views.cart_detail, name='cart_detail'),
    path('add/<int:product_id>/', views.cart_add, name='cart_add'),
    path('remove/<int:product_id>/', views.cart_remove, name='cart_remove')
]

From the portions you’ve posted, the only place where cart_add is being reversed is at this line in your template:

You’ll need to verify that anywhere you’re rendering this template is supplying an object in the context named product, with an attribute named id. (Well, that’s not entirely 100% precise, but close enough for the moment.)

Given the use of the same name for template files in different directories, you also want to ensure that no reference to a template named detail.html in any other view is referring to onlineshop/product/detail.html. This may involve reviewing your TEMPLATE settings.

You should be getting a full traceback from this error, that may provide additional clues for finding the primary source of the issue.

is there any way this error is caused by not adding context processors?

Not that I can see or think of. This form tag is rather standard, there’s nothing in it requiring anything special. Nor is it saying that there’s a problem with rendering the page.
It’s telling you that you’re trying to render that url tag either without a product object or a product object without an attribute, key, or method named id.

so I have to fix views right?

We can’t tell what you need to fix. There’s not enough information in what you’ve posted to determine an actual cause.

should I provide ‘settings’ code too?

Lets start with just seeing the full traceback being provided, and specifying what page you were looking at and what you did that ended up generating this error.

I get this error when I click on product title, to see product details

So which page are you looking at at that time? (What url, view, template?)

we are looking at a onlineshop/detail.html.
url: path(‘int:id/slug:slug/’, views.product_detail, name=‘product_detail’)
and a product_detail view from onlineshop/views.py.
If you take a look in onlineshop/detail.html, there is a button: add to cart, which contains {% url ‘cart:cart_add’ product.id %}

In product_detail, you have:

You’re not passing the context in to the render call.

oh, probably the stupidest mistake I have ever done. thanks for your help

Can you show an example?