一种方法是让正则表达式将所有给定的过滤器作为单个字符串读取,然后将它们拆分为视图中的单个值。
我想出了以下网址:
(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)