Django и jQuery Template

Published 9/21/2011 by e0ne in Python | Web Development
Tags: ,

 

По отдельность Django и плагин jQuery Template у меня работали хорошо. А вот вместе возникли небольшие проблемы. Вот только не знаю: это все из-за моей невнимательности или данная фича/бага плагина тоже сыграла свою роль.

Вначале просто  data binding работал отлично и никаких проблем не предиделось. Но стоило только появиться необходимости использовать тег {{if}} из jQuery Template, встретились первые неожиданности.

Неожиданность номер раз:

Не совсем, конечно, неожиданность, а, скорее, первая меленькая проблемка. Конструкция “{{“ - совпадает с синтаксисом шаблонов в Django, от чего мы получаем ошибку что у нас неправильный темплейт. Пришлось открыть доки django и найти там что такое template tag и как им пользоваться.

В итоге мой шаблон стал выглядеть, примерно, таким образом:

{% templatetag openvariable %} if highlight {% templatetag closevariable %}
  <div style="background-color: ${color}">
{% templatetag openvariable %} else {% templatetag closevariable %}
  <div>
{% templatetag openvariable %} /if {% templatetag closevariable %}
  ${name}
</div>

Читабельность всего этого упала в разы, но стало работать. Не совсем правильно, но работать...

Неожиданность номер два:

Вместо ожидаемого результата на странице я получал нечто похожее на:

{{ if highlight }}
  <div style="background-color: red">
{{ else }}

Первая мысль - в разметке страницы заэкранированы символы “{{“ и “}}”. Но, как часто это бывает - первая идея оказалась неправильной. Снова открыл доки Django и jQuery Template. Далеко не сразу заметил что в примерах jQuery Template нет пробела между “{{“ и “if”... Неожиданно, но удаление примеров помогло и все сразу заработало. 

Вывод один - читать документацию (любую!) по диагонали нельзя!

Пример, уже традиционно, лежит на GitHub: https://github.com/e0ne/BlogSamples/tree/master/DjangoAndJqueryTemplate

 


Выбор веб-фреймворка не в .NET стеке для нового проекта достаточно нетривиальная задача. Их много - больших, маленьких, хороших и не очень, горячих и зелёных. Так как при работе с Python больше сталкивался с Django, то для себя, т.е. очень IMHO, сделал несколько правил.

Использовать Django нужно когда: 

  • нужно получить опыт с Django;
  • нужно сделать быстро сайт с админской частью (блог, CMS и т.д.);
  • есть хорошее готовое приложение/модуль для Django и его нужно сомсем немного доточить напильником;
  • нет необходимости заморачиватья с DAL (data access layer) и стандартного ORM вполне достаточно;
  • какие-то из модулей Django уж ооочень хорошо подходят для текущей задачи;
  • нужно сделать что-то очень быстро и нет опыта с другими фреймворками.
Итого получилось 6 пунктов. Если не выполняются хотя бы 3, то нужно задуматься о целесобразности использования Django. IMHO.

Как сказал один из авторов Django в своей книге: "Django - это всего-лишь приложение на Python".

 


То, о чём писал в твиттере, но не мог написать в блоге раньше. 

Была небольшая и, на первый взгляд, достаточно простая задача - показать на странице таблицу, с возможностью сортировки и автообновления. Ну ещё и поиск по ней. После некоторого времени, потраченного на поиск и попытки исправления существующих решений стало понятно, что написать с нуля будет быстрее и дешевле (тут имеется в виду также дальнейшая поддержка всего этого). Готовые реализации javasctipt-библиотек и плагинов к jQuery были или слишком уж навороченный для данной задачи или, мягко говоря, очень плохо справлялись с обновлением таблицы и последующей сортировкой. Результатом этого всего стало написание плагина для jQuery c нуля.

Коротко о плагине:

 

  • позволяет автоматически обновлять и сортировать таблицы;
  • построен на базе jQuery Template (в будущем планируется версия без этого);
  • сортировка работает исходя из данных что пришли от сервера в JSON, и не зависит от html-разметки;
  • маленький размер и минимум функциональности.

Более подробно с примерами и какой-то документацией о плагине можно почитать на http://ajaxtable.e0ne.info/. Также страница в официальном репозитории пакетов http://plugins.jquery.com/project/jQueryAjaxTable и исходники на GitHub: https://github.com/e0ne/jQuery-AjaxTable.

Комментарии, пожелания и замечания очень приветствуются.

 


 

Все знают, что JavaScript - динамический язык, что несет за собой некоторые особенности, например, отсутствие констант. Правильнее было бы сказать, что отсутствие констант - это проблема не самого JavaScript, а браузеров. 

Согласно спецификации ECMAScript 5th Edition, ECMA Script (для простоты понимания и удобства буду использовать термин JavaScript) у объекта Object должен быть метод freeze, который принимает объект и создает на его основе новый неизменяемый (inmutable) объект, у которого все свойства становятся read only и пропадает возможность удалить и/или изменять свойства объекта.

Синтаксис очень простой: Object.freeze(obj);

Для проверки, является ли объект замороженным, существует метод Object.isFrozen(obj).

Пример небольшого кода, чтобы убедится что все работает:

var obj =
   {
      prop: function () {},
      foo: "bar"
   };   
obj.foo = "baz";
obj.lumpy = "woof";
delete obj.prop;
var o = Object.freeze(obj);
alert(o === obj);
alert(Object.isFrozen(obj) === true);
alert(obj.foo);
obj.foo = "hello";
alert(obj.foo);
delete obj.foo;
alert(obj.foo);
obj.func = "function";
alert(obj.function);

Более детальное описание и примеры можно найти на страницы разработчиков FireFox: https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Object/freeze

И всё было бы хорошо, если б не браузеры, а, точнее, их поддержка или неподдержка метода Object.freeze. Если верить таблице http://kangax.github.com/es5-compat-table/, то его поддерживают только последние версии Internet Explorer, FireFox, Google Chrome и всё. Такой код не будет работать в Safari и Opera, что практически уничтожает всю полезность данного подхода. 

Так что использовать такие “константы” или нет - решать нужно исход из специфики проекта, а именно - списка поддерживаемых браузеров. В моём случае, к сожалению, поддержка Safari и Internet Explorer 8 намного важнее, чем использование почти настоящих констант в JavaScript’е.

 


 

Рассказ о “странном” баге и о том, как влияет верстка работу всего приложения.

При работе с ASP.NET Webforms мы постоянно сталкиваемся с формами. По умолчанию, это одна серверная форма <form id=”aspnetForm” runat=”sever”>, расположенная сразу же за тегом <body>. Но это, можно сказать, классический пример - такую заготовку делает нам Visual Studio при создании другого проекта. На практике же всё может сильно отличаться.

 

Следует отметить, что на странице вы не можете создать более одной серверной формы (с атрибутом runat=”server”). Такое уж ограничение архитектуры ASP.NET. Почему так - догадаться не сложно, но...

 

Но в жизни в проекте бывают ситуации, когда просто необходимо добавить ещё одну форму на страницу. Из достаточно распространённых примеров это: 

 

когда в шапке сайта где-то рядом с меню нужно разместить форму поиска, при этом, для увеличения скорости загрузки, сама шапка сайта находится вне серверной формы. Такой случай достаточно распространённый и уже практически стал стандартом де-факто для разработчиков.

 

Решение данной задачи достаточно простое - добавление второй формы (<form>) на страницу, но уже без атрибута runat=”server”. Следует отметить, что с точки зрения html - несколько форм на одной странице являются абсолютно нормальным и работающем решением. Если только не наступать та те же грабли, на которые наступил я...

 

А допустил я достаточно “детскую” ошибку - т.к. было ограничение на одну серверную форму, то я добавил вторую, клиентскую, внутрь серверной. На первый взгляд всё работало хорошо, обе формы успешно отправлялись на сервер и отправляли все необходимые данные. Вот только работало это в браузерах Google Chrome, Safari и FireFox. Проблемы начались в Opera и Internet Explorer. Выглядело это, на мой взгляд, действительно потрясающе: 

 

Форма фидбека с UpdatePanel двумя TextBox и LinkButton на странице. Обработчик OnClick у кнопки успешно отрабатывал, за исключением того, что значения текстбоксов были пустыми. Дело было вечером и я подумал что проблема в UpdatePanel. Вот только странно было что в IE это тоже не работало. Под нож попала UpdatePanel, что не дало никакого положительного результата. “Странно” - подумал я и проверил это всё ещё раз в разных браузерах. Баг был на месте. Тут пришлось вплотную взяться дебаггером за эту страницу и через некоторое время, внимательно посмотрев на Request.Params причина была обнаружена - сабмитилась не та форма.

 

Дальнейшие танцы с бубном возникшие идеи результата не принесли я на помощь пришел Google:

 

 

Обрадовало меня сразу две вещи:

 

  • такая проблема была не только у меня;
  • причина всего этого безобразия стала ясна уже только при беглом просмотре результатов выдачи гугла - 3-я ссылка вела на сайт W3C, на которой черным по белому было написано: “the FORM element can’t be nested”. (http://www.w3.org/MarkUp/html3/forms.html)

 

 

После этого проблема была быстро решена способом, описанным в начале поста, но мне захотелось ещё немного копнуть внутрь.

 

Тут же был создан небольшой html файл (forms1.html)  и был дан валидатору http://validator.w3.org/. К моему удивлению, валидатор был уверен в том, что html код является полностью валидным. После смены doctype с Transitional на Strict (forms2.html) страница уже оказывалась невалидной. И тут всё окончательно прояснилось и стало на свои места.

 

С точки зрения html, как и с точки зрения здравого смысла - ничего хорошего не принесут. Всё дело лишь в том, что с годами некоторые браузеры стали настолько умными, что уже понимают практически все общеизвестные ошибки в вёрстке и делают это так, как, по их мнению, задумывалось. Отсюда и различное поведение одних страниц в разных браузерах, и попытки обойти проблемы одного браузера, не поломав ничего в других и т.д.

 

В который раз убеждаюсь в необходимости грамотного верстальщика на любом проекте и необходимости соблюдать html-стандарты на своих сайтах. Только не фанатично, у всех правил есть исключения...

 

Исходники форм: https://github.com/e0ne/BlogSamples/tree/master/NestedForms

 


Dev Time #4 - Python

Published 3/31/2011 by e0ne in Events | Python | Web Development

Среда, 13 апреля этого года должна была пройти так же, как и остальные среды, но не тут-то было. В этот день состоится очередная встреча харьковского сообщества разработчиков Dev Time (мой отчет с первой встречи: http://blog.e0ne.info/post/First-Kharkov-DevTime-event-summary.aspx). 

Особенностью этой встречи будет то, что это первое подобное события на моей памяти в Харькове, посвященное языку программирования Python. О Python будет говорить Настя Хоменко aka @Eva__Brown с докладом "Python Tips" (детали уточняются). Вторым докладчиком буду я. И поговорю я о Google App Engine и web фреймворке tipfy

Примерное содержание моего доклада:

  • Goole App Engine (GAE) и Python
  • Почему не Django?
  • Преимущества и недостатки фреймворка tipfy
  • Быстрая разработка веб-приложений с помощью фреймворка tipfy
  • Расширяем tipfy или "после сборке доработать напильником"

Пожелания, относительно моего доклада, приветствуются. 

Приходите, надеюсь вам понравится.

 

P.S. Для посещения мероприятие чтение поста "tipfy - маленький, но мощьный web framework для GAE" является не обязательным, а вот регистрация на сайте Dev Time - обязательна.


Чем дольше пользуюсь NuGet, тем больше интересных вещей с его нахожу. Одной из таких библиотек является elmah. Простота установки, особенно с использованием NuGet, и широкие возможности настройки подойдут к практически любому проекту. Я сам для этого раньше предпочитал использовать Logging модуль из Microsoft Enterprice Library, но сейчас понял что есть более гибкие и современные решения.

Elmah создан специально для того, чтобы логировать ошибки в ASP.NET приложениях.

Среди его функций есть такие:

  • логирование ошибок в различные БД (MS Sql, Oracle, PostgreSQL, SQLite и др.);
  • экспорт логов в формат CSV для последующего анализа;
  • создание RSS-фида для получения информации о новых ошибках;
  • отправление сообщений о новых исключениях прямо в ваш twitter!

Отдельно хочу сказать о логировании ошибок, которые произошли в момент обновления UpdatePanel - иногда это бывает очень полезно.

Если вы всё ещё по каким-то причинам не используете NuGet, то для вас установка будет происходить следующим способом:

  • скачать сборку Elmah.dll (всего 156KB);
  • добавить в ваш веб-проект reference на elmah;
  • последний и самый сложный пункт - внести необходимые изменения в web.config.

Так как elmah работает как обычный web handler, то проблем с ним быть никаких не должно.

По умолчанию, вся информация об ошибках хранится в памяти, а это значит что данные будут храниться пока вы не перезапустите ваш веб-сервер.

Для того, чтобы посмотреть последние логи, необходимо открыть следующий URL: http://localhost:8090/elmah.axd. В качестве тестового приложения я создал новый Web Application и посмотрел на логи:

На удивление обнаружил, что в логах уже есть одна 404-я (Not Found) ошибка. Посмотрев на детали исключения сразу становится понятно, что не найден файл favicon.ico.

Для примера, я поместил на страницу кнопку, обработчик OnClick которой бросает исключение:

throw new NotImplementedException();

Посмотрим теперь на нашм логи:

У нас появилась запись с кодом 500 (Server Error) и типом NotImplemented.

В деталях каждой записи кроме времени и текста ошибки хранится полный callstack, все параметры, которые пришли в запросе (Request.Params) и, на мой взгляд самое интересное - ссылка с именем “Original ASP.NET error page”. Перейдя по ней можно посмотреть как выглядела страница, когда произошла ошибка. Таким образом можно увидеть что показывалось пользователю, когда это случилось.

Последняя фича, о которой я хотел бы сказать - это возможность настройки фильтрации исключений. Это позволит вам логировать только те исключения, которые вам необходимы.

На этом мой короткий обзор закончен. Более детально об elmah можно прочитать на официальной странице (http://code.google.com/p/elmah/). Моей целью не было написать полный и подробный обзор-руководство по этой очень удобной и полезной библиотекой. Цель этого поста - всего-лишь познакомить с elmah тех читателей, которые еще не слышали о ней.

P.S. Для самых ленивых выложил тестовый проект на GitHub: https://github.com/e0ne/BlogSamples/tree/master/ElmahSampleProject


Наверняка любой разработчик, который пишет для web сталкивался с проблемой кэширования контента. При этом проблема может делится на две части: кэширование динамического контента и кэширование статического контента. Эти проблемы связанные с оптимизацией времени загрузки сайта. Я же подойду к этой проблеме с другой стороны: проблемы с кэшированием при разработке. Ведь все мы сталкивались с тем, что при изменении всего-лишь одной строке в javascript или изменении класса в CSS приходится в очередной раз очищать кэш браузера. И никакие магические сочетания Ctrl + F5 не помогают. В случае с Internet Explorer не всегда помогает даже очиситка кэша (Ctrl+R) в IE Developer Tools - приходится это делать стандартными средствами браузера.

Для себя я нашел следующий выход из сложившейся ситуации: отключение кэширования на стороне веб-сервера на момент разработки. Почему это нельзя сделать в браузере? Одна из причин - потому что, как правило, сайт разрабатывается для более чем одного браузера. Другая - можно забыть включить обратно и работы с другими сайтами будет не такая комфортная.

Уточню, что я редко использую веб-сервер, встроенный в Visual Studio, т.к. считаю что IIS, встроенный в Windows Vista/7 более приближен к тому решению, которое будет на stage и production environments. Из этих же соображений некоторый используют Windows Server на своих рабочих станциях.

Как всегда, сначала немного теории о том, как это всё кэширование работает.

В заголовке ответа от веб-сервера (Response) может быть поле Cache-Control, которое говорит браузеру о том, как нужно кэшировать полученный файл. Самые используемые поля это:

  • no-cache - отключить кэширование;
  • max-age - максимальное время (в секундах) жизни закешированного ресурса;
  • min-fresh - минимальное время (в секундах) жизни закешированного ресурса.


Так же часто используется параметр “Expires”, который содержит дату, когда необходимо будет заново загрузить ресурс с сервера.
Подробнее о всём это можно почитать в спецификации HTTP протокола по адресу http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html.

Ещё одним способом указать браузеру, что запрашиваемый ресурс не изменился является ответ с кодом “304 Not Modified” (http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html).

Таким образом, для отключения кэширования нам достаточно всегда добавлять поле no-cache в заголовок response. Побочных эффектов я тут вижу два:

  • нужно не забыть потом включить кэширование;
  • при отключенном кэшировании производительность сайта может значительно упасть, но я надеюсь, что у всех нас имеются достаточно мощные ПК для комфортной работы/разработки.

Подробно всё уже давно описано на сайтах Technet (http://technet.microsoft.com/en-us/library/cc770661(WS.10).aspx) и IIS (http://www.iis.net/ConfigReference/system.webServer/staticContent/clientCache), поэтому повторяться не буду, тем более что у меня вряд ли получится написать лучше. Скажу только пару слов о моём решении.

Кэширование я отключаю с момощью комманды “appcmd set config /section:staticContent /clientCache.cacheControlMode:DisableCache”, которую я поместил в bat-файл discache.bat.

Включаю аналогичной командой “appcmd set config /section:staticContent /clientCache.cacheControlMode:NoControl” из файла encache.bat.

Ярлыки для этих файлов можно поместить в удобное для вас место либо добавть путь к ним в переменную окружения PATH для быстрого доступа к ним из любого места. Таким образом включение/выключение кэширования происходит одним кликом/одной строчкой в командной строке и позволяет избавиться от всех проблем при активной работе с javascript, CSS и картинками.


jQuery - достаточно мощный и удобный javascript framework. Последняя версия 1.4.4 имеет размер 179KB для разработчиков и всего-лишь 26KB для использования в production. 26 килобайт кода не много, но в таком сжатом формате разобраться практически не возможно даже с помощью дебаггера. В неупакованный версии разобраться проще, в дебаггере всё выглядит хорошо, но всё-равно все тонкости работы и устройства jQuery проходят мимо нас. В 7180-ти строках javascript-кода даже с помощью средств IDE и не менее мощной комбинации Ctrl+F  найти нужный кусок кода не так уж и просто.

Что же делать тем, кто хочет разобраться к том, как работает jQuery? Ответ достаточно простой: скачать исходники. Звучит странно и непонятно. В голове возникает вопрос: какие могут быть исходники у библиотеки, написанной на javascript? Под фразой “исходники jQuery” я подразумеваю то, что лежит у них в репозитории http://github.com/jquery/jquery.

 


Сразу уточню, что описываю я бранч версии 1.4.4. В текуoем master branch (trunk), в котором идёт работа над версией 1.5, переработан модуль ajax, из-за чего все, что к нему относится, вынесено в каталог /src/ajax. Кроме того, разработчики настоятельно не рекомендуют использовать последнюю версию из репозитария для production использования. На момент написания этого поста последняя версия - 1.5pre.

Итак, вернёмся к нашим баранам исходникам.  В корне у нас есть следующие 4 каталога, файлы лицензий и файлы для build-систем (make и ant):

  • build - файлы, необходимые для сборки;
  • speed - тесты на производительность (benchmarks): необходимы для того, чтобы измерить производительность текущей версии, по сравнению со собранной из исходников; если вы не вносили никаких изменений - время выполнения обоих скриптов будет одинаковым;
  • test - unit tests (модульные тесты), написанные с помощью qunit;
  • src - сами исходники jQuery.

Для сборки необходимы Java 1.6.0 и выше и NodeJS, если вы будете собирать из master branch. Сборка выполняется одним из двух способов: с помощью ant, которому нужно передать в качестве параметра файл build.xml, либо с помощью выполнения команды make, которая доступна во всех *nix-системах. После сборки файлы jquery.js и jquery.min.js будут находится в папке /dist.

Какие же есть преимущества у использования такой версии jQuery? На мой взгляд, стоит выделить такие:

  1. В исходниках значительно проще разобраться, чем в одном собранном файле. Здесь каждый модуль расположен в отдельном файле (css.js, event.js и т.д.), что даёт возможность быстро и легко узнать как это всё работает. Этот способ и натолкнул меня на  этот подход к использованию jQuery и написанию этого поста.
  2. Есть возможность отключить ненужные/не используемые модули для уменьшения размера итогового файла. Этот подход применяется при скачивании http://jqueryui.com/download с нужными компонентами.
  3. Можно вносить необходимые для проекта изменения и с помощью тестов убедиться что всё работает. Эти изменения также можно будет предлагать разработчикам jQuery в виде патчей.
  4. При использовании последней версии вы становитесь бета-тестерами и вносите свой, пусть и небольшой, но всё же вклад в развитие проектов :).


По своему опыту могу сказать, что даже для тестовых приложений использование самых последних исходников приносит достаточно много проблем: иногда это просто не собирается (http://forum.jquery.com/topic/can-t-build-latest-jquery-from-the-git-repository), могут не работать многие плагины, особенно с учетом переработанного в версии 1.5pre модуля ajax. Но при использовании бранча с последнего стабильного релиза, inho, плюсов значительно больше.


 

Так уж сложилось, что мне было необходимо выбрать framework для разработки небольшого приложения на python + GAE. Первым делом я посмотрел в сторону Django и немного огорчился, узнав что теперь GAE team рекомендуют использовать форк django-nonrel - практически тот же django, но дающий возможность простой работы с NoSQL базами данных. Так как django-nonrel все ещё не достиг версии 1.0 и имеет ряд недоработок, которые разработчики обещают исправить в ближайшее время. Из недостатков, которые для меня оказались решающими стоит отметить - не работает с GAE “из коробки”, соответственно необходимо многое доустанавливать-настраивать руками, на что не хотелось тратить время. Позже я обязательно напишу про этот фреймворк, но уже на примере django-nonrel + MongoDB. Тем временем мне порекомендовали посмотреть в сторону tipfy, на котором я остановился и о котором написан этот пост.

Как написано на сайте http://www.tipfy.org/, tipfy - маленький, но мощьный web framework, сделанный специально для GAE (“tipfy is a small but powerful framework made specifically for Google App Engine”). Текущая версия - 0.6.4, что говорит нам о том, что у него всё ещё впереди.

tipfy действительно очень маленький и быстрый, но благодаря расширениям (extensions) быстро приобретает необходимую функциональность. Посмотрим на структуру архива, который можно скачать по адресу http://www.tipfy.org/tipfy.zip

 

 

 

Всё, что лежит в корне архиве необходимо только для сборки своей версии tipfy, с добавленными или отключенными расширениями. К слову, добавление или отключение какого-либо расширение осуществляется редактированием всего одной строчки в файле buildout.cfg и запуском билд-скрипта, который загрузит и установит нужные расширения. Подробнее об этом можно почитать на странице http://www.tipfy.org/wiki/guide/extensions/#adding-or-removing-extensions

 

Всё, что необходимо для запуска и работы приложения находится в каталоге app. Как видно на скриншоте, там уже есть приложение “hello world” (куда же без него? :)). После “hello world” сразу же бросается в глаза jinja2 - достаточно известный template engine с хорошей докоментацией и сообществом. Если по каким-то причинам вам не нравится(перечеркнуто) подходит jinja, можно использовать Genshi или Mako.

В целом, приложение с использованием tipfy практически ничем не отличается от любого MVC-приложения, за исключением, конечно, специфики фреймворка.

Рассмотрим приложение “hello world” более детально.

Так как, это достаточно простой пример, то никакой модели нет. Но в случае необходимости - модели будут представлять собой обычные классы, наследуемые от GAE db.Model:

 

Наше приложение состоит из двух модулей (url.py и handlers.py) и одного шаблона (hello_world.html).

urls.py:

Содержит правила маппинга url-адресов на обработчики. По умолчанию их два:

 

  • Rule('/', endpoint='hello-world', handler='apps.hello_world.handlers.HelloWorldHandler') - указывает какой обработчик должен обрабатывать default page
  • Rule('/pretty', endpoint='hello-world-pretty', handler='apps.hello_world.handlers.PrettyHelloWorldHandler') - обработчик, который выполнится при открытии http://yoursite.com/pretty

 

handlers.py:

Каджый handler представляет собой класс, который наследуется от RequestHandler и имеет методы get(), post и др. для обработки соответствующих типов запросов.

В нашем случай это: 

 

 

 

В целом отмечу что переход с django на tipfy не вызывает никаких трудностей, только несколько маленьких неудобств, а именно: отсутствие уже готовой админки (частично, её может заменить аналога приложения в GAE) и другой template engine (хотя, многие успешно используют jinja в djanпo).

Такое короткое введение в tipfy и GAE. Продолжение следует...