вторник, 15 марта 2011 г.

Простое меню в Django на основе содержимого url.py

Изучая django, я неожиданно столкнулся с проблемой. Я постоянно меняю структуру сайта (url.py) и мне приходиться постоянно вводить в адресной строке браузера новые url. И тут я подумал, что неплохо было добавить к моему тестовому сайту менюшку, которая будет брать список URL из url.py. Первым делом я начал смотреть готовые решения, которых, кстати, не оказалось. Также был вариант использовать БД и sitemap для редактирования ссылок. А что делать, если я не хочу использовать БД? Загуглив еще разок я нашел замечательную функцию get_resolver из django.core.urlresolvers и решил написать собственный генератор списка url.

Задача стояла следющая: автоматически генерировать список URL, которые находятся в url.py
и добавлять эти URL в собственный шаблон в виде меню.


В результате получилась следующая функция


import socket

def get_links():
    links = []
    address = socket.gethostbyname_ex(socket.gethostname())[-1][0] # for IP
    #address = socket.gethostbyname_ex(socket.gethostname())[0] # for name
    resolver = get_resolver(None).reverse_dict.items()
    for i in sorted(resolver, key=lambda resolve: resolve[1]):
        short_url = i[1][0][0][0]
        url_desc = {"url":str(address) + "/" + short_url}
        url_desc["name"] = str(i[0]).split(' ')[1]
        links.append(url_desc)
    return links

Тут необходимо немножко пояснить:
Для того чтобы все ссылки были абсолютными, а не относительными используется функция gethostbyname.
иначе при переходе на ссылку "http://localhost/main_page/" сгенериется динамически меню с ссылкой:
http://localhost/main_page/main_page/
Поэтому я решил использовать абсолютный путь для генерирования меню.
также если в /etc/hosts вы указали, что localhost это mysite.com, то можно писать

address = "mysite.com"

По умолчанию get_links() берет url из urlpatterns, а имя ссылки соответствует названию функции,
что не всегда этично. Для того чтобы указать назавания ссылок можно использовать простой if.
def set_name(url, default_value):
    if url == r'':
        return 'root'
    elif url == r'custom_hello/':
        return 'Custom menu name 2'
    else:
        return default_value

Тогда вместо
url_desc["name"] = str(i[0]).split(' ')[1]

следует писать
url_desc["name"] = set_name(short_url, str(i[0]).split(' ')[1])

Теперь в каждой функции представления мы можем писать

links = get_links()
return render_to_response('template.html',locals())

В шаблоне реализуем меню (при необходимости можно добавить чуть-чуть javascript):

{% for link in links %}
<a href="http://{{ link.url }}">{{ link.name }}</a>
{% if not forloop.last %}|{% endif %}
{% endfor %}

Но, определять в каждом представлении переменную links, это не очень кошерно.
Получается что links = get_links() будет присутствовать в каждом предствлении.
И если мы его забудем добавить, то не сгенерируется меню в шаблоне.

Отсюда еще одна задача.
Добавить links = get_links() в locals().

Для этого пишем еще одну функцию:
def get_locals_with_links(local_dict):
    new_d = {}
    for i in dict(local_dict).copy().items():
        if str(i[0]).find("__") == -1:
            new_d[str(i[0])] = i[1]
    new_d.update({'links':get_links()})            
    return new_d

После этого мы можем использовать ее при генерации шаблона:

from URL_helper import get_locals_with_links

def hello(request):
    hello = "Hello, World!!!"
    return render_to_response('template.html',get_locals_with_links(locals()))


Ремарка: статья называется "простое меню...", потому что в функции get_links() отсутвует парсинг регулярных выражений.
Но этого вполне достаточно для того, чтобы понять, как извлекать значения urlpatterns из ulr.py.

Исходники проеакта на django.

Комментариев нет:

Отправить комментарий