Cómo usar mixins para procesar formularios AJAX en Django

VM Vera Mazhuga Vera Mazhuga

Vera Mazhuga

Software Developer
3 min read.

Django es un framework de Python poderoso, pero desgraciadamente no ofrece solución alguna para procesar los formularios usando AJAX: enviar los datos al servidor, volver el listado de errores si los datos no habían pasado la validación.


Procesar los formularios con AJAX tiene unas ventajas: no es necesario recargar la página, ni renderizar todo el formulario de nuevo. Vamos a mirar como se puede validar los formularios usando Django y AJAX.


En Internet hay muchos ejemplos de cómo devolver los datos en el formato JSON desde la vista, pero aquí vamos a mover toda la lógica a un mixin. Esto va a permitirnos escribir todas nuestras vistas sin ninguna lógica adicional. Para nuestro ejemplo vamos a usar las vistas basadas en clases.


Describimos los pasos con más detalle:

  1. Creamos una clase del formulario muy sencillo de Django, basada en un modelo que se llama Comment:

    from django import forms
    from django.db import models
    
    class Comment(models.Model):
        created_at = models.DateTimeField(
            verbose_name=u'Date',
            auto_now_add=True,
        )
        message = models.TextField(
            verbose_name=u'Message',
        )
    
    class CommentForm(forms.Form):
    
        class Meta:
            model = Comment
    Escribimos una vista basada en clases que se dedica a crear nueva instancia del modelo Comment:
    from django.views.generic import CreateView 
    
    class CommentCreateView(JSONFormMixin, CreateView):
        model = Comment
        form_class = CommentForm
    Como se puede notar, este formulario aparte de la clase CreateView hereda un mixin JSONFormMixin.Vamos a verlo con mayor detalle:
    class JSONMixin(object):
        def render_to_response(self, context, **httpresponse_kwargs):
            return self.get_json_response(
                self.convert_context_to_json(context),
                **httpresponse_kwargs<
            )
    
        def get_json_response(self, content, **httpresponse_kwargs):
            return HttpResponse(
                content,
                content_type='application/json',
                **httpresponse_kwargs
            )
        def convert_context_to_json(self, context)
            u""" Este método serializa un formulario de Django y
            retorna un objeto JSON con sus campos y errores
            """
            form = context.get('form')
            to_json = {}
            options = context.get('options', {})
            to_json.update(options=options)
            to_json.update(success=context.get('success', False))
    
            fields = {}
            for field_name, field in form.fields.items():
                if isinstance(field, DateField) \
                        and isinstance(form[field_name].value(), datetime.date):
                    fields[field_name] = \
                        unicode(form[field_name].value().strftime('%d.%m.%Y'))
                else:
                    fields[field_name] = \
                        form[field_name].value() \
                        and unicode(form[field_name].value()) \
                        or form[field_name].value()
    
            to_json.update(fields=fields)
    
            if form.errors:
                errors = {
                    'non_field_errors': form.non_field_errors(),
                }
                fields = {}
                for field_name, text in form.errors.items():
                    fields[field_name] = text
                errors.update(fields=fields)
                to_json.update(errors=errors)
    
            return json.dumps(to_json)

    De forma predeterminada las vistas de Django vuelven la información en el formato text/html. Para cambiar este comportamiento, reescribimos el método render_to_response para que devuelva el objeto JSON. Luego, tenemos que redefinir el método convert_context_to_json, para indicar cómo procesar el objeto del formulario.

    En nuestro caso tenemos que obtener el formulario context.get('form') y formar desde sus atributos un diccionario to_json, que después vamos a devolver al cliente, convirtiéndolo previamente al formato JSON json.dumps(to_json). El diccionario to_json va a tener la siguiente estructura:

    {
        fields: {
            message: 'contenido del campo message',
          },
        errors: {
            non_field_errors: 'errores no relacionados con algún campo específico',
            fields: {
                message: 'errores relacionados con el campo message'
            }
        }
        options: { },   // parámetros adicionales que queremos devolver al cliente
        success: false  // o true si el formulario es válido
    }

    Como podemos ver, ahora tenemos todos los datos, que normalmente CreateView de Django devuelve a la plantilla, en el formato JSON.

  2. Ahora podemos pintar nuestro formulario en la plantilla:

    <form id="add_comment_form" method="post" action="{% url 'comment_create' %}">{% csrf_token %}
        {{ comment_form }}
        <a id="add_comment_submit" class="btn">Send
    id="add_comment_submit" class="btn"

    El siguiente paso será escribir una función de JavaScript que va a capturar el evento "submit" del formulario, serializa todos los datos diligenciados, los envía a nuestra vista CommentCreateView y procesa la respuesta.

    $('#add_comment_submit').click(function() {
        $.ajax({
            url: $('#add_comment_form').attr('action'),
            type: 'POST',
            data: $('#add_comment_form').serialize(),
            dataType: 'json',
            success: function(response) {
                if(response.success){
                    $('#add_comment_form')[0].reset();
                    $('.form-error').remove();
                    $('#comments').prepend(response.fields.message);
                } else {
                    $('.form-error').remove();
                    for(var error in response.errors.fields) {
                        $('#add_comment_form #id_' + error).before('<div class="form-error">' + response.errors.fields[error] + '</div>');
                    }
                }
            }
        });
    });

    En caso de éxito el algoritmo primero revisa si el formulario es válido o no. En caso de ser válido, limpia todos los campos del formulario y los errores en caso de que antes hubiese pintados y agrega el mensaje al listado de comentarios. Si los datos tienen errores, en el bucle vamos por todos los campos del formulario y agregamos el texto del error correspondiente.

    Para simplificar el manejo de formularios en JavaScript se puede usar el plugin jQuery Form.

    Al final tenemos un mixin, que nos permite procesar los formularios de Django con facilidad y devolver los datos en el formato JSON, que podemos usar para todos nuestros formularios que necesiten validación vía AJAX.


Written by Vera Mazhuga

VM Vera Mazhuga Vera Mazhuga

Vera specializes in writing and maintaining code for various applications. Her focus on problem-solving and efficient programming ensures reliable and effective software solutions.

Newsletter

Subscribe to our newsletter:

Read more

Git sabe muchas cosas

Git hace un montón de cosas por nosotros; entre las más comunes/reconocidas: Mantiene un registro de todos los cambios/archi...

4 min read.

Build Once. Own Forever.