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)