【问题标题】:django - regex for optional url parametersdjango - 可选 url 参数的正则表达式
【发布时间】:2011-07-20 22:04:46
【问题描述】:

我在 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 个过滤器,这变得难以管理。

实现我想要实现的目标的最佳方式是什么?

【问题讨论】:

    标签: python django url-pattern urlconf


    【解决方案1】:

    一种方法是让正则表达式将所有给定的过滤器作为单个字符串读取,然后在视图中将它们拆分为单独的值。

    我想出了以下网址:

    (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)
    

    【讨论】:

    • 感谢您的帮助和很好的回答!唯一似乎不能 100% 工作的是,返回的值都有一个冒号“:”在它们的末尾。
    • 嗯,我在这里测试时没有注意到。你能举一个这样的 URL 例子吗?
    【解决方案2】:

    这绝对是 GET 参数的用例。您的 urlconf 应该只是 /city/state/,然后各种过滤器最后作为 GET 变量:

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

    现在,在您看来,您接受 citystate 作为正常参数,但其他所有内容都存储在 request.GET QueryDict 中。

    【讨论】:

      【解决方案3】:

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

      【讨论】:

        【解决方案4】:

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

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

        【讨论】:

        • Django 是否能够反转该 URL?这可能与 bababa 的情况无关......
        • 是的,我知道我可能无法在此视图中使用反向,但我的情况没问题。
        猜你喜欢
        • 2019-01-15
        • 2016-04-07
        • 1970-01-01
        • 2015-05-08
        • 2012-02-21
        • 2011-01-20
        • 2012-06-15
        • 2011-08-06
        • 2011-03-22
        相关资源
        最近更新 更多