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



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

В результате получилась следующая функция 
  1.  
  2. import socket
  3.  
  4. def get_links():
  5.     links = []
  6.    
  7.     address = socket.gethostbyname_ex(socket.gethostname())[-1][0] # for IP
  8.     #address = socket.gethostbyname_ex(socket.gethostname())[0] # for name
  9.    
  10.     resolver = get_resolver(None).reverse_dict.items()
  11.  
  12.     for i in sorted(resolver, key=lambda resolve: resolve[1]):
  13.         short_url = i[1][0][0][0]
  14.         url_desc = {"url":str(address) + "/" + short_url}
  15.         url_desc["name"] = str(i[0]).split(' ')[1]
  16.         links.append(url_desc)
  17.     return links
  18.  


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

По умолчанию get_links () берет url из urlpatterns, а имя ссылки соответствует названию функции,
что не всегда этично. Для того чтобы указать назавания ссылок можно использовать простой if.

  1.  
  2. def set_name(url, default_value):
  3.     if url == r'':
  4.         return 'root'
  5.     elif url == r'custom_hello/':
  6.         return 'Custom menu name 2'
  7.     else:
  8.         return default_value
  9.  

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

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


Теперь в каждой функции представления мы можем писать
  1.  
  2. links = get_links()
  3. return render_to_response('template.html',locals())
  4.  


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

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


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

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

Для этого пишем еще одну функцию:

  1.  
  2. def get_locals_with_links(local_dict):
  3.     new_d = {}
  4.     for i in dict(local_dict).copy().items():
  5.         if str(i[0]).find("__") == -1:
  6.             new_d[str(i[0])] = i[1]
  7.     new_d.update({'links':get_links()})            
  8.     return new_d
  9.  


После этого мы можем использовать ее при генерации шаблона:
  1.  
  2. from URL_helper import get_locals_with_links
  3.  
  4. def hello(request):
  5.     hello = "Hello, World!!!"
  6.     return render_to_response('template.html',get_locals_with_links(locals()))
  7.  


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

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