【问题标题】:How do I construct a Django reverse/url using query args?如何使用查询参数构造 Django 反向/url?
【发布时间】:2010-05-06 03:31:05
【问题描述】:

我有像 http://example.com/depict?smiles=CO&width=200&height=200 这样的 URL(以及其他几个可选参数)

我的 urls.py 包含:

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

我可以转到该 URL 并获取构建的 200x200 PNG,所以我知道这部分有效。

在来自“cansmi.cansmi”响应的模板中,我想在给定一些查询参数的情况下为命名模板“cyclops-depict”构建一个 URL。我以为我能做到

{% url cyclops-depict smiles=input_smiles width=200 height=200 %}

其中“input_smiles”是通过表单提交对模板的输入。在这种情况下,它是字符串“CO”,我认为它会创建一个类似于顶部的 URL。

此模板因 TemplateSyntaxError 失败:

渲染时遇到异常:使用参数 '()' 和关键字参数 '{'smiles': u'CO', 'height': 200, 'width': 200}' not找到了。

这是 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.

【问题讨论】:

标签: django url query-string reverse


【解决方案1】:

按照某些答案的建议,通过字符串连接构建带有查询字符串的 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

请注意空格和感叹号等特殊字符的正确编码!

【讨论】:

  • 同意,比普通串联更好。
  • 我想从模板内部生成 URL。如果我理解正确,虽然这有助于制作 URL,但它仍然需要模板标签挂钩。
  • @Andrew Dalke 你是对的,你仍然需要使用基于 urllib.urlencode 的实现来实现自定义标签
  • 现在是六年后了。在 Django 的reverse 中仍然没有像样的方法来合并查询参数吗?
  • 一般来说我同意。但是在固定参数的情况下,字符串连接就可以了。
【解决方案2】:

您的正则表达式没有占位符(这就是您获得 NoReverseMatch 的原因):

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

你可以这样做:

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

URLconf search does not include GET or POST parameters

或者如果你想使用 {% 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

【讨论】:

  • 这很不雅,这就是为什么我认为必须有更好的解决方案。一些参数来自表单输入。实际的模板表达式是 {% url cyclops-depict %}?smiles={{input_smiles}}&width={{size}}&height={{size}} 。这比无效/假设的 {% query-url cyclops-depict smiles=input_smiles width=size height=size %} 更难理解和解释。在 URL 中包含模式当然是可能的,但是 8 个参数中的 7 个是可选的,并且没有自然排序,这使得它相当强制。 (Grrr。Django 应该是为完美主义者准备的。)
  • 如果您想封装构建 URL 的逻辑,您可以编写自己的 [自定义模板标签][1]。让它接受 entry 之类的参数,甚至是完整的上下文并返回构建的 URL。这样你甚至可以模拟url 标签并拥有你喜欢的语法。[1]:docs.djangoproject.com/en/dev/howto/custom-template-tags/…
  • 我要回到这个项目。一件事是我正在向非软件开发人员教授这个(他们是进行一些编程的计算化学家),我不想解释这一切。我得再考虑一下。感谢您的跟进!
【解决方案3】:

我推荐使用内置 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'

要在模板中使用它,您需要创建自定义模板标签

【讨论】:

    【解决方案4】:

    原始答案均未解决视图代码中解决 URL 的相关问题。对于未来的搜索者,如果您尝试这样做,请使用 kwargs,例如:

    reverse('myviewname', kwargs={'pk': value})
    

    【讨论】:

    • reverse with kwargs 仅适用于路径参数,不适用于查询参数。
    • 很好的澄清@Pace 我不知道...谢谢!
    【解决方案5】:

    使用 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)
    

    【讨论】:

      【解决方案6】:

      以前答案的工作变化和我处理这些东西的经验。

      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'
      

      【讨论】:

        猜你喜欢
        • 2021-05-29
        • 2018-02-11
        • 2011-06-10
        • 2012-12-05
        • 2019-03-22
        • 1970-01-01
        • 2015-05-07
        • 1970-01-01
        • 2021-01-16
        相关资源
        最近更新 更多