Обращение имен с именами в Django: несколько экземпляров одного и того же приложения
Я работал с Django некоторое время (сейчас на версии 1.2), но совсем недавно начал работать над приложением, которое должно поддерживать несколько экземпляров. Например, файл проекта urls.py
будет включать его дважды в двух разных пространствах имен, например:
urlpatterns = patterns('',
(r'^instance1/', include('myapp.urls', namespace='instance1')),
(r'^instance2/', include('myapp.urls', namespace='instance2')),
)
Я хорошо разбирался, пока не понял, что мне нужно выяснить, что делать со всеми внутренними вызовами reverse()
(или вызовы шаблонов для фильтра {% url %}
). Например, скажем, я делаю что-то вроде следующего в одном из моих представлений:
return HttpResponseRedirect(reverse('view_name'))
или что-то вроде этого в одном из моих шаблонов:
<a href="{% url view_name %}">link text</a>
... где view_name
- это имя шаблона URL, содержащегося в myapp.urls
. Поскольку я использую пространства имен, это вызовет ошибку: нет представления под названием view_name
. Скорее, я должен сказать это либо instance1:view_name
, либо instance2:view_name
. Но делать это динамически меня бросает.
Я немного посмотрел, и похоже, что аргумент current_app
, переданный либо в Context
, либо RequestContext
, был разработан, чтобы помочь с этим, но не совсем ясно, как динамически передавать правильное имя приложения current_app
. Итак, какой правильный способ сказать Django, какое пространство имен использовать?
EDIT: Моим вариантом использования является использование одной установки приложения несколько раз. То есть, он существует только один раз на диске, но включается несколько раз в корень проекта urls.py
(каждый раз под другим пространством имен, как в моем примере выше). Имея это в виду, есть ли хороший способ отслеживать, какое пространство имен вызывается из вида/шаблона, и использовать любое использование reverse()
или {% url %}
в том же пространстве имен? Я знаю, что Django 1.3 предоставит некоторые дополнительные функции, которые могут помочь в этом (а именно новый и улучшенный resolve()
), но, безусловно, там хороший способ сделать это сейчас...
Ответы
Ответ 1
Не очень хорошее решение, но поскольку вы используете тот же текст для своего пространства имен и начальной части пути URL, вы можете извлечь этот элемент из request.path
(request.path.split('/')[1]
) и установить его как current_app
в контекст запроса или просто использовать его как пространство имен в представлениях.
http://docs.djangoproject.com/en/dev/topics/http/urls/#url-namespaces пункт 2.
Вы можете сделать это, например. в обработчике контекста (если вы хотите использовать пространство имен в шаблоне).
Для представлений вы можете написать декоратор, который передает вашу функцию в дополнительное пространство имен kwarg и использует ее как:
@feed_namespace
def view1(request, *args, **kwargs):
ns = kwargs['namespace']
или просто напишите функцию reverse_namespaced с дополнительным параметром (запросом), где функция получает пространство имен и использует его вместо обратного.
Конечно, если вы это сделаете, вам всегда придется использовать путь запроса/пространство имен для этого приложения.
Ответ 2
С момента публикации вопроса многое изменилось, но для будущих googlers (например, я) было бы полезно указать, что request
теперь имеет пространство имен (по крайней мере с 1.7, как показано в этот пример.
Из того, что я понял, мы должны просто передать current_app
позиционный аргумент на reverse
/redirect
, но я не смог заставить его работать, поэтому я создал метод помощи для этой цели:
def __redirect(request, viewname, *args, **kwargs):
to = viewname
if callable(to):
to = viewname.__module__ + '.' + viewname.__name__
if request.resolver_match.namespace:
to = '%s:%s' % (request.resolver_match.namespace, to)
return redirect(
to,
*args,
**kwargs
)
Я использую redirect
здесь, но все аргументы передаются на reverse
, поэтому он тот же.
Ответ 3
Существует страница doc об изменении имен URL-адресов.
http://docs.djangoproject.com/en/dev/topics/http/urls/#topics-http-reversing-url-namespaces
Либо reverse('instance1:myapp.urls.some_view')
, либо reverse('instance1:view_name')
должен работать, или оба:) - я никогда не пробовал это сам.
Ответ 4
Переменная current_app
- это то, что вам нужно, чтобы установить что-то, что вам нравится.
Лично я бы рекомендовал установить его как-то вроде __name__.rsplit('.', 1)[0]
, чтобы вы получили spam
в spam/views.py
.
Но вы можете определить его как угодно, если ваше имя приложения соответствует тому, что вы определяете в файле urls.
Ответ 5
Если вы не слишком привязаны к пространствам имен, вы можете вместо этого написать urls.py, чтобы выглядеть как:
urlpatterns = patterns('',
(r'^(P<instance>/)', 'controllers.route_me'),
)
Это вызовет функцию controllers.route_me и передаст ему запрос, а также строку "instance", с которой вы могли бы справиться следующим образом:
# (in controllers.py)
def route_me(request, instance):
# you now have complete control, and do what you need to do with the 'instance' and 'request' vars
pass