Django - regex for optional URL parameters

I have a view in django that can take several different filter parameters, but all of them are optional. If I have 6 additional filters, do I really need to write URLs for each combination of 6, or is there a way to determine which parts of the URLs are optional?

To give you an example with two filters, I could use all of these URL features:

/<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>/ 

All of these URLs point to the same view, and the only required parameters are city and state. With 6 filters, it becomes uncontrollable.

What is the best way to do what I want to achieve?

+7
source share
4 answers

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): # 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) 

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: # Repeated entry, complain to user or something. else: given.add(name) 
+9
source

This is absolutely a precedent for GET parameters. Your urlconf should be /city/state/ , then different filters go at the end like GET variables:

 /city/state/?radius=5&company=google 

Now, in your opinion, you accept city and state as normal parameters, but everything else is stored in request.GET QueryDict.

+8
source

You can also create only one URL (which only checks the beginning of the path, which should be the same) that points to your view, and then request.path in your view. On the other hand, if you really have a lot of additional filter parameters in various combinations, the best solution is to do filtering through GET parameters very often, especially if the URLs used for filtering do not need optimization for any search engine ...

+2
source

Try using something like this in urls.py:

 url(r'^(?P<city>[^/]+)/(?P<state>[^/]+)/(radius/(?P<miles>[^/]+)/|company/(?P<company_name>[^/]+)/)*$', 'view') 
-one
source

All Articles