3

Hello experts! Could you please help. I have few views that return different querysets. my goal is to export current queryset to csv when user click button in browser.

Could you please recommend what is the best algorithm for doing this ? I already did all apart save query to csv only when button is clicked in browser.

Thank you in advance!

Iana
  • 458
  • 3
  • 7
  • 18
  • 3
    The best algorithm, is always comparative. Could you let us know what you have tried, so that we can then suggest you, that can be improved upon? – Nagaraj Tantri Apr 16 '15 at 10:52
  • You probably first need a form that would get the query sets as inputs and then do the csv business inside the view. Once the file created you could make it available to the user by displaying a url to the csv file – Thomas Gak-Deluen Apr 16 '15 at 12:01

4 Answers4

9

in your view

import csv
.....
output = []
response = HttpResponse (content_type='text/csv')
writer = csv.writer(response)
query_set = User.objects.select_related('profile').all()
#Header
writer.writerow(['First Name', 'Last Name', 'Full Name', 'Short Name'])
for user in query_set:
    output.append([user.first_name, user.last_name, user.get_full_name, user.profile.short_name])
#CSV Data
writer.writerows(output)
return response

This works prefectly

Cipher
  • 2,060
  • 3
  • 30
  • 58
Rizwan Mumtaz
  • 3,875
  • 2
  • 30
  • 31
4

If you are using classed based views, its really easy (this is the sort of task where they shine). Subclass the main view, override the template and content type.

Here is an example from one of my projects

views.py

class SavedSamplesView(ListView):
    """
    This is the standard view returning HTML
    """
    template_name = "SavedSamples.html"
    model = Sample 
    context_object_name = "sample_list"

    def get_queryset(self) :
        slug =  self.kwargs['submission_slug']
        return Sample.objects.filter(submission__submission_slug=slug)

class SavedSamplesCsvView(SavedSamplesView):
    """
    Subclass of above view, to produce a csv file
    """
    template_name = 'SavedSamples.csv'
    content_type = 'text/csv'

The template SavedSamples.cvs looks like this (the formatting to get the newline is a little ugly, but it works). The first line is the headers, remove that if you don't need it:

sample.id , sample.name , ... , comments
{% for sample in sample_list %}{{ sample.id }},{{ sample.name }},....,{{sample.comments}}
{% endfor %}

urls.py

url(r'^savedsamplescsv/(?P<submission_slug>[ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890\-_]+)/', views.SavedSamplesCsvView.as_view(),  name='saved_samples_csv'),

I would use a link instead of a button, and style it as button.

wobbily_col
  • 11,390
  • 12
  • 62
  • 86
  • In the `urls.py` you can also use a shorter version of the regex: `url(r'^savedsamplescsv/(?P[A-Za-z0-9\-_]+)/$', views.SavedSamplesCsvView.as_view(), name='saved_samples_csv'),` (and yes, I added the `$` character at the end of the regex) – help_asap Apr 16 '15 at 15:47
  • True. I think there may have been a reason I did it that way, but can't remember now. – wobbily_col Apr 16 '15 at 15:47
  • To be more pythonic: _"Explicit is better than implicit."_ Most of the times :P – help_asap Apr 16 '15 at 15:50
  • Actually I prefer your version which is more implicit in my opinion (must be my Perl background). But the current one has been working in production for a while, so no reason to potentially introduce problems. – wobbily_col Apr 16 '15 at 15:53
3

I use the following snippet to generate a CSV for my queryset. It also includes related fields, that you can choose to ignore or use something else to serialize the data. ( Such as the default serializers that come with Django )

import csv


def dump_to_csv(model, qs):
    """
    Takes in a Django queryset and spits out a CSV file.
    """

    model = qs.model
    writer = ''

    headers = []

    # Get standard fields
    for field in model._meta.get_fields():
        headers.append(field) if 'extra' not in field.name else None

    writer += ','.join([field.name for field in headers])
    writer += '\n'

    for obj in qs:
        row = []
        for field in headers:
            # Append all general fields
            if field.get_internal_type() not in ['ForeignKey', 'ManyToManyField', 'OneToOneField']:
                val = getattr(obj, field.name)
                if callable(val):
                    val = val()
                if type(val) == str:
                    val = val.encode("utf-8")
                row.append(str(val))

            # Append all fk fields
            elif field.get_internal_type() in ['ForeignKey', 'OneToOneField']:
                from django.core import serializers
                import json

                value = field.value_from_object(obj)

                if value not in [None, ""]:
                    qs = field.remote_field.model.objects.filter(pk=value)
                    json_data = serializers.serialize("json", qs, fields=(field.name for field
                                                                          in qs.first()._meta.get_fields() if
                                                                          'extra' not in field.name))
                    json_data = [o['fields'] for o in json.loads(json_data)]
                    json_data = json.dumps(json_data)
                    json_data = json_data.replace(",", ";")
                    json_data = json_data.replace("\"", "'")
                    row.append(json_data)
                else:
                    row.append("[]")

            # Append all m2m fields
            elif field.get_internal_type() in ['ManyToManyField']:
                from django.core import serializers
                import json

                qs = getattr(obj, field.name).all()
                json_data = serializers.serialize("json", qs)
                json_data = [o['fields'] for o in json.loads(json_data)]
                json_data = json.dumps(json_data)
                json_data = json_data.replace(",", ";")
                json_data = json_data.replace("\"", "'")
                row.append(json_data)

        writer += ','.join(row)
        writer += '\n'

    return writer

The reason I have this is because my use case requires me to have a JSON dump of each related object as well.

You can then return this in the response so:

file = dump_to_csv(qs.model, qs)

            response = HttpResponse(file, content_type='text/csv')
            response['Content-Disposition'] = u'attachment; filename="{0}"'.format('export.csv')
            return response
Navid Khan
  • 979
  • 11
  • 24
0

Im late, but this could help someone... In my case, i create a submit btn in my template to post something like this:

import pandas
[...]
if request.method == 'POST':
    [...]
    dataset = "ur query here"
    response = HttpResponse (content_type='text/csv')
    response['Content-Disposition'] = 'attachment; filename="file.csv"'                                
    pandas.DataFrame(dataset).to_csv(response)
    return response