Как создать реверс / URL-адрес Django, используя аргументы запроса?

У меня есть такие URL-адреса, как http://example.com/depict?smiles=CO&width=200&height=200 (и с несколькими другими необязательными аргументами)

Мой urls.py содержит:

urlpatterns = patterns('',
    (r'^$', 'cansmi.index'),
    (r'^cansmi$', 'cansmi.cansmi'),
    url(r'^depict$', cyclops.django.depict, name="cyclops-depict"),

Я могу перейти по этому URL-адресу и получить созданный PNG размером 200x200, так что я знаю, что эта часть работает.

В моем шаблоне из ответа «cansmi.cansmi» я хочу создать URL-адрес для именованного шаблона «cyclops-depict» с учетом некоторых параметров запроса. Я думал, что смогу сделать

{% url циклоп-изображение смайлов=input_smiles width=200 height=200 %}

где «input_smiles» — это ввод в шаблон через отправку формы. В данном случае это строка «CO», и я думал, что она создаст URL-адрес, подобный приведенному выше.

Этот шаблон завершается с ошибкой TemplateSyntaxError:

При рендеринге возникло исключение: реверс для 'cyclops-depict' с аргументами '()' и аргументами ключевого слова '{'smiles': u'CO', 'height': 200, 'width': 200}' не найден.

Это довольно распространенное сообщение об ошибке как здесь, в StackOverflow, так и в других местах. В каждом случае, который я обнаружил, люди использовали их с параметрами в регулярном выражении пути URL, что не так, как у меня, когда параметры входят в запрос.

Это значит, что я делаю это неправильно. Как мне сделать это правильно? То есть я хочу построить полный URL-адрес, включая параметры пути и запроса, используя что-то в шаблоне.

Для справки,

% python manage.py shell
Python 2.6.1 (r261:67515, Feb 11 2010, 00:51:29) 
[GCC 4.2.1 (Apple Inc. build 5646)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> from django.core.urlresolvers import reverse
>>> reverse("cyclops-depict", kwargs=dict())
'/depict'
>>> reverse("cyclops-depict", kwargs=dict(smiles="CO"))
Traceback (most recent call last):
  File "<console>", line 1, in <module>
  File "/Library/Python/2.6/site-packages/django/core/urlresolvers.py", line 356, in reverse
    *args, **kwargs)))
  File "/Library/Python/2.6/site-packages/django/core/urlresolvers.py", line 302, in reverse
    "arguments '%s' not found." % (lookup_view_s, args, kwargs))
NoReverseMatch: Reverse for 'cyclops-depict' with arguments '()' and keyword arguments '{'smiles': 'CO'}' not found.

person Andrew Dalke    schedule 06.05.2010    source источник
comment
Я создал запрос функции: code.djangoproject.com/ticket/25582   -  person guettli    schedule 21.10.2015


Ответы (6)


В вашем обычном выражении нет заполнителей (поэтому вы получаете NoReverseMatch):

url(r'^depict$', cyclops.django.depict, name="cyclops-depict"),

Вы можете сделать это следующим образом:

{% url cyclops-depict %}?smiles=CO&width=200&height=200

Поиск URLconf не включает GET или POST параметры

Или, если вы хотите использовать тег {% url %}, вы должны изменить структуру своего шаблона URL на что-то вроде

r'^depict/(?P<width>\d+)/(?P<height>\d+)/(?P<smiles>\w+)$' 

тогда вы могли бы сделать что-то вроде

{% url cyclops-depict 200 200 "CO" %}

Последующие действия:

Простой пример пользовательского тега:

from django.core.urlresolvers import reverse
from django import template
register = template.Library()

@register.tag(name="myurl")
def myurl(parser, token):
    tokens = token.split_contents()
    return MyUrlNode(tokens[1:])

class MyUrlNode(template.Node):
    def __init__(self, tokens):
        self.tokens = tokens
    def render(self, context):
        url = reverse('cyclops-depict')
        qs = '&'.join([t for t in self.tokens])
        return '?'.join((url,qs))

Вы можете использовать этот тег в своих шаблонах следующим образом:

{% myurl width=200 height=200 name=SomeName %}

и, надеюсь, он должен вывести что-то вроде

/depict?width=200&height=200&name=SomeName
person Davor Lucic    schedule 06.05.2010
comment
Это довольно неэлегантно, поэтому я подумал, что должно быть лучшее решение. Некоторые параметры берутся из формы ввода. Фактическим выражением шаблона будет {% url cyclops-depict %}?smiles={{input_smiles}}&width={{size}}&height={{size}} . Это сложнее понять и объяснить, чем неверный/гипотетический {% query-url cyclops-depict smiles=input_smiles width=size height=size %}. Наличие шаблонов в URL-адресе, конечно, возможно, но 7 из 8 параметров являются необязательными, и нет естественного порядка, что делает его скорее принудительным. (Гррр. И Джанго должен быть для перфекционистов.) - person Andrew Dalke; 06.05.2010
comment
Если вы хотите инкапсулировать логику для создания URL-адресов, вы можете просто написать свой собственный [пользовательский тег шаблона][1]. Заставьте его принимать такие параметры, как entry или даже полный контекст, и возвращать построенный URL. Таким образом, вы даже можете эмулировать тег url и иметь синтаксис, который вам нравится.. [1]: docs.djangoproject.com/en/dev/howto/custom-template-tags/ - person Davor Lucic; 06.05.2010
comment
Я возвращаюсь к этому проекту. Во-первых, я учу этому не-разработчиков программного обеспечения (это вычислительные химики, которые занимаются программированием), и я не хочу все это объяснять. Мне придется подумать об этом еще немного. Спасибо за продолжение! - person Andrew Dalke; 13.05.2010

Создание URL-адреса со строкой запроса путем конкатенации строк, как это предлагается в некоторых ответах, является такой же плохой идеей, как создание SQL-запросов путем конкатенации строк. Это сложно, неэлегантно и особенно опасно, когда пользователь предоставляет (ненадежный) ввод. К сожалению, Django не предлагает простой возможности передать параметры запроса в reverse< /а> функция.

Однако стандарт Python urllib предоставляет желаемую функциональность кодирования строки запроса.

В моем приложении я создал вспомогательную функцию:

def url_with_querystring(path, **kwargs):
    return path + '?' + urllib.urlencode(kwargs) # for Python 3, use urllib.parse.urlencode instead

Затем я вызываю его в представлении следующим образом:

quick_add_order_url = url_with_querystring(reverse(order_add),
    responsible=employee.id, scheduled_for=datetime.date.today(),
    subject='hello world!')
# http://localhost/myapp/order/add/?responsible=5&
#     scheduled_for=2011-03-17&subject=hello+world%21

Обратите внимание на правильную кодировку специальных символов, таких как пробел и восклицательный знак!

person geekQ    schedule 17.03.2011
comment
Согласен, лучше, чем простая конкатенация. - person Davor Lucic; 28.03.2011
comment
Я хотел бы сгенерировать URL-адрес внутри шаблона. Если я вас правильно понимаю, хотя это помогает сделать URL, ему все еще нужен крючок тега шаблона. - person Andrew Dalke; 18.04.2011
comment
@Andrew Dalke Вы правы, вам все равно нужно будет реализовать собственный тег с реализацией, основанной на urllib.urlencode. - person geekQ; 20.04.2011
comment
Сейчас мы шесть лет спустя. В Django reverse все еще нет достойного способа включить параметры запроса? - person physicalattraction; 23.01.2017
comment
В общем согласен. Но в случае фиксированных параметров подойдет конкатенация строк. - person x-yuri; 29.06.2018

Я рекомендую использовать встроенный в django QueryDict. Он также правильно обрабатывает списки. End автоматически экранирует некоторые специальные символы (например, =, ?, /, '#'):

from django.http import QueryDict
from django.core.urlresolvers import reverse

q = QueryDict('', mutable=True)
q['some_key'] = 'some_value'
q.setlist('some_list', [1,2,3])
'%s?%s' % (reverse('some_view_name'), q.urlencode())
# '/some_url/?some_list=1&some_list=2&some_list=3&some_key=some_value'

q.appendlist('some_list', 4)
q['value_with_special_chars'] = 'hello=w#rld?'
'%s?%s' % (reverse('some_view_name'), q.urlencode())
# '/some_url/?value_with_special_chars=hello%3Dw%23rld%3F&some_list=1&some_list=2&some_list=3&some_list=4&some_key=some_value'

Чтобы использовать это в шаблонах, вам нужно будет создать собственный тег шаблона.

person imposeren    schedule 24.12.2014

Ни один из исходных ответов не решает связанную с этим проблему, разрешающую URL-адреса в коде представления. Для будущих искателей, если вы пытаетесь это сделать, используйте kwargs, что-то вроде:

reverse('myviewname', kwargs={'pk': value})
person Aitch    schedule 02.01.2013
comment
reverse с kwargs работает только с параметрами пути, а не с параметрами запроса. - person Pace; 30.05.2013
comment
Хорошее разъяснение @Pace Я этого не знал ... Спасибо! - person allexiusw; 05.03.2021

Ответ, который использовал urllib, действительно хорош, однако, пытаясь избежать конкатенации строк, он использовал его в path + '?' + urllib.urlencode(kwargs). Я считаю, что это может создать проблемы, когда path уже имеет некоторые параметры запроса.

Модифицированная функция будет выглядеть так:

def url_with_querystring(url, **kwargs):
    url_parts = list(urlparse.urlparse(url))
    query = dict(urlparse.parse_qsl(url_parts[4]))
    query.update(kwargs)
    url_parts[4] = urllib.urlencode(query)
    return urlparse.urlunparse(url_parts)
person Nour Wolf    schedule 11.11.2015

Рабочий вариант предыдущих ответов и мой опыт работы с этим материалом.

from django.urls import reverse
from django.utils.http import urlencode

def build_url(*args, **kwargs):
    params = kwargs.pop('params', {})
    url = reverse(*args, **kwargs)
    if params:
        url += '?' + urlencode(params)
    return url

Как использовать:

>>> build_url('products-detail', kwargs={'pk': 1}, params={'category_id': 2})
'/api/v1/shop/products/1/?category_id=2'
person michal-michalak    schedule 15.09.2018