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

imn*_*mns 7 python django url-pattern urlconf

我在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>/
Run Code Online (Sandbox Code Playgroud)

所有这些网址都指向相同的视图,唯一需要的参数是城市和州.使用6个过滤器,这变得难以管理.

做我想做的最好的方法是什么?

Bla*_*air 9

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

我想出了以下网址:

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

匹配所需的城市和州很容易.这filters部分有点复杂.内部部分 - (?:/[^/]+/[^/]+)*匹配表单中给出的过滤器/name/value.但是,*量词(就像所有Python正则表达式量词)只返回找到的最后一个匹配 - 所以如果/radius/80/company/mycompany/company/mycompany存储了url .相反,我们告诉它不要捕获单个值(?:在开始时),并将其放在捕获块内,该块将所有过滤器值存储为单个字符串.

视图逻辑非常简单.请注意,正则表达式只匹配成对的过滤器 - 因此/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)
Run Code Online (Sandbox Code Playgroud)

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

radius = filters.pop('radius', None)
company = filters.pop('company', None)
Run Code Online (Sandbox Code Playgroud)

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)
Run Code Online (Sandbox Code Playgroud)


Dan*_*man 8

这绝对是GET参数的用例.你的urlconf应该是/city/state/,然后各种过滤器作为GET变量结束:

/city/state/?radius=5&company=google
Run Code Online (Sandbox Code Playgroud)

现在,在您的视图中,您接受citystate作为正常参数,但其他所有内容都存储在request.GETQueryDict中.