суббота, 10 июля 2010 г.

Django и AJAX

Итак, задача:

1. Ajax должен работать так же просто и эффективно как Django
2. Количество кода для средних задач должно быть настолько минимальным, насколько это возможно
3. Подключить Ajax-функцию должно быть не сложнее чем написать простейшую python-функцию

Решение:

Уж не знаю кто как решает эту задачу, но я уверен что почти каждый django-разработчик сталкивался с ней хотя бы раз в жизни. Вот и я однажды задумался по этому поводу. И почему-то мне захотелось использовать в решении этой задачи RPC. Настоящий такой, красивый и простой RPC...

И не знаю бы сколько велосипедов и паровозов я еще изобрел, если бы не наткнулся на один замечательный проект. Итак, внимание... DajaxProject.com

Судя по всему, проект еще достаточно зеленый, однако пользоваться библиотеками уже вполне можно и нужно. Кстати, о птичках. На сайте есть две библиотеки:

1. Dajaxice:
Связующее ядро dajaxproject. Ее основная цель заключается в банальном асинхронном взаимодействии в рамках кода на стороне сервера Django и вашего JavaScript-кода.

Работает оно примерно следующим образом:




2. Dajax:
Мощный инструмент для легкой и супер-быстрой разработки асинхронной логики в web-приложений с использованием Python и почти отсутствием JavaScript-кода.


Схема работы:



Итак, инструкция по применению:

1. Качаем стабильную версию Dajaxice и Dajax. Не забываем, что ядром является именно Dajaxice, поэтому оно всему и голова.
2. Распаковываем оба архива и в каждом поочередно делаем так:


$ sydo python setup.py install


3. В Вашем settings.py ищем INSTALLED_APPS и добавляем в самый низ две строки:


INSTALLED_APPS = ( 
'django.contrib.auth',
 'django.contrib.contenttypes',
 'django.contrib.sessions', 
'django.contrib.sites',
...
 'dajaxice', 
 'dajax', 
...
 )


4. Смотрим, чтобы TEMPLATE_LOADERS выглядели примерно так. Если нужно, редактируем:


TEMPLATE_LOADERS = (
    'django.template.loaders.filesystem.Loader',
    'django.template.loaders.app_directories.Loader',
    'django.template.loaders.eggs.Loader',
)


5. Добавляем новую переменную DAJAXICE_MEDIA_PREFIX в settings.py:


DAJAXICE_MEDIA_PREFIX="dajaxice"


6. В urls.py в самом конце добавляем новую строку:


urlpatterns = patterns('',
    ...
    (r'^%s/' % settings.DAJAXICE_MEDIA_PREFIX, include('dajaxice.urls')),
    )

7. Если необходимо, добавляем импорт в urls.py:


from django.conf import settings

8. Выбираем приложение, в котором нам нужно создать RPC-вызовы и создаем там новый модуль ajax.py. Я например, предпочитаю такие вещи хранить отдельно, поэтому создал отдельное приложение jsrpc.

9. Собственно пишем первый метод, который обработает наш AJAX-запрос:


# -*- coding: utf-8 -*-
from django.utils import simplejson 
from dajaxice.core import dajaxice_functions 

def primer(request,message):
    return_message=u'Полученное сообщение: {0}'.format(message)
    return simplejson.dumps({'message':return_message})


10. В settings.py делаем так, чтобы новый метод был виден для AJAX-запросов:


DAJAXICE_FUNCTIONS = (
        'jsrpc.ajax.primer',
         )

11. В шаблоне, в пределах тега пишем так:


{% load dajaxice_templatetags %}{% dajaxice_js_import %}

12. Создаем кнопку чтобы проверить работоспособность:


<input onclick="Dajaxice.jsrpc.primer('primer_callback',{'message':'Хэллоу Уорлд!'});" type="button" value="Push Me!" />


Перевожу... В onclick мы должны указать какую из подключенных функций мы будем юзать. После Dajaxice нужно указать только приложение и метод в модуле ajax.py. В нашем примере получается Dajaxice.jsrpc.primer. 

Далее, в качестве первого параметра указываем callback, в который возвратить данные, чтобы потом разгребать средствами JavaScript. В нашем случае - primer_callback, его мы создадим позже.

Ну и как же без набора параметров... Если мы что-то хотим передать серверу, пишем точно также как обычно передаем из сервера в шаблоны. В данном случае, мы просто передаем строку на сервер. А сервер нам должен вернуть ее же. Не забываем объявить параметр message в параметрах метода, иначе ничего не получится.

13. Напишем callback:


<script type="text/javascript">
function primer_callback(data){
    if(data!=Dajaxice.EXCEPTION){
        alert(data.message);
    }else{
        alert('Error');
    }
}
</script>



Обратите внимание, что мы получаем "свыше" уже сериализованные данные, что, безусловно, очень удобно.

14. Должно работать:


До сих пор мы пользовались только первой библиотекой - ядром Dajaxice. Что же нам могут предложить во второй библиотеке, если итак все уже шоколадно?


Скажу от себя, что предлагают нам не совсем "правильные" вещи, хотя и достаточно удобные.


Изначально Django проектировался так, чтобы полностью исключить любую возможную логику в шаблонах. И наоборот, серверная логика не должна знать вообще о существовании шаблонов, об их устройстве и структуре HTML. Если нам захотелось поменять верстку в шаблонах, это никак не должно отражаться на серверном коде. И это правильно. То что описано ниже, напрочь противоречит всем правилам! Однако, как бы то ни было, считаю своим долгом рассказать о том "неправильном" подходе, который предлагает библиотека Dajax, а дальше - выбирайте сами.


Так как мы уже подключили практически все необходимое, остается лишь одна маленькая деталь. В скачанном и распакованном архиве django-dajax-0.8.4 идем в каталог src/ и выбираем нужную библиотеку, в зависимости от того, какой JavaScript-фреймворк мы используем в проекте. Нам доступны:


1. Dojo
2. JQuery
3. Mootools
4. Prototype


Я пользуюсь JQuery, поэтому все нижеописанное будет на ее примере, хотя отличия для других библиотек незначительные.


1. Копируем jquery.dajax.core.js в каталог MEDIA_ROOT/js/.
2. В шаблоне, в пределах тега <head></head> подключаем эту самую библиотеку:


<script type="text/javascript" src="{{ MEDIA_URL }}/js/jquery.dajax.core.js" charset="utf-8"></script>


3. Подключили. Теперь создаем кнопочку и поле для вывода, чтобы почувствовать разницу в использовании:



<input type='button' value='Push Me!' onclick="Dajaxice.jsrpc.primer2('Dajax.process');" id='knopka'>
<div id='testarea'></div>


4. Создаем метод в уже созданном модуле


# -*- coding: utf-8 -*-
from django.utils import simplejson
from dajaxice.core import dajaxice_functions
from dajax.core import Dajax
...

def primer2(request):
    dajax = Dajax()
    dajax.assign('#knopka','value',u'Нажато')
    dajax.assign('#testarea','innerHTML',u'Бла-бла-бла')
    return dajax.json()


Обратите внимание, что значения для HTML-элементов Вы указываете именно на сервере. Это то о чем я писал выше. Неудобно, но зато JavaScript всего одна строка, да и то в пределах onclick. Решайте сами, использовать или нет.


5. Ну и подключаем метод в settings.py:


DAJAXICE_FUNCTIONS = (
        ...
        'jsrpc.ajax.primer2',
         )





6. Вот собственно и все. Теперь все должно работать:





4 комментария:

  1. А почему не использовать чистуюю библиотеку jQuery?

    ОтветитьУдалить
  2. Это Вы про $.ajax и про $.post?
    Не, ну этого пока никто не отменял, но лично мне этот подход совсем не нравится.
    Во-первых - чистотой кода ну совсем не пахнет.
    Во-вторых - сериализировать все приходится руками.
    В-третьих - Ловить переменные через аргументы гораздо удобнее, чем так:

    if request.method == 'POST':
    var = request.POST['var']

    В-четвертых - при правильном подходе, с помощью Dajax можно обойтись вообще без JS-методов и callback'ов.

    Этого недостаточно?

    ОтветитьУдалить
  3. в IE6 с callback все плохо =( приходится работать с чистым jquery

    ОтветитьУдалить