Answer the question
In order to leave comments, you need to log in
How to implement a shopping cart in django?
Just started learning django, don't judge too harshly. There is a book model and a basket model where you need to throw this book.
I created a separate application for the shopping cart. On the book's detailed description page, I created links to add it to the cart.
a href="{% url 'basket:add' book.pk %}" Добавить книгу в корзину
{% extends "master.html" %}
{% block title %}<title>Basket</title>{% endblock %}
{% block content %}
<h1>Basket</h1>
{% if basket %}
<ul>
{% for book in basket %}
<li>
<a href="{{ book.get_absolute_url }}">{{ book.title }}</a> ({{book.author}})
</li>
{% endfor %}
</ul>
<hr>
{% else %}
<p>There are no books in the basket.</p>
{% endif %}
{% endblock %}
class Book(models.Model):
title = models.CharField(max_length=200)
author = models.ForeignKey('Author', on_delete=models.SET_NULL, null=True)
summary = models.TextField(
max_length=1000,
null=True, blank=True,
help_text="Enter a brief description of the book"
)
isbn = models.CharField(
max_length=13,
blank=True,
help_text='13 Character <a href="https://www.isbn-international.org/content/what-isbn">ISBN number</a>')
genre = models.ManyToManyField(
Genre,
help_text="Select a genre for this book", blank=True)
def display_genre(self):
return ','.join(genre.name for genre in self.genre.all()[:3])
display_genre.short_description = 'Genre'
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse("book_detail", args=[str(self.id)])
class Basket(models.Model):
user = models.ForeignKey(
settings.AUTH_USER_MODEL,
on_delete=models.CASCADE,
related_name='basket')
book = models.ForeignKey(
Book, on_delete=models.CASCADE)
quantity = models.PositiveIntegerField(
verbose_name='количество', default=0)
add_datetime = models.DateTimeField(
verbose_name='время', auto_now_add=True)
def basket(request):
content = {}
return render(request, 'basketapp/basket.html', content)
def basket_add(request, pk):
book = get_object_or_404(Book, pk=pk)
basket = Basket.objects.filter(user=request.user, book=book).first()
if not basket:
basket = Basket(user=request.user, book=book)
basket.quantity += 1
basket.save()
return HttpResponseRedirect(reverse('basketapp:basket'))
def basket_remove(request, pk):
content = {}
return render(request, 'basketapp/basket.html', content)
Answer the question
In order to leave comments, you need to log in
1. Issue the code accordingly
2. The model is garbage. Learn O2M Relationships
3. Should you store carts on the backend? They are stored on the backend when they like to spam cart abandoners
4. In the code
basket = Basket.objects.filter(user=request.user, book=book).first()
if not basket:
basket = Basket(user=request.user, book=book)
basket.quantity += 1
basket.save()
from collections import UserDict
from core.models import ProductOption, Product
from .models import Item
class Basket(UserDict):
changed = False
def add(self, quantity=0, option=None, set_=False):
self.changed = True
id_ = str(option.product.id)
option = str(option.id)
self.setdefault(id_, {})
self[id_].setdefault(option, 0)
if set_:
self[id_][option] = quantity
else:
self[id_][option] += quantity
if self[id_][option] <= 0:
del self[id_][option]
if not self[id_]:
del self[id_]
return 0
else:
return self[id_][option]
@property
def total_count(self):
return sum(x for product, options in self.items() for _, x in options.items())
@property
def total_price(self):
prices = {str(id_): price for id_, price in
Product.objects.filter(id__in=self.keys()).values_list('id', 'price')}
return sum(x * prices[product] for product, options in self.items() for _, x in options.items())
def cost(self, option):
price = option.product.price
return self.count_option(option) * price
def count_option(self, option):
product_id = str(option.product.id)
option_id = str(option.id)
return self.get(product_id, {}).get(option_id, 0)
def flush(self):
self.changed = True
for key in list(self):
del self[key]
def build_order(self, order):
items = []
for product_id, data in self.items():
product = Product.objects.get(id=product_id)
for option_id, quantity in data.items():
if quantity == 0:
continue
option = None
if option_id != '0':
option = ProductOption.objects.get(id=option_id)
items.append(
Item(order=order, option=option, quantity=quantity, product=product)
)
order.items.bulk_create(items)
self.flush()
return order
def fix(self):
"""Фиксит корзину на случай, если опции удалили, а они находятся в корзине"""
ids = self.keys()
exist_in_db = (Product.objects
.filter(id__in=ids, options__in_stock=True, options__show=True)
.values_list('id', flat=True))
to_remove = set(ids) - set(str(x) for x in exist_in_db)
for id_ in to_remove:
del self[id_]
if to_remove:
self.changed = True
def to_dict(self):
return dict(self)
from django.utils.deprecation import MiddlewareMixin
from .basket import Basket
class BasketMiddleware(MiddlewareMixin):
def process_request(self, request):
request.basket = Basket(request.session.get('basket', {}))
def process_response(self, request, response):
if getattr(request, 'basket', None) is None:
return response
if request.basket.changed:
request.session['basket'] = request.basket.to_dict()
request.session.save()
return response
# ...
SESSION_ENGINE = 'django.contrib.sessions.backends.signed_cookies'
MIDDLEWARE_CLASSES = [
# ...
'orders.middleware.BasketMiddleware',
]
# ...
@method_decorator(csrf_exempt, name='dispatch')
class ChangeBasketOption(View):
def post(self, request):
change = int(request.POST.get('change'))
pk = int(request.POST.get('id'))
set_ = bool(request.POST.get('set', 0))
option = get_object_or_404(ProductOption, pk=pk)
value = request.basket.add(option=option, quantity=change, set_=set_)
cost = request.basket.cost(option)
return JsonResponse({
'value': value,
'cost': cost,
'total': request.basket.total_price
})
class Basket(FormView):
template_name = 'orders/basket.html'
form_class = OrderForm
success_url = reverse_lazy('ordered')
def get(self, request, *args, **kwargs):
request.basket.fix()
return super().get(request, *args, **kwargs)
def get_context_data(self, **kwargs):
kwargs['products'] = Product.objects.filter(id__in=self.request.basket.keys())
kwargs['can_order'] = self.request.basket.total_price >= min_order_cost()
return super().get_context_data(**kwargs)
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs['user'] = self.request.user
return kwargs
def form_valid(self, form):
order = form.save()
order = self.request.basket.build_order(order)
mail_new_order(order)
return super().form_valid(form)
You can see the code of my online store. There is a well-implemented shopping cart.
https://github.com/UlugbekMuslitdinov/exizmat
Didn't find what you were looking for?
Ask your questionAsk a Question
731 491 924 answers to any question