Итак, настроим gwt. Скачиваем eclipse 3.6 для java.
Далле переходим на страницы с загрузками GWT. Ставим gwt плагин для eclipse.
Создаем проект File > New > Web Application Project.
Название: genre
package: ru.musicmans
Запуск - Debug As > Web Application.
Переходим по адресу, устанавливаем плагин. Все работает.
Устанавливаем GWT Designer. Читаем quick start.
Далее можно конвертировать, созданный проект из gwt plugin (gwt plugin мы поставили потому, что в нем все равно находиться сам gwt) в gwt java project, который идет с дизайнером (правой кнопкой на проекте - convert to) или создать новый:
Сразу создадим модуль ru.musicmans.genre.GenreTree:
Итак, проект создан. Открываем genre.java и кликаем на вкладку Design.
Что нам надо для организации передачи данных? Мы не можем использовать rpc call gwt, так как у нас на стороне сервера django. Что делать в данном случае? Я рассматривал данный вопрос около года назад. Итог такой: возможен вывод данных в темплейте и преобразование их в javascript object, но это не очень оптимальный путь, тем более, что приложению обычно нужны данные в процессе работы (в том числе, обновленные). Поэтому лучшим решением мне предоставляется REST (с помощью этого подхода не надо проходить новый путь создания интерфейсов сервисов). Я решил не использовать SmartGWT, слишком он навороченный. В чистом GWT нет поддержки REST, поэтому воспользуемся Restlet Framework, ну а со стороны django - django-piston.
Скачаем Restlet Framework, Edition for Google Web Toolkit. Установим (укажем java build path в свойствах проекта).
Django-Piston
Теперь перейдем к серверной стороне. django-piston я уже как-то упоминал. Так вот, устанавливаем:
>c:\Python26\Scripts\pip.exe install hg+http://bitbucket.org/jespern/django-piston@c4b2d21db51a#egg=piston
Читаем документацию. Создадим приложение api, пропишем (r'^api/', include('api.urls')), в главном urls.py. Создадим в приложении файлы urls.py и handlers.py. Остальные файлы, кроме __init__.py можно удалить.
handlers.py:
from django.core.urlresolvers import reverse
from piston.handler import BaseHandler#@UnresolvedImport
from genre.models import GenreDirStyle#@UnresolvedImport
class GenreHandler(BaseHandler):
allowed_methods = ('GET', )
fields = ('name', 'type', 'url' )
model = GenreDirStyle
@classmethod
def url(self, genre):
return reverse('genre_genre', args=[genre.id])
def read(self, request, genre_id):
genre = GenreDirStyle.objects.get(id=int(genre_id))
return genre
urls.py:
from django.conf.urls.defaults import *
from piston.resource import Resource#@UnresolvedImport
from api.handlers import GenreHandler#@UnresolvedImport
genre_resource = Resource(handler=GenreHandler)
urlpatterns = patterns('',
url(r'^genre/(?P[^/]+)/$', genre_resource, name='api_genre_id'),
)
Переходим по адресу, например http://localhost:8000/api/genre/3/ :
{
"url": "/genre/id/4/",
"type": 3,
"name": "Prog-Rock"
}
Чтобы открывать application/json в firefox, установите дополнение, а еще лучше используйте RESTClient для Firefox.
Вернемся к клиентской части.
Добавим widget дерево в gwt приложение tree:
Запускаем отладку, кстати, сразу рекомендую изменить параметр logLevel в конфигурации отладки.
С помощью firebug видим, что ответа на запрос по адресу http://localhost:8000/api/genre/3 не увенчались успехом, поэтому вспоминаем про проблему SOP.
В процессе поиска решений наткнулся на django-crossdomainxhr-middleware.py, позволяющее использовать кроссдоменные запросы (требуется firefox > 3.5).
Но мы пока его использовать не будем.
Итак, проблему SOP при разработке решим следующим образом. Отключим Jetty сервер, запускаемый в отладке, а также укажем порт 8000.
Сделаем символическую ссылку с директории war проекта на www\media\static\gwt\genre, под windows, например, так:
www\media\static\gwt>mklink /d genre d:\path\to\gwt\war\
Далее, можно отлаживать приложение по адресу примерно такому (предварительно запустив веб-сервер отладки django) http://localhost:8000/media/static/gwt/genre/GenreTree.html?gwt.codesvr=127.0.0.1:9997 адресу. Параметр gwt.codesvr обязателен при отладке gwt приложения.
А еще лучше создать темплейт django, подключив к нему приложение gwt примерно следующим образом. Создаем div с id='genreTreeEntryPointId' в темплейте, а в gwt root panel определяем следующим образом:
RootPanel rootPanel = RootPanel.get("genreTreeEntryPointId");
Теперь мы можем отлаживать gwt приложение прямо в "окружении" django-проекта, например, по адресу такому http://localhost:8000/genre/tree/?gwt.codesvr=127.0.0.1:9997&genre_id=2 .
Серверный и клиентский код выкладывать слишком много, остановимся на нюансах:
- Выбор конкретного жанра в дереве (например при нажатии на меню жанров сверху). В этом случае загрузка списка для создания дерева вложенного множества осуществляется следующим образом (нам надо загрузить ветку, каждый элемент которой должен загрузить соседей):
treeqs = GenreDirStyle.objects.raw("""SELECT t2.*
FROM genre_genredirstyle AS t1
LEFT JOIN genre_genredirstyle AS t2
ON t2.lft BETWEEN t1.lft AND t1.rgt
WHERE t1.lft < %s AND t1.rgt > %s AND t1.tree_id = 1 AND t2.depth-1 = t1.depth AND t2.tree_id = %s
ORDER BY t2.lft;""", (genre.lft, genre.rgt, genre.tree_id))
Построение дерева решил возложить на плечи клиентов.
- При загрузке приложения выводим div с изображением, который заменяется самим приложением, после его загрузки, а также выполнения всех ajax запросов.
- Настройки приложения выводим как JSON объект в javascript, получая значения которого в gwt приложении:
private final Dictionary paramsDict = Dictionary.getDictionary("gwtGenreParameters");
String paramsDict.get("API_GENRES_MAIN_URL");
- Автоматический разбор JSON ответа. Используем данное приложение.
Оно зависит от: google-gin и totoe (погуглите и подключайте в проект).
- Для обозначения состояния элемента дерева используем своей объект класса (сокращенный вид):
private class GenreTreeItemData
{
private int id;
private Boolean alreadyLoaded = false;
private String description;
}
используя функцию setUserObject(Object).
- При создании проекта, в настройках gwt приложения наследуется стиль по умолчанию gwt standart. Так вот, проблема в том, что в нем есть правила css, переопределяющие наши (в том числе body). Решить это можно двумя способами, вот первый, а можно просто удалить нежелательные строки из standard.css файла в директории gwt\standard\ (их там немного и они вначале).
- Для генерации документации по API используем вид from piston.doc import documentation_view.
После работы все как обычно и результат:
ps. На стороне сервера попробовал использовать Aptana 3.0, там действительно отменная поддержка Django темплейтов в PyDev (но наткнулся на баг, ctrl+space вешает IDE, может только у меня так?).