0

I am having trouble exporting the results of a Django query to csv using a subclass of my query class as suggested here. https://stackoverflow.com/a/29678525/3973597

I end up with a Page not found (404) error. Here is the relevant code...

views.py

class QueryResultsView(ListView):

    template_name = 'query/query_results.html'
    model = StDetail
    context_object_name = "object_list"

    def get_queryset(self):
        form_input = self.request.GET
        filters = {"person_name": form_input.get('name'),
                   "city": form_input.get('city'),
                  }
        # delete keys that are None
        filters = {k: v for k, v in filters.items() if v is not ''}
        self.detail_data = get_list_or_404(self.model, **filters)
        return(self.detail_data)

    def get_context_data(self, **kwargs):
        context = super(QueryResultsView, self).get_context_data(**kwargs)
        context['query_results'] = self.get_queryset()
        return(context)

class QueryResultsCsvView(QueryResultsView):

    # Subclass of above view, to produce a csv file
    template_name = 'query/QueryResults.csv'
    content_type = 'text/csv'

urls.py

app_name = QueryConfig.name
urlpatterns = [
...
    url(r'^query',
        QueryFormView.as_view(),
        name='person-query'),
    url(r'^results',
        QueryResultsView.as_view(),
        name='query-results'),
    url(r'^results/csv/$',
        QueryResultsCsvView.as_view(),
        name='query-results-csv'),
]

query_results.html

...
<a href="{% url 'query:query-results-csv' %}">Download Results</a>
...

QueryResults.csv

Date, City, Name, Sex
{% for object in object_list %}
{{object.date}},{{object.city}},{{object.name}},{{object.sex}}
{% endfor %}

The query works without any problems. However, when I click on the Download Results link I get a Page not found (404) error. Can somebody tell me what I'm missing?

user14241
  • 727
  • 1
  • 8
  • 27

1 Answers1

1

Your first problem is that r'^results'matches results/csv/ as well as results because it doesn't use a $ to match the end of the URL.

Try something like the following (I've added trailing slashes to match the usual Django style).

urlpatterns = [
    url(r'^query/$',
        QueryFormView.as_view(),
        name='person-query'),
    url(r'^results/$',
        QueryResultsView.as_view(),
        name='query-results'),
    url(r'^results/csv/$',
        QueryResultsCsvView.as_view(),
        name='query-results-csv'),
]

Once you've done that, you're next problem is that you are simply linking to the query-results-csv results view, but you are not passing any form data to it. In the template for the query-results view, you could add the same querystring to the link by changing it to:

<a href="{% url 'query:query-results-csv' %}?{{ request.GET.urlencode }}">Download Results</a>

Finally, note that form_input.get(key) returns None if the key does not exist, therefore your filter should use if v instead of if v is not '' (if you really only wanted to exclude values with the empty string, you should have used if v != '').

filters = {k: v for k, v in filters.items() if v}
Alasdair
  • 298,606
  • 55
  • 578
  • 516
  • Thanks for the help. So how would I go about passing the same form data to this view as I did with the query results? – user14241 Dec 07 '18 at 21:00
  • 1
    You can submit a form, the same way you are doing for the query results, or you can include the querystring in the link. – Alasdair Dec 08 '18 at 00:48
  • I guess part of the problem I'm having is that I want to be able to leave fields in the query blank. Is there any way for me to pass the kwargs of the query string through the template? My form objects are set to required=False. – user14241 Dec 08 '18 at 15:29
  • 1
    I’m not sure what you mean by *pass kwargs of the query string through the template*. You could include the current querystring in the link by changing the it to `Download Results` – Alasdair Dec 08 '18 at 16:31
  • Thanks! This was extremely helpful! This gives me the functionality I need to generate the .csv downloads. My url patterns worked as I had them originally, but being able to send the query string to my subclass was the trick. You may edit your answer and I will accept it. – user14241 Dec 08 '18 at 18:05
  • Your original URL patterns shouldn't work. As I explained, `results/csv/` will be matched by the regex `r'results/'`, so the request will be handled by `QueryResultsView.as_view()` instead of `QueryResultsCsvView`. – Alasdair Dec 08 '18 at 19:09
  • Sorry, you are correct. I changed my last entry to r'^csv' – user14241 Dec 08 '18 at 20:01
  • Changing to `r'^csv'` will work, but it's fragile because you are ignoring my advice to add `$` to the end of the regexes. – Alasdair Dec 08 '18 at 21:30
  • Yes, sorry. Appending '/$' broke my links for some reason. I am able to append '$' however. – user14241 Dec 08 '18 at 21:55
  • Your suggestion about filter.items I believe misunderstands the point of that code. Adding not breaks the intended functionality. I added this to allow form entries to remain blank. In order to remove keys that are '', my original code works. I got the idea from https://stackoverflow.com/questions/33797126/proper-way-to-remove-keys-in-dictionary-with-none-values-in-python – user14241 Dec 08 '18 at 22:16
  • Thanks for fixing the typo. I will make the suggested change. To be clear, I did not ignore your advice. I tried your suggestion and it broke my site's functionality. – user14241 Dec 09 '18 at 17:16
  • I'm actually after `if v` as opposed to `if not v`. – user14241 Dec 09 '18 at 17:38