Source code for shuup.core.basket.command_dispatcher

# -*- coding: utf-8 -*-
# This file is part of Shuup.
#
# Copyright (c) 2012-2021, Shuup Commerce Inc. All rights reserved.
#
# This source code is licensed under the OSL-3.0 license found in the
# LICENSE file in the root directory of this source tree.
from __future__ import unicode_literals

import six
from django.core.exceptions import ValidationError
from django.http import HttpResponseRedirect, JsonResponse
from django.shortcuts import redirect
from django.utils.translation import ugettext_lazy as _

from shuup.apps.provides import get_provide_objects
from shuup.core.basket import commands
from shuup.core.basket.command_middleware import BaseBasketCommandMiddleware
from shuup.core.signals import get_basket_command_handler
from shuup.utils.django_compat import force_text
from shuup.utils.excs import Problem


[docs]class BasketCommandDispatcher(object): """ BasketCommandDispatcher handles (usually AJAX) requests that somehow update the basket. You should never instantiate BasketCommandDispatcher yourself -- instead use `get_basket_command_dispatcher()`. All `handle_*` methods are expected to accept `**kwargs`. """ commands_module = commands def __init__(self, request, basket=None): """ :type request: HttpRequest """ self.request = request self.ajax = self.request.is_ajax() # :type self.basket: BaseBasket self.basket = basket or request.basket
[docs] def get_command_handler(self, command): handler = getattr(self.commands_module, "handle_%s" % command.lower(), None) if handler and callable(handler): return handler for receiver, handler in get_basket_command_handler.send( BasketCommandDispatcher, command=command, instance=self ): if handler and callable(handler): return handler
[docs] def handle(self, command, kwargs=None): """ Dispatch and handle processing of the given command. :param command: Name of command to run. :type command: unicode :param kwargs: Arguments to pass to the command handler. If empty, `request.POST` is used. :type kwargs: dict :return: response. :rtype: HttpResponse """ kwargs = kwargs or dict(six.iteritems(self.request.POST)) try: handler = self.get_command_handler(command) if not handler or not callable(handler): raise Problem(_("Error! Invalid command `%s`.") % command) kwargs.pop("csrfmiddlewaretoken", None) # The CSRF token should never be passed as a kwarg kwargs.pop("command", None) # Nor the command kwargs.update(request=self.request, basket=self.basket) kwargs = self.preprocess_kwargs(command, kwargs) response = handler(**kwargs) or {} except (Problem, ValidationError) as exc: if not self.ajax: raise msg = exc.message if hasattr(exc, "message") else exc response = { "error": force_text(msg, errors="ignore"), "code": force_text(getattr(exc, "code", None) or "", errors="ignore"), } response = self.postprocess_response(command, kwargs, response) if self.ajax: return JsonResponse(response) return_url = response.get("return") or kwargs.get("return") if return_url and return_url.startswith("/"): return HttpResponseRedirect(return_url) return redirect("shuup:basket")
[docs] def preprocess_kwargs(self, command, kwargs): """ Preprocess kwargs before they are passed to the given `command` handler. Useful for subclassing. Must return the new `kwargs`, even if it wasn't mutated. :param command: The name of the command about to be run. :param kwargs: dict of arguments. :return: dict of arguments. """ for basket_command_middleware in get_provide_objects("basket_command_middleware"): if not issubclass(basket_command_middleware, BaseBasketCommandMiddleware): continue # create a copy kwargs = dict( basket_command_middleware().preprocess_kwargs( basket=self.basket, request=self.request, command=command, kwargs=kwargs ) ) return kwargs
[docs] def postprocess_response(self, command, kwargs, response): """ Postprocess the response dictionary (not a HTTP response!) before it is either turned into JSON or otherwise processed (in the case of non-AJAX requests). :param command: The command that was run. :param kwargs: The actual kwargs the command was run with. :param response: The response the command returned. :return: The response to be processed and sent to the client. """ for basket_command_middleware in get_provide_objects("basket_command_middleware"): if not issubclass(basket_command_middleware, BaseBasketCommandMiddleware): continue response = dict( basket_command_middleware().postprocess_response( basket=self.basket, request=self.request, command=command, kwargs=kwargs, response=response ) ) return response