==== AJAX ==== Checking authentication ======================= .. currentmodule:: djem.ajax Django's built in ``login_required()`` decorator is not well suited to AJAX requests. If the user making the request is not authenticated, it issues a redirect to the configured login view. This is not particularly useful to an AJAX client. Djem provides a simple alternate decorator, :func:`ajax_login_required`, to use on views that are the target of AJAX requests. Instead of issuing a redirect if the user is not authenticated, it returns a ``HttpForbiddenResponse``. .. versionadded:: 0.7 The :func:`ajax_login_required` decorator. Responding to AJAX requests =========================== .. currentmodule:: djem.ajax Django provides ``JsonResponse`` to aid in responding to data-centric AJAX requests (as opposed to those that return rendered HTML to be injected directly into the page). Djem provides a simple extension of ``JsonResponse`` that adds some additional features: :class:`AjaxResponse`. :class:`AjaxResponse` automatically adds any messages in the `Django messages framework `_ store to the response body. This allows views that are called via AJAX and return JSON-encoded data to still make use of the messages framework and have those messages automatically embedded in the response and removed from the message store. The messages are added to the parent ``JsonResponse``'s ``data`` dictionary using the "messages" key. The messages themselves are dictionaries containing the following: * message: The message string. * tags: A string of the tags applied to the message, space separated. If there are no messages to add, the "messages" key will not be added to the ``data`` dictionary at all (i.e. it will not be added as an empty list). Using :class:`AjaxResponse` differs from ``JsonResponse`` in the following ways: * The first positional argument should be a Django ``HttpRequest`` instance. This argument is required - it is used to retrieve messages from the message framework store. * The ``data`` argument is optional. If provided, it must always be a ``dict`` instance, and messages will be added to this dictionary. If not provided, messages will be added to a new ``dict`` instance, passing it to the parent ``JsonResponse``. Using the ``safe`` argument of ``JsonResponse`` to JSON-encode other types is not supported (see the `documentation `_ for the ``safe`` argument of ``JsonResponse``). * The optional argument ``success`` can be set to add a "success" attribute to the ``data`` dictionary. The "success" attribute will always be added as a boolean value, regardless of what was passed to the ``success`` argument (though it will not be added at all if nothing was passed). As with messages, this will be added to the ``data`` dictionary passed to the parent ``JsonResponse`` regardless of whether one was provided to :class:`AjaxResponse` itself or not. With the exception of ``safe``, as noted above, :class:`AjaxResponse` accepts and supports all arguments of ``JsonResponse``. Usage ----- This simple example demonstrates how :class:`AjaxResponse` can be used with the messages framework: .. code-block:: python from django.contrib import messages from djem import AjaxResponse def my_view(request): # do something... messages.success(request, 'Did something!') return AjaxResponse(request) This will give a JSON-encoded response body that looks like this: .. code-block:: python { "messages":[{ "message":"Did something!", "tags":"success" }] } The following is a more complete example, based on the "polls" application created `in the Django tutorial `_. This view records a vote on a given ``Choice``: .. code-block:: python from django.contrib import messages from djem import AjaxResponse from polls.models import Choice def vote_on_question(request, choice_id): try: choice = Choice.objects.get(pk=choice_id) except Choice.DoesNotExist: messages.error(request, 'Invalid choice.') return AjaxResponse(request, success=False) choice.votes += 1 choice.save() messages.success(request, 'Vote recorded.') return AjaxResponse(request, {'votes': choice.votes}, success=True) .. note:: This example is for illustrative purposes only. It does not represent a good way to increment a counter in the database - such an operation `should be performed atomically `_. CSRF ==== .. currentmodule:: djem.templatetags.djem As `Django notes in its own documentation `_, adding the CSRF token to AJAX POST requests can be done on each individual request, or you can use JavaScript framework features to add it to all outgoing POST requests, using the ``X-CSRFToken`` header. Djem's :ttag:`csrfify_ajax` template tag does exactly that in a single line: .. code-block:: html+django {% load djem %} ... {% csrfify_ajax %} ... The tag injects a ``