一种方法是将正则表达式读取所有给定的过滤器作为单个字符串,然后在视图中将它们拆分成单独的值。我想出了以下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 = filters[1:].split('/')
filters = dict(zip(split[::2], split[1::2]))
radius = filters.get('radius', None)
company = filters.get('company', None)
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:
else:
given.add(name)