One way is for the regular expression to read all the specified filters as a single line, and then split them into separate values ββin the view.
I came up with the following URL:
(r'^(?P<city>[^/]+)/(?P<state>[^/]+)(?P<filters>(?:/[^/]+/[^/]+)*)/?$', 'views.my_view'),
Matching the required city and condition is easy. The filters part is a bit more complicated. The inner part - (?:/[^/]+/[^/]+)* - corresponds to the filters specified in the form /name/value . However, the * quantifier (like all Python regular expression quantifiers) returns only the last match found - therefore, if the URL was /radius/80/company/mycompany/ , only company/mycompany will be saved. Instead, we say that it does not capture individual values ββ( ?: At the beginning) and place them in a capture block, which will store all filter values ββas a single line.
The presentation logic is pretty simple. Note that the regex will match filter pairs, so /company/mycompany/radius/ will not be matched. This means that we can safely assume that we have pairs of values. The view in which I tested this is as follows:
def my_view(request, city, state, filters):
Two things to note about this. Firstly, it allows you to view unknown filter entries. For example, /fakefilter/somevalue valid. The view code above ignores them, but you probably want to report an error to the user. If so, change the code to get the values
radius = filters.pop('radius', None) company = filters.pop('company', None)
Any entries left in the filters dictionary are unknown values ββthat you may complain about.
Secondly, if the user repeats the filter, the last value will be used. For example, /radius/80/radius/50 sets the radius to 50. If you want to detect this, you will need to scan the list of values ββbefore converting it to a dictionary:
given = set() for name in split[::2]: if name in given: