Django - 可选 URL 参数的正则表达式

7

我是一名有用的助手,可以为您翻译文本。

我在 Django 中有一个视图,可以接受许多不同的过滤参数,但它们都是可选的。如果我有 6 个可选过滤器,我是否真的需要为这 6 个过滤器的每种组合编写 URL,还是有一种方法可以定义 URL 的哪些部分是可选的?

举个例子,对于只有 2 个过滤器的情况,我可以有以下所有可能的 URL:

/<city>/<state>/
/<city>/<state>/radius/<miles>/
/<city>/<state>/company/<company-name>/
/<city>/<state>/radius/<miles>/company/<company-name>/
/<city>/<state>/company/<company-name>/radius/<miles>/

所有这些url指向相同的视图,唯一需要的参数是城市和州。如果有6个过滤器,这将变得难以管理。

如何最好地实现我想要的目标?

4个回答

9
一种方法是将正则表达式读取所有给定的过滤器作为单个字符串,然后在视图中将它们拆分成单独的值。我想出了以下URL:
(r'^(?P<city>[^/]+)/(?P<state>[^/]+)(?P<filters>(?:/[^/]+/[^/]+)*)/?$',
 'views.my_view'),

匹配所需的城市和州很容易。但是filters部分稍微有些复杂。内部部分-(?:/[^/]+/[^/]+)* -匹配以/name/value形式给出的过滤器。然而,*量词(像所有Python正则表达式量词一样)只返回找到的最后一个匹配项-因此,如果url是/radius/80/company/mycompany/,则只会存储company/mycompany。相反,我们告诉它不捕获单个值(在开头的?:),并将其放在一个捕获块中,该块将所有过滤器值存储为单个字符串。
视图逻辑非常简单。请注意,正则表达式仅匹配过滤器对-因此/company/mycompany/radius/不会匹配。这意味着我们可以安全地假设我们有一对值。我测试过的视图如下:
def my_view(request, city, state, filters):
    # Split into a list ['name', 'value', 'name', 'value']. Note we remove the
    # first character of the string as it will be a slash.
    split = filters[1:].split('/')

    # Map into a dictionary {'name': 'value', 'name': 'value'}.
    filters = dict(zip(split[::2], split[1::2]))

    # Get the values you want - the second parameter is the default if none was
    # given in the URL. Note all entries in the dictionary are strings at this
    # point, so you will have to convert to the appropriate types if desired.
    radius = filters.get('radius', None)
    company = filters.get('company', None)

    # Then use the values as desired in your view.
    context = {
        'city': city,
        'state': state,
        'radius': radius,
        'company': company,
    }
    return render_to_response('my_view.html', context)

需要注意的两个问题。首先,它允许未知的过滤器条目进入您的视图。例如,/fakefilter/somevalue 是有效的。上面的视图代码忽略了这些内容,但您可能希望向用户报告错误。如果是这样,请更改获取值的代码为:

radius = filters.pop('radius', None)
company = filters.pop('company', None)

任何留存在 filters 字典中的条目都是未知值,您可以对其提出抱怨。
其次,如果用户重复使用筛选器,则将使用最后一个值。例如,/radius/80/radius/50 将半径设置为 50。如果您想检测到这一点,您需要在列表转换为字典之前扫描值列表:
given = set()
for name in split[::2]:
    if name in given:
        # Repeated entry, complain to user or something.
    else:
        given.add(name)

感谢您的帮助和出色的答案!唯一看起来不太完美的地方是,返回的所有值都在末尾带有冒号“:”。 - imns
哦,我在这里测试时没有注意到这个问题。你能给一个出现这种情况的URL示例吗? - Blair

8

这绝对是使用GET参数的情况。你的urlconf应该只是/city/state/,然后各种过滤器作为GET变量附加在末尾:

/city/state/?radius=5&company=google

现在,在您的视图中,您接受citystate作为普通参数,但其他所有内容都存储在request.GET QueryDict中。


2
你可以创建一个仅检查路径开头的URL(应该是相同的),指向你的视图,然后在你的视图中解析request.path。另一方面,如果你真的有很多可选的过滤参数以各种组合的形式,最好的解决方案通常是通过GET参数进行过滤,特别是如果用于过滤的URL不需要针对任何搜索引擎进行优化...

-1

在你的urls.py中尝试使用类似这样的东西:

url(r'^(?P<city>[^/]+)/(?P<state>[^/]+)/(radius/(?P<miles>[^/]+)/|company/(?P<company_name>[^/]+)/)*$', 'view')

Django能否反向解析该URL?在bababa的情况下可能并不相关... - dcrosta
是的,我知道我可能无法在这个视图中使用反向功能,但对于我的情况来说没关系。 - imns

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接