Django: Расширяем тег url
Django 2008-03-26 Dovbush PavelОдним из первых встретившихся мне недоразумений в Djnogo стало неудобство ссылок на другие страницы.
Есть тег url, который принемает view и параметры. Если у отображения нет модели, то это то, что надо. Но использовать его для объекта имеющего свой view по-моему не-DRY. Пусть мы немного перемешаем MVC, но объект будет занать свой view-по-умолчанию.
Мысль прописывать полный путь вручную используя операцию форматирования (%) мне показалась неприемлимой. После непродолжительных поисков была найдена функция reverse.
Первым делом, я насоздавал у всех моделей методов url которые возвращают полный адрес до отображения объекта, используя функцию reverse. Ну и использовалось в шаблонах это ввиде {{ object.url }}. Вроде ничего.
Потом обнаружил @permalink и get_absolute_url. (Вот что значит не прочитать сначала всю документацию.) Уже лучше - reverse убран в декоратор. Но теперь придется писать {{ object.get_absolute_url }}. Длииинно... Да и получается, что в шаблонах появляется два типа урлов - один на модельные отображения, другой на безмодельные.
Для унификации этих двух типов я перекрыл стандартный тег url. Смысл работы такой: если ему передан однин параметр, и этот параметр разрешается в контексте в объект, класс которого наследован от django.db.models.Model и у него есть атрибут get_absolute_url, то тег возвращает результат его вызова. Иначе вызывается оригинальный обработчик тега url.
Вот что получилось:
from django.template.defaulttags import url as url_orig @register.tag def url(parser, token): bits=token.contents.split(' ') if len(bits)==2: filter_obj=parser.compile_filter(bits[1]) return URLNode(filter_obj, url_orig(parser, token)) return url_orig(parser, token) class URLNode(template.Node): def __init__(self, filter_obj, origURLNode): self.filter_obj=filter_obj self.origURLNode=origURLNode def render(self, context): obj=self.filter_obj.resolve(context) from django.db import models if isinstance(obj,django.dbmodels.Model) and hasattr(obj,'get_absolute_url'): return obj.get_absolute_url() return self.origURLNode.render(context)
Использование:
<a href="{% url article %}">одна из статей</a> и <a href="{% url main_page_view %}">Главная</a>
Такой подход обеспечит большую универсальность. Например в меню:
# settings.py SITE_MENU=( (Page.objects.get(slug='main'), u'Главная', ), ('some_index_page_view', u'Какой-то индекс', ), (Page.objects.get(slug='about'), u'About', ), # ... )
<!-- menu.htm --> {% for item,title in menu_items %} <a href="{% url item %}">{{ title }}</a> {% endfor %}
Тег url используется как для объектов так и для отображений. Пример может и неочень (у меня title берется из другого места), просто идея.
По-моему намного удобнее.
Или я не заметил слона в зоопарке Django?