3

I am working on a Django setup where I can receive a url containining a query string as part of a GET. I would like to be able to process the data provided in the query string and return a page that is adjusted for that data but does not contain the query string in the URL.

Ordinarily I would just use reverse(), but I am not sure how to apply it in this case. Here are the details of the situation:

Example URL: .../test/123/?list_options=1&list_options=2&list_options=3

urls.py

urlpatterns = patterns('', 
    url(r'test/(P<testrun_id>\d+)/'), views.testrun, name='testrun')
    )

views.py

def testrun(request, testrun_id):
    if 'list_options' in request.GET.keys():
        lopt = request.GET.getlist('list_options')
        :
        :
        [process lopt list] 
        :
        :

    :
    :
    [other processing]
    :
    :

    context = { ...stuff... }
    return render(request, 'test_tracker/testview.html', context)

When the example URL is processed, Django will return the page I want but with the URL still containing the query string on the end. The standard way of stripping off the unwanted query string would be to return the testrun function with return HttpResponseRedirect(reverse('testrun', args=(testrun_id,))). However, if I do that here then I'm going to get an infinite loop through the testrun function. Furthermore, I am unsure if the list_options data that was on the original request will still be available after the redirect given that it has been removed from the URL.

How should I work around this? I can see that it might make sense to move the parsing of the list_options variable out into a separate function to avoid the infinite recursion, but I'm afraid that it will lose me the list_options data from the request if I do it that way. Is there a neat way of simultaneously lopping the query string off the end of the URL and returning the page I want in one place so I can avoid having separate things out into multiple functions?

EDIT: A little bit of extra background, since there have been a couple of "Why would you want to do this?" queries.

The website I'm designing is to report on the results of various tests of the software I'm working on. This particular page is for reporting on the results of a single test, and often I will link to it from a bigger list of tests.

The list_options array is a way of specifying the other tests in the list I have just come from. This allows me to populate a drop-down menu with other relevant tests to allow me to easily switch between them.

As such, I could easily end up passing in 15-20 different values and creating huge URLs, which I'd like to avoid. The page is designed to have a default set of other tests to fill in the menu in question if I don't suggest any others in the URL, so it's not a big deal if I remove the list_options. If the user wishes to come back to the page directly he won't care about the other tests in the list, so it's not a problem if that information is not available.

10
  • I don't understand. Why are you using list_options 3 times in the same GET request? Commented Aug 8, 2013 at 17:10
  • I would have two separate views. Even though you'll have more than one, it will probably be easier to test and because you will have two views to do separate bits of processing. I think you can work around the loop by having two views, since you're doing less in each by separating concerns. Commented Aug 8, 2013 at 17:39
  • Why do you want to do this anyway? Querystrings are normal and there is no technical reason to strip them out. Commented Aug 8, 2013 at 18:01
  • @dan: I'm passing in an array of values to be processed, hence re-use of the list_options variable. Commented Aug 8, 2013 at 22:12
  • @miki725: I want to do this because in theory there could be as many as 15-20 elements in the array I'm passing in. I don't really want the URLs to be hundreds of characters long. Commented Aug 8, 2013 at 22:16

1 Answer 1

3

First a word of caution. This is probably not a good idea to do for various reasons:

  • Bookmarking. Imagine that .../link?q=bar&order=foo will filter some search results and also sort the results in particular order. If you will automatically strip out the querystring, then you will effectively disallow users to bookmark specific search queries.
  • Tests. Any time you add any automation, things can and will probably go wrong in ways you never imagined. It is always better to stick with simple yet effective approaches since they are widely used thus are less error-prone. Ill give an example for this below.
  • Maintenance. This is not a standard behavior model therefore this will make maintenance harder for future developers since first they will have to understand first what is going on.

If you still want to achieve this, one of the simplest methods is to use sessions. The idea is that when there is a querystring, you save its contents into a session and then you retrieve it later on when there is no querystring. For example:

def testrun(request, testrun_id):
    # save the get data
    if request.META['QUERY_STRING']:
        request.session['testrun_get'] = request.GET
        # the following will not have querystring hence no infinite loop
        return HttpResponseRedirect(reverse('testrun', args=(testrun_id,)))

    # there is no querystring so retreive it from session
    # however someone could visit the url without the querystring
    # without visiting the querystring version first hence
    # you have to test for it
    get_data = request.session.get('testrun_get', None)
    if get_data:
        if 'list_options' in get_data.keys():
            ...
    else:
        # do some default option
        ...

    context = { ...stuff... }
    return render(request, 'test_tracker/testview.html', context)

That should work however it can break rather easily and there is no way to easily fix it. This should illustrate the second bullet from above. For example, imagine a user wants to compare two search queries side-by-side. So he will try to visit .../link?q=bar&order=foo and `.../link?q=cat&order=dog in different tabs of the same browser. So far so good because each page will open correct results however as soon as the user will try to refresh the first opened tab, he will get results from the second tab since that is what is currently stored in the session and because browser will have a single session token for both tabs.

Even if you will find some other method to achieve what you want without using sessions, I imagine that you will encounter similar issues because HTTP is stateless hence you will have to store state on the server.

There is actually a way to do this without breaking much of the functionality - store state on client instead of server-side. So you will have a url without a querystring and then let javascript query some API for whatever you will need to display on that page. That however will force you to make some sort of API and use some javascript which does not exactly fall into the scope of your question. So it is possible to do cleanly however that will involve more than just using Django.

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

1 Comment

Cheers - the session stuff is the sort of thing I was looking for. I think you might have a point about possibly using some javascript instead. Of your three points of warning the third one is probably one I should be thinking about, so I'll weigh up my options before continuing with this - it might be that doing it client-side is more maintainable (although my javascript knowledge is weak, so I'll have to tread carefully). Thanks for your help!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.