Django Recipes. Part 1 - AJAX Forms

Hello, habrauser!

I may have already started a midlife crisis , but in the summer I began work on a major Open Source project. However, about it a little later , when the code will not be ashamed . So, I want to share a number of snippets that I had to write to fit the DRY Don't Repeat Youself concept . Therefore, I am going to write several articles.
By the way, you can pay attention to my previous article .

I'll start with the implementation of AJAX.


I want to make a reservation right away - not so long ago I found an example on the dzhangi website for implementing ajax forms. As it turned out, I did almost the same thing, but I did it myself and I am satisfied =) Below I will give my example and analyze it.
image

I ask for cat.

Speaking about the processing of forms in the jung, taking into account the appearance of the django.views.generic module back in release 1.5, one cannot but pay attention to the FormView class. Since all other generic classes for processing forms are inherited from it, I chose it as the object of my experiments.

So, let's go:



Backend


The is_valid (self, form) and is_invalid (self, form) methods are responsible for processing the returned result if the form meets the requirements of is_valid () and not, respectively. Making our way up the ancestors to FormMixin we see the following:
    def form_valid(self, form):
        """
        If the form is valid, redirect to the supplied URL.
        """
        return HttpResponseRedirect(self.get_success_url())
    def form_invalid(self, form):
        """
        If the form is invalid, re-render the context data with the
        data-filled form and errors.
        """
        return self.render_to_response(self.get_context_data(form=form))


In the case of the is_valid () method, only a redirect bothers us. Therefore, we can safely replace this code with the answer we need:
from django.shortcuts import HttpResponse
def form_valid(self, form):
        return HttpResponse('OK')

As a result, we get the HTTP 200 code in the output that jquery expects .

I also allowed myself to peek into the ModelFormMixin class and add a form save before the response. As a result, we got such a hybrid:
    def form_valid(self, form):
        form.save()
        return HttpResponse('OK')


The answer in case of a form error is somewhat more complicated - we need to return the dictionary of errors and display them for the user. However, the task is easily performed using the standard attribute errors of the form:
import json
from django.http import HttpResponseBadRequest
    def form_invalid(self, form):
        errors_dict = json.dumps(dict([(k, [e for e in v]) for k, v in form.errors.items()]))
        return HttpResponseBadRequest(json.dumps(errors_dict))


AjaxFormMixin


Thus we get the following class:
from django.views.generic import FormView
class AjaxFormMixin(FormView):
    template_name = 'form_ajax.html'  # Поскольку существенная часть форм в проекте рендерятся одним шаблоном, то зачем его везде указывать=) Впрочем, эта строка несущественна.
    def form_valid(self, form):
        form.save()
        return HttpResponse('OK')
    def form_invalid(self, form):
        errors_dict = json.dumps(dict([(k, [e for e in v]) for k, v in form.errors.items()]))
        return HttpResponseBadRequest(json.dumps(errors_dict))


We now have a class that we can safely inherit, like any other type of Class-based views . The call will look something like this:
from django.contrib.auth.forms import PasswordChangeForm # если кому-то будет полезно;-)
class PasswordChange(AjaxFormMixin):
    form_class = PasswordChangeForm


It can be used as CreateView only with the caveat that we __init __ () method of the parent FormMixin class does not accept the model argument and we will have to do this work on our own:
from django.forms.models import modelform_factory
class TaskUserAssign(AjaxFormMixin):
    form_class = modelform_factory(models.TaskRole)


AjaxUpdateFormMixin


So, we have a base class for handling ajax forms. Why not follow the django-way and create one more generic "in the image" to update yourself . In the same django.views.generic module we find the UpdateView class and the answer for us - we need the object attribute for our class ... Voila:
class AjaxUpdateFormMixin(AjaxFormMixin, UpdateView):
    def get(self, request, *args, **kwargs):
        self.object = self.get_object()
        return super(AjaxFormMixin, self).get(request, *args, **kwargs)
    def post(self, request, *args, **kwargs):
        self.object = self.get_object()
        return super(AjaxFormMixin, self).post(request, *args, **kwargs)

Without a fancy, we copy the code, we just inherit everything else from the existing AjaxFormMixin class . By the way, this class already has a model attribute, since we inherited it from UpdateView , whose roots go back to ModelFormMixin .

As a result, we have a set of classes that support the creation, modification of objects and other actions with them (if necessary, override is_valid () as well as / dev / brain and / dev / hands to taste) with ajax answers, for free inheritance not prohibited by law .


PS. The purpose of the article was not so much to demonstrate my achievements (although what is there to dissemble), but to analyze the process itself. A little later, when my assistant completes the application muzzle code, we will supplement the article with the front-end component code as well. In the meantime, thank you all for your attention.

Also popular now: