-2

In order to reflect user-selected filters in the URL, I need urlpatterns to include an arbitrary number of optional parameters, e.g.:

  • /myview/filter1:option1/filter1:option2/filter2:option1/filter3:option5/

In the view I would do something like:

def myview(request):
    if request.GET.get("filter1"):
        for option in request.GET.get("filter1"):
            # do something with option
    
    if request.GET.get("filter2"):
        for option in request.GET.get("filter2"):
            # do something with option
    # etc.

Each user-selected filter can (potentially, depending on the type of filter) have multiple selected options, and multiple filters can be applied at one time. See MobyGames as an example of exactly what I mean.

If that's not feasible with Django urlpatterns, alternatively the options could be separated by a delimiter, and in the view I could add e.g. request.GET.get("filter1").split(";").

I imagine I need regex to achieve this, but I don't really know where to start. I read through the relevant docs, but didn't find anything that directly addresses this kind of pattern.

Edit: the answer at this question is somewhat helpful, but doesn't fully answer my question.

If I'm understanding correctly, I would need something like:

url(r'^myview/(?Pfilter1:<filter1>[a-zA-Z\/]*)/(?Pfilter2:<filter2>[0-9\/]*)/(?Pfilter3:<filter3>[0-9\/]{4}*)/$', myview)

Where each <filter#> is optional, may be repeated an arbitrary number of times, and may have a different pattern (e.g. some with a-zA-Z, some may include numbers, some may be years - I can figure out the needed patterns later).

6
  • 1
    Why include them in the path, rather than as query parameters? Commented Feb 15 at 20:43
  • @jonrsharpe These would be filters selected by the user. They would be added to URL so that the user can refresh, bookmark, copy-paste, etc. the URL with the selected filters intact. I've added "user-selected" to the question, hope that clarifies it a bit. Commented Feb 15 at 20:46
  • That doesn't answer my question, because query parameters also appear in the URL and can be bookmarked or shared: /myview?filter1=option1&filter1=option2&.... Like this: stackoverflow.com/questions?tab=Active Commented Feb 15 at 20:48
  • @jonrsharpe Oh, I see what you mean. I would simply prefer a clean URL with slashes rather than query parameters, as seen in the example linked in the question. Commented Feb 15 at 20:50
  • If there's a good reason why I should not be doing this at all but instead use query parameters, that would also answer my question, of course. Commented Feb 15 at 20:56

1 Answer 1

0

The answer in Make Django accept url with infinite parameters is perfectly correct and would translate to something like the below for your case:

url(r'^myview/(?P<path>[a-zA-Z\/]*)/$', myview),

def myview(request, path):
    filters = path.split('/')
    filterdict = dict()
    for item in filters:
        try:
            key, val = item.split(':'):
            filterdict[key] = value
        except e as Exceptiion:
            .... # Handle malformed urls here.

What's going on in the above regex is that it will capture everything after "myview/" until the closing'/' in the URL. In regex the '^' character represents the start of the string so the regex says it should start with 'myview/' and '$' represents the end of the string so it means it should end with a '/'. All your filters including the slashes which separate them between the 'myview/' and the final '/' in the url path get captured into the path capturing group and then you split that into your list of filters in the function.

Ofcourse you should handle situations where the url is malformed with proper exception handling and watch out for any trickery manually input urls can do and how you handle those filters.

And as always, please remember url paths and query parameters are user supplied input so be careful with the supplied values and don't use them in unsafe ways where they directly interact without sanity checking with backend data or the host (e.g. dont run os.system() or os.popen() using the filters/values as that can cause some nasty security issues.)

Sign up to request clarification or add additional context in comments.

7 Comments

To handle cases when there are multiple options for the same filter, I guess you would do something like: if key in filterdict: filterdict[key].append(val) else: filterdict[key] = [ val ]
That is correct. Good catch, I didn't look closely enough to see your repitition of filter1 in your example URL path.
Potential problem: /myfilter:value:with:colons/
@bur: what if the name of the filter itself contains a colon. There is a reason why one uses a querystring for this: both the key and the value are percentage encoded, meaning it is exactly clear where the key ends, and the value begins.
I suppose for more generalized applications this would be a potential problem, but for my own purposes, there is no reason a filter would ever need a colon in its name. I've implemented this into my project now and it's working like a charm.
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.