Публикации с меткой «django template»

Сергей Мельниченко

Как подружить Django с шаблонами в utf-8 c BOM атрибутом?


При работе с Django шаблонами периодически сталкиваюсь с проблемой, когда в браузере появляются "пробелы"(белые зоны) в дизайне. Первое рассмотрение проблемы, используя FireBug, заводит в тупик. Свёрстанный макет отображается нормально, а вот та же страница, собранная из нескольких файловых шаблонов в Django имеет при отображении "пробелы". Анализ результирующих html страниц показал, что на "бракованной" странице появляется череда байтов "EF BB BF".

Небольшое отступление... Череда байтов "EF BB BF", именуемая меткой порядка байтов (byte order mark, BOM), применяется для обозначения формата UTF-8, хотя к этому формату и неприменимо понятие порядка байтов.

Поиск решений

Поискав решение проблемы наткнулся на пару топиков:
- Template adds extra characters when using utf8 file encoding.

"The first bytes of your header.html file are EF BB BF. That's the UTF-8 encoding of the BOM...It proposes noting, stripping, and relocating the BOM to the front of the ultimately rendered template."

- Django Templates and BOM (byte order marks).

"If I will render page.html output will contain TWO byte order marks:
!UTF_8_BOM!!UTF8_BOM!<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01
Transitional//EN" ... "


- weird problem with django templates.

"...when templates are being rendered (i guess), in code, right before the doctype, strange character is being placed. I call it "strange" because if I select it, copy it and try to paste nothing is pasted. This "dot" is seen only if I do "view source" in webkit browsers..."

Все решения сводятся к одному - сохраняйте файлы шаблонов в формате UTF-8 без BOM! :) А что делать, если редактор не позволяет сохранять файлы в кодировке UTF-8 без BOM?

Тут есть несколько решений:
1. Запустить скрипт, который удалит все BOM символы в шаблонах.
2. Использовать обработку шаблонов при загрузке.

Например, в качестве IDE я использую Eclipse и кодировку UTF-8 для файлов. Ну и вот этот "паршивец" считает своим долгом поместить в каждый файл шаблона BOM байты (поиск решение по отключению этого эффекта не дал результата). А каждый раз запускать скрипт не удобно, так что выбираем второе решение.

Обработка шаблонов для удаления BOM символов при загрузке

Смотрим какие загрузчики нужно поправить (settings.py):

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

Писать наследников "лень", так что используем monkey patching. Пишем небольшую корректирующую функцию :), помещаем ее и инициализацию в файл urls.py.


def patch_load_template_source(func):
def decorator(self, template_name, template_dirs=None):
item = func(self, template_name, template_dirs)
//для сравнения, по хорошему, bom байты нужно переводить в ту же кодировку, что и кодировка загруженного файла. опустил этот момент.
if item[0][:1]==u'\ufeff':
return (item[0][1:], item[1])
return item
return decorator

//патчим filesystem.Loader
from django.template.loaders import filesystem
filesystem.Loader.load_template_source = patch_load_template_source(filesystem.Loader.load_template_source)

//патчим app_directories.Loader
from django.template.loaders import app_directories
app_directories.Loader.load_template_source = patch_load_template_source(app_directories.Loader.load_template_source)

ЗЫ: Данный пример не претендует на звание идеального, а лишь демонстрирует рабочую версию одного из решений :).

Сергей Мельниченко

Как подружить шаблоны Django в кодировке utf-8 c BOM байтами?



При работе с Django шаблонами периодически сталкиваюсь с проблемой, когда в браузере появляются "пробелы"(белые зоны) в дизайне. Первое рассмотрение проблемы, используя FireBug, заводит в тупик. Свёрстанный макет отображается нормально, а вот та же страница, собранная из нескольких файловых шаблонов в Django имеет при отображении "пробелы". Анализ результирующих html страниц показал, что на "бракованной" странице появляется череда байтов "EF BB BF".

Небольшое отступление... Череда байтов "EF BB BF", именуемая меткой порядка байтов (byte order mark, BOM), применяется для обозначения формата UTF-8, хотя к этому формату и неприменимо понятие порядка байтов.

Поиск решений

Поискав решение проблемы наткнулся на пару топиков:
- Template adds extra characters when using utf8 file encoding.

"The first bytes of your header.html file are EF BB BF. That's the UTF-8 encoding of the BOM...It proposes noting, stripping, and relocating the BOM to the front of the ultimately rendered template."

- Django Templates and BOM (byte order marks).

"If I will render page.html output will contain TWO byte order marks:
!UTF_8_BOM!!UTF8_BOM!<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01
Transitional//EN" ... "


- weird problem with django templates.

"...when templates are being rendered (i guess), in code, right before the doctype, strange character is being placed. I call it "strange" because if I select it, copy it and try to paste nothing is pasted. This "dot" is seen only if I do "view source" in webkit browsers..."

Все решения сводятся к одному - сохраняйте файлы шаблонов в формате UTF-8 без BOM! :) А что делать, если редактор не позволяет сохранять файлы в кодировке UTF-8 без BOM?

Тут есть несколько решений:
1. Запустить скрипт, который удалит все BOM символы в шаблонах.
2. Использовать обработку шаблонов при загрузке.

Например, в качестве IDE я использую Eclipse и кодировку UTF-8 для файлов. Ну и вот этот "паршивец" считает своим долгом поместить в каждый файл шаблона BOM байты (поиск решение по отключению этого эффекта не дал результата). А каждый раз запускать скрипт не удобно, так что выбираем второе решение.

Обработка шаблонов для удаления BOM символов при загрузке

Смотрим какие загрузчики нужно поправить (settings.py):

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

Писать наследников "лень", так что используем monkey patching. Пишем небольшую корректирующую функцию :), помещаем ее и инициализацию в файл urls.py.


def patch_load_template_source(func):
def decorator(self, template_name, template_dirs=None):
item = func(self, template_name, template_dirs)
//для сравнения, по хорошему, bom байты нужно переводить в ту же кодировку, что и кодировка загруженного файла. опустил этот момент.
if item[0][:1]==u'\ufeff':
return (item[0][1:], item[1])
return item
return decorator

//патчим filesystem.Loader
from django.template.loaders import filesystem
filesystem.Loader.load_template_source = patch_load_template_source(filesystem.Loader.load_template_source)

//патчим app_directories.Loader
from django.template.loaders import app_directories
app_directories.Loader.load_template_source = patch_load_template_source(app_directories.Loader.load_template_source)

ЗЫ: Данный пример не претендует на звание идеального, а лишь демонстрирует рабочую версию одного из решений :).

Еще один блог о Django

Используем встроенные строковые методы Python'а в Django шаблонах

Вместо предисловия

Привет! Давно здесь не отписывался. Почему? Наверное главная причина, что после выхода 1.1 версии уже не так активно слежу за развитием Django. Может быть в ближайшее время меня пробъет на творчество и я выдам пару-тройку новых постов, но не обещаю ;)


Но это я отвлекся от темы поста. А она заключается вот в чем. Надо было сегодня в шаблонах Django для некоторых урлов убрать конечные слеши, т.е. просто вызвать url.rstrip('/'). Просмотрев в который раз список всторенных шаблонных фильтров и не обнаружив там нужного, я задумался: как быть? Создавать простой фильтр, типа:

from django.template import Library
from django.template.defaultfilters import stringfilter


register = Library()


@register.filter
@stringfilter
def rstrip(text, chars=None):
    return text.rstrip(chars)

совершенно не хотелось. Ибо вдруг мне в будущем захочется добавить поддержку lstrip или strip метода. Что надо будет морочиться с Ctrl+C, Ctrl+V и минимальными исправлениями отяжеляя эту темплейт библиотеку?

Потому не долго думая и вроде бы не отыскав необходимого мне решения в гугле, я решил повелосипедить и добавить возможность вызова всех строковых методов Python в Django шаблоне.

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

from django.template import Library
from django.template.defaultfilters import stringfilter


register = Library()


for name in dir(u''):
    if name.startswith('_'): continue

    filter = lambda value, *args: getattr(unicode(value), name)(*args)
    filter = stringfilter(filter)

    register.filter(name, filter)

Написав простой тест-кейс я уже было приготовился доставать шампанское и переходить к следующей задаче, но не тут-то было :( TemplateSyntaxError для {{ url|strip:"/" }} заставила притормозить коней. Почему же так произошло? Я пошел к месту ошибки и понял, что проблема в *args, точнее в том, что django.template.FilterExpression.args_check ожидает определенный набор аргументов, а не, наоборот, не определенный заранее.

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

@stringfilter
def make_filter(name):
    def filter(value, first=None, second=None, third=None):
        args = [first, second, third]
        method = getattr(force_unicode(value), name)

        while True:
            try:
                return method(*args)
            except TypeError:
                args.pop(len(args) - 1)

    return filter

и заодно переписал регистрацию этого фильтра. Запустил тест-кейс, получил Ran OK! и на свою голову решил усложнить тест-кейс, проверив работу фильтра с двумя аргументами, например, {{ "abcdef"|replace:"abc":"def" }}.

Каково же было мое удивление, когда Django сказала, нет много аргументов для фильтров - это не хорошо. И сгенерировала очередную TemplateSyntaxError. Что ж, пришлось реализовывать это в виде отдельного простого тега {% stringmethod %}. Принцип его работы простейший, как видно из кода:

@register.simple_tag
def stringmethod(name, value, first=None, second=None, third=None):
    return make_filter(name)(value, first, second, third)

В свою очередь это повлекло за собой обновление теста, и {{ "abcdef"|replace:"abc":"def" }} превратилось в {% stringmethod "replace" "abcdef" "abc" "def" %}. Монструозно, соглашусь, но что поделаешь. В итоге, получив Ran OK! я немного подправил документацию и выложил это все дело, как отдельный гист на гитхаб.

Пользуйтесь, возможно вам это пригодится!

зы. На последок упомяну еще о пару особенностях stringmethods. Во-первых, она не переписывает встроенный join фильтр, во-вторых, она плохо справляется с format.

зыы. Если вы знаете reusable apps решающие похожие проблемы - не стесняйтесь писать в комменты :)

Метки

.net .NET C# .sort 1.2 2009 2010 404 error admin ajax amazon analytics and apache api archlinux asp.net async asynchronous autocomplete bash blender blog blogengine blogs book bootstrap bot bpython buildout byteflow bzr C c plus plus C++ cache cbv Chaco checkio chrome ci ckeditor class based views clojure closure cms cms с удобной админкой code coding style collectd COM comet competition conference ConfigParser contest Context continuous integration CouchDB coverage CppCMS cpyext cpython crud csrf CSS ctypes curl custom model fields cx_freeze cython database db dbm dbqueries debian debug debugging decorator decorators deploy deployment descriptor design dev devconf developers development diveintopython Django django 1.2 django 1.3 django advent django framework django template django trunk django weblog django-admin-tools django-cms django-compressor django-hosts django-piston django-registration django-sphinx django.admin djangoadvent djangocms djangodash doc documentation drupal e-legion eclipse EGit emacs encoding Enthought epoll erlang event exception ExtJS fabric facebook fastcgi finaloption fixtures fonts forms formset fp framework freebsd freeswitch fs2web ftp fun funcparserlib functional gae gamin gandi generic views gettext gevent gil git github gitosis Google Google App Engine google picasa Google Translate google wave Google Web Toolkit grab grablab greenlet gtd gui haskell hg hgshelve highlighter host hosting how-to howto html html5lib Hudson humor i18n icfpc ide idiomatic image-scripting improvements Internet interpreter ipython ironpython izmenimsya.ru jabber java javascript jenkins jetbrains JIT job jquery json jstree jython kde kiev kiyv kyivpy l10n ldap library libs Life Links linux Linux & Unix LLVM logging logs lxml Mac OS X magic mail markdown Matplotlib Mayavi maybe mediavirus meetup memcache Memcached memory messages metaclass middleware migration mikrotik mkd model models mod_python mod_wsgi mongodb monitoring mptt musicmans.ru musicx mvc my-projects mysql netCDF networkx newforms newforms-admin news nginx Nhibernate nix nose NoSQL numpy oop open source OpenID openoffice opster optimization oracle orm os pagination parsing path patterns pdf PDF-принтер PEP PEP8 performance performance optimization perl personality photo php picture-driven computing PIL pinax pingback pip plasma plone plugin plugins postgresql programming progress bar psycopg2 py2exe pybb pybbm pycamp pycharm pycon pycow pycurl pydev pygtk pylons PyNGL pypy pyqt PyQt4 pyrad pyramid PySide Python Python 2.5 python 2.7 python 3 python c api python speed python-mssql python3 pywinauto Qt Qt4 queue rabbitmq radius raw sql re redis redsolution redsolution cms regexp regular expressions release repoze.bfg RequestContext reusable apps robokassa rss ru ruby ruby-on-rails sample satchmo scalability SciPy scraping screencast search selenium self.error seo server setattr settings setuptools shell sikuli sms snippet socket.io software sorting south sphinx spider sql sqlalchemy sqlite ssh startup step-by-step subdomain subversion svn SyntaxHighlighter system tags tdd tddspry teh drama template templates templatetags test testing thinkpad threading threads tips tips and tricks tools tornadio tornado tornado server tricks tutorial tweepy twisted twitter typography uapycon Ubuntu ucsvlog uml Uncategorized unicode unit test unit testing UnitTest Unladen Swallow upload urllib urls utf-8 uwsgi validation vcs versioning video vim virtualenv Visual Studio vkontakte voip wave web web-devel web-services web-разработка webdev webfaction webkit webpy websockets webtest widget widgets Win API windows Wirbel work wrapper wsgi wxPython wxWidgets wysiwyg xapian xml xmonad xmpp xpath yandex youtube zip zomg zope [cdata[cbv]] [cdata[ci]] [cdata[class based views]] [cdata[continuous integration]] [cdata[django framework]] [cdata[django-sphinx]] [cdata[django]] [cdata[nginx]] [cdata[python]] [cdata[virtualenv]] [cdata[программирование]] автоматизация администрирование администрирование django админка алгоритмы архитектура атрибуты базы данных Без рубрики безопасность библиотеки блоге бот веб-разработка видео Визуализация данных вконтакте Все записи гвидо ван россум граббер графика графы декоратор декораторы дескриптор дескрипторы документация заметки игра жизнь идея интересное киев Клиентам книги конференция личное математика метаклассы модели модули монады морфология мысли невозможное новости о облачные вычисления обо мне Обработка данных оптимизация оптимизация кода Основная лента основы парсинг парсинг сайтов перевод песочница Питон поебень поиск правила кодирования программирование Проектирование производительность работа рабочее размышлизмы Разное разработка разработка приложений разработки регулярные выражения сайт событие события ссылки статьи тестирование тесты Тюмень убунтариум фигня философия формы форум Хабрахабр хакинг хостинг шаблоны шаблоны проектирования эксперимент Эксперименты юмор я пиарюсь Яндекс