Source code for superdjango.views.ajax.mixins

"""
**Why no AJAXMixin?**

An AJAX mixin similar to Braces' AjaxResponseMixin simply allows a view for "fork" behavior based on
``request.is_ajax()``. We feel a better practice is to create a view that specifically handles AJAX requests because
this is much more obvious, easier to test, and reduces the code complexity of the view.

"""
# Imports

from django.conf import settings
from django.core import serializers
from django.core.serializers.json import DjangoJSONEncoder
from django.http import HttpResponse, HttpResponseBadRequest
import json
import logging

logger = logging.getLogger(__name__)

# Exports

__all__ = (
    "JSONMixin",
)

# Mixins


[docs]class JSONMixin(object): """Handle JSON requests/responses. .. code-block:: python from superdjango.views import GenericView, JSONMixin class MyAjaxView(JSONMixin, GenericView): def post(self, request, *args, **kwargs): # process the request that produces a dict or queryset or model instance. data = self.get_some_data() return self.get_json_response(data) """ ajax_required = not settings.DEBUG """Indicates requests must be made via AJAX. Defaults to ``False`` in development.""" content_type = "application/json" """The expected content type of the request or response.""" decoder_class = None """Optional class used to decode the request. It takes the current request instance as the only parameter and must implement a load method which returns a ``dict``. """ encoder_class = DjangoJSONEncoder """The class used to encode JSON responses.""" json_required = False """Indicates that requests must be made using JSON.""" # noinspection PyAttributeOutsideInit
[docs] def dispatch(self, request, *args, **kwargs): """Set ``request_json`` and validate the request before continuing with the dispatch.""" # IF AJAX is required. if not request.is_ajax() and self.ajax_required: return self.dispatch_bad_request(error={'error': "An AJAX request is required."}) # Assign for later, possible use. self.args = args self.kwargs = kwargs self.request = request # Get the JSON, if any, sent to the view. self.json_request = self.get_json_request() # Validate the request. conditions = [request.method != "OPTIONS", self.json_required, self.json_request is None] if all(conditions): return self.dispatch_bad_request() # Continue dispatching the request. # noinspection PyUnresolvedReferences return super().dispatch(request, *args, **kwargs)
[docs] def dispatch_bad_request(self, error=None, **kwargs): """Handle a bad request. :param error: A dictionary describing the error in the form of ``{'error': "Description."}``. Defaults to ``{'error': "Bad request."}`` :type error: dict Keyword arguments are passed to ``json.dumps()``. """ if error is None: error = {'error': "Bad request."} kwargs.setdefault("ensure_ascii", False) json_data = json.dumps(error, cls=self.encoder_class, **kwargs).encode("utf-8") return HttpResponseBadRequest(json_data, content_type=self.get_content_type())
[docs] def get_content_type(self): """Get the content type for the request/response. :rtype: str """ return self.content_type
[docs] def get_json_request(self): """Get JSON from the current request. :rtype: dict | None """ if self.decoder_class is not None: decoder_class = self.decoder_class decoder = decoder_class(self.request) return decoder.load() try: return json.loads(self.request.body.decode("utf-8")) except json.JSONDecodeError as e: if self.json_required: logger.warning("%s failed to get JSON from request: %s" % (self.__class__.__name__, e)) return None
[docs] def get_json_response(self, data, status=200, **kwargs): """Get the JSON response. :param data: The data to be serialized. If this is a dictionary, simple serialization is used using the ``encoder_class`` attribute. If a QuerySet is given, Django's built-in serializer is used. :type data: dict | list | tuple | django.db.models.QuerySet :param status: The HTTP status code. :type status: int Additional keyword arguments are passed to ``json.dumps()`` or Django's serializer. :rtype: HttpResponse .. warning:: If ``data`` is not provided as a ``dict``, ``list``, or ``tuple`` it is assumed to be a QuerySet. This will produce unexpected results if you fail to provide the supported data type. """ if type(data) in (dict, list, tuple): kwargs.setdefault("ensure_ascii", False) json_data = json.dumps(data, cls=self.encoder_class, **kwargs).encode("utf-8") else: json_data = serializers.serialize("json", data, **kwargs) return HttpResponse(json_data, content_type=self.get_content_type(), status=status)