django - 可选 url 参数的正则表达式

2023-12-21

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

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


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

我想出了以下网址:

(r'^(?P<city>[^/]+)/(?P<state>[^/]+)(?P<filters>(?:/[^/]+/[^/]+)*)/?$',
 'views.my_view'),

匹配所需的城市和州很容易。这filters部分有点复杂。内部部分——(?:/[^/]+/[^/]+)*- 匹配表格中给出的过滤器/name/value。但是,那*量词(像所有 Python 正则表达式量词一样)仅返回找到的最后一个匹配项 - 所以如果 url 是/radius/80/company/mycompany/ only 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)
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

django - 可选 url 参数的正则表达式 的相关文章

随机推荐