Django форма подписки на AJAX

категория: Django
Недавно создавал форму подписки на новости на сайте pixity.ru. Решил немного описать. Задача: сделать форму с двумя полями (имя и e-mail) и кнопочкой "подписаться". Данные, при нажатии на кнопку, будут отправляться с помощью ajax и сохраняться в базу, поля формы при успешной отправке будут очищаться, а поменяется надпись на "спасибо". Создаем новое приложение "call" и пишем модель и в ней форму ModelForm. call/models.py:
#! coding: utf-8
from django.db import models
from django.forms import ModelForm
#
#
class Call(models.Model):
    name = models.CharField(u"имя", max_length=50)
    email = models.CharField(u"e-mail", max_length=150)
#
    class Meta:
        verbose_name = 'Подписка'
        verbose_name_plural = 'Подписки'
#
    def __unicode__(self):
        return self.name
#
#
class CallForm(ModelForm):
    class Meta:
        model = Call
Потом добавляем приложение call в INSTALL_APPS, делаем начальную миграцию модели, ведь мало ли что-то придется редактировать. Добавляем модель в админку. call/admin.py
#! coding: utf-8
from django.contrib import admin
from .models import Call
#
#
class CallAdmin(admin.ModelAdmin):
   list_display = ('name', 'email')
#
admin.site.register(Call, CallAdmin)
Прописываем ссылку. urls.py:
url(r'^call/$', 'call.views.save_call',),
Пишем вьюху, которая будет сохранять информацию в базу только при отправке аяксом. call/views.py:
#! coding: utf-8
from django.http import HttpResponse
from call.models import Call
#
from django.views.decorators.csrf import csrf_protect
#
#
@csrf_protect
def save_call(request):
    if request.method == "POST" and request.is_ajax():
        c = Call(
            name=request.POST.get("name", "")[:50],
            email=request.POST.get("email", "")[:150]
        )
        c.save()
        return HttpResponse("ok")
    else:
        return HttpResponse("bad")
Дальше создаем темплейт тег для формочки. В приложении call создаем папку templatetags, заходим в нее, создаем __init__.py и form.py. Регистрируем тег с нашей формой. call/templatetags/form.py:
#! coding: utf-8
from django import template
from call.models import CallForm
#
register = template.Library()
#
#
@register.inclusion_tag("call_form.html")
def show_form(request):
    return {
        "form": CallForm()
    }
Теперь пишем шаблон, в который передаем форму отправки. call/templates/call_form.html:
<form action="." method="POST" id="save-call">
    {% for field in form.visible_fields %}
        <div class="save-call-field">
            {{ field.label_tag }}<br>{{ field }}
        </div>
    {% endfor %}
    <button>ПОДПИСАТЬСЯ</button>
</form>
Прописываем красивые стили для всего этого дела, чтоб выглядело по сочней :). Добавляем наш тэг туда где должна быть форма подписки на обновления, в моем случае это footer.html. Подгружаем шаблонный тег {% load form %} и вставляем где будет находиться форма {% show_form request %}. И наконец пишем js, который будет отправлять данные асинхронно. Не забываем подключить jQuery. static/js/main.js:
$(".footer button").click(function() {
//
    var name = $("input[name=name]");
    var email = $("input[name=email]");
//
    var error = "";
// Проверка введена ли информация в поля формы
    if ((name.val() == '') || (email.val() == '')) {
         error = "Вы не ввели имя и e-mail";
         if (name.val() == '' && email.val() != '') {
            error = "Вы не ввели имя";
         }
         if (name.val() != '' && email.val() == '') {
            error = "Вы не ввели e-mail";
         }
         alert(error);
    }
// Если поля заполнены, отправляем их значения
    if (!error) {
        $.ajax({
            url: "/call/",
            type: 'POST',
            dataType:"html",
            data: {
                "name": name.val(),
                "email": email.val(),
            },
            error: function() {
                alert('Ошибка получения запроса');
            },
// При успехе очищаем поля и меняем кнопочку
            success: function(data) {
                name.val('');
                email.val('');
                $(".footer button").text("СПАСИБО")
                        .addClass("save-call-button");
            },
// CSRF механизм защиты Django
            beforeSend: function(xhr, settings) {
                console.log('-------------before send--');
                function getCookie(name) {
                    var cookieValue = null;
                    if (document.cookie && document.cookie != '') {
                        var cookies = document.cookie.split(';');
                        for (var i = 0; i < cookies.length; i++) {
                            var cookie = jQuery.trim(cookies[i]);
                            // Does this cookie string begin with the name we want?
                        if (cookie.substring(0, name.length + 1) == (name + '=')) {
                            cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
                            break;
                        }
                    }
                }
                return cookieValue;
                }
                if (!(/^http:.*/.test(settings.url) || /^https:.*/.test(settings.url))) {
                    // Only send the token to relative URLs i.e. locally.
                    xhr.setRequestHeader("X-CSRFToken", getCookie('csrftoken'));
                }
            }
        });// ajax
    }
    return false;
});
CSRF защита не влазит, возьмите ее здесь Если возникнут вопросы, не стесняйтесь - задавайте.


blog comments powered by Disqus