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')
]