Одним из методов было бы заставить регулярное выражение читать все заданные фильтры как одну строку, а затем разбивать их на отдельные значения в представлении.
Я придумал следующий 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 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)