2014-09-28

Add a button to export the search results in Django

I have a simple search page with a form at the top for the user to enter the search criteria (e.g. last name, first name, age, etc), and the search results are shown in the bottom half of the page.
What I want to do is to add a button to the form to export the search results as a CSV file. At first, I tried to have the search and export buttons post to two different URLs, but of course that did not work because an HTML form can only post to one and one only one URL.
Then I tried to have both the search button and the export button post to the same URL, and have the Django view handling the difference based on which button the user has clicked to submit the form, e.g. if the search button is clicked, then routes to the search view, else if the export button is clicked, then routes to the export view. It worked but there is a problem.
Since the user can change the search criteria before clicking the export button, and since it is a POST, the export view will execute the search based on the newly entered search criteria and export the new search results, which will be different from what the user sees in the bottom half of the search page. This is counter-intuitive and unacceptable to me.
My third attempt is to have the search and export button post to two different URLs. When a user clicks the search button, the search view will execute the search and save the search criteria (e.g. last name, first name, age, etc) in a Django session variable. If the user then clicks the export button, the export view will grab the search criteria from the session variable, execute the search, and export the results. It works, but it is not very portable because whenever I change the search form, I will need to change the views.
My next approach is to save the Django QuerySet in a session variable instead of the search criteria. It didn't work because a QuerySet is not JSON serializable. I cannot get Django to save the QuerySet in a session variable.
My final approach is to save the QuerySet query (rather than the search criteria) in a session variable. Then it works perfectly. I don't have to modify the views when the form changes. The export view grabs the QuerySet query from the session and then execute it as a raw SQL query. The performance hit of saving the QuerySet query is negligible, although the session variable itself will be wasted if the user eventually does not click the export button. But that is really just a small price to pay.
=== search view sample code ===

last_name = form.cleaned_data['last_name'] or None
first_name = form.cleaned_data['first_name'] or None
age = form.cleaned_data['age'] or None

if last_name: kw['last_name__icontains'] = last_name
if first_name: kw['first_name__icontains'] = first_name
if age: kw['age__exact'] = age

object_list = Person.objects.filter(**kw) # for the search
request.session['search_sql'] = str(Person.objects.filter(**kw).query) # for the export

=== export view sample code ===

sql = request.session.get('search_sql')
object_list = Person.objects.raw(sql)

No comments: