12

I'm fairly new to django and Python and want to be able to export a list of items in my model i.e products. I'm looking at the documentation here - https://docs.djangoproject.com/en/dev/howto/outputting-csv/

I'm persuming I need will need to create a variable that stores all the data that I want. But not sure where it would within the snippet of code on the link above.

Apologies as this is a very noobish question but would really Any help at all.

Here is the code to my script so far:

import csv

from products.models import Product

from django.http import HttpResponse


def export_to_csv(request):
    response = HttpResponse(content_type='text/csv')
    response['Content-Disposition'] = 'attachment; filename="mytest.csv"'
Josh Davies
  • 373
  • 2
  • 6
  • 14

10 Answers10

17

Have a look at the python csv module.

You'll probably want to get the models fields with

def get_model_fields(model):
    return model._meta.fields

Then use

getattr(instance, field.name)

to get the field values (as in this question).

Then you'll want something like

with open('your.csv', 'wb') as csvfile:
    writer = csv.writer(csvfile)
    # write your header first
    for obj in YourModel.objects.all():
        row = ""
        for field in fields:
             row += getattr(obj, field.name) + ","
        writer.writerow(row)

It's a bit verbose (and untested), but it should give you an idea. (Oh and don't forget to close your file)

Community
  • 1
  • 1
Aidan Ewen
  • 13,049
  • 8
  • 63
  • 88
  • Found you can use "for field in obj._meta.get_all_field_names():". Might be a bit more succinct. – Joe J Apr 28 '14 at 17:07
  • I had to make `row` a list: `row = []` and then `row.append(getattr(obj, field.name)`. Or simply: `writer.writerow(getattr(obj, field.name) for field in fields)` – Arild Apr 02 '19 at 08:28
15

Depending on the scenario - you may want to have a CSV of your model. If you have access to the Django Admin site, you can plug in a generic action for any model displayed as a list (google: django admin actions)

http://djangosnippets.org/snippets/790/

If you're operating with a console (python manage.py ...), you can use such a script, which I just used:

(place it in: yourapp/management/commands/model2csv.py)

"""
 Prints CSV of all fields of a model.
"""

from django.core.management.base import BaseCommand, CommandError
import csv
import sys

class Command(BaseCommand):
    help = ("Output the specified model as CSV")
    args = '[appname.ModelName]'

    def handle(self, *app_labels, **options):
        from django.db.models import get_model
        app_name, model_name = app_labels[0].split('.')
        model = get_model(app_name, model_name)
        field_names = [f.name for f in model._meta.fields]
        writer = csv.writer(sys.stdout, quoting=csv.QUOTE_ALL)
        writer.writerow(field_names)
        for instance in model.objects.all():
            writer.writerow([unicode(getattr(instance, f)).encode('utf-8') for f in field_names])

This does not catch any exceptions etc., but as an Admin you won't cause them to be raised, right?

Use it like:

./manage.py model2csv my_ecommerce.Product > products.csv
Tomasz Gandor
  • 8,235
  • 2
  • 60
  • 55
  • It does not seem to work if one of the attributes is a foreign key. For such attributes I am getting values of the form `id unknown`, where `id` is the value of the foreign key (e.g., `1 unknown`). What works for me is `field_names = [f.attname for f in model._meta.fields]`. – silentser Jan 11 '16 at 13:04
  • I recommend using select_related on queryset. If you have foreign keys you will have tons of SQL queries. `for instance in model.objects.all().select_related(): writer.writerow([unicode(getattr(instance, f)).encode('utf-8') for f in field_names])` – michal-michalak Jun 23 '17 at 08:46
3

You can also make a template to assist in formatting!

The template is a common Django template

from django.template import loader
def export_to_csv(request):
    response = HttpResponse(mimetype='text/csv')
    response['Content-Disposition'] = 'attachment; filename="products-list.csv"'
    template = loader.get_template('templates/products_template.csb')
    response.write(template.render(Context({'products': Products.objects.all()})))
    return response
Zokis
  • 390
  • 6
  • 12
  • 1
    help-link: [CSV Using the template system](https://docs.djangoproject.com/en/2.2/howto/outputting-csv/#using-the-template-system) – Grijesh Chauhan Nov 19 '19 at 05:43
3

I use this on my code. A function called from view. It automatically get model fields to make columns. You can also customize the field list you want to export.

Function

import csv

from django.http import HttpResponse

from .models import Books


def export_qs_to_csv(model_class = None, qs = None, field_names = None):
    if model_class and not qs:
        qs = model_class.objects.all()
    if qs and not model_class:
        model_class = qs.model

    meta = model_class._meta
    if not field_names:
        field_names = [field.name for field in meta.fields]

    response = HttpResponse(content_type='text/csv')
    response['Content-Disposition'] = 'attachment; filename={}.csv'.format(meta)
    writer = csv.writer(response)

    writer.writerow(field_names)
    for obj in qs:
        row = writer.writerow([getattr(obj, field) for field in field_names])

    return response

Usage

@user_passes_test(lambda u: u.is_superuser)
def export_books(request):
    return export_qs_to_csv(model_class = Books)
    # or
    return export_qs_to_csv(qs = Books.objects.filter(published = True))
    # or
    return export_qs_to_csv(
        qs = Books.objects.filter(published = True),
        field_names = [
            "title",
            "price",
            "publishing_date",
        ]
        )

Original answer

It works, and it needs only to define model class in model_class variable. This Django view let use downloads CSV. CSV name is Django_app.model_name.csv.

import csv

from django.http import HttpResponse

from .models import Trade


def export_to_csv(request):
    # The only line to customize
    model_class = Trade

    meta = model_class._meta
    field_names = [field.name for field in meta.fields]

    response = HttpResponse(content_type='text/csv')
    response['Content-Disposition'] = 'attachment; filename={}.csv'.format(meta)
    writer = csv.writer(response)

    writer.writerow(field_names)
    for obj in model_class.objects.all():
        row = writer.writerow([getattr(obj, field) for field in field_names])

    return response
Samuel Dauzon
  • 10,744
  • 13
  • 61
  • 94
3

Using django.db.models.query.QuerySet.values results in more optimised queries for my use case.

import csv
from datetime import datetime

from django.http import HttpResponse

# Populate this list with your model's fields
# Replace MODEL with your model
fields = [f.name for f in MODEL._meta.fields]

# The following code will live inside your view
timestamp = datetime.now().isoformat()

response = HttpResponse(content_type="text/csv")
response[
    "Content-Disposition"
] = f"attachment; filename={timestamp}.csv"
writer = csv.writer(response)

# Write the header row
writer.writerow(fields)

# Replace MODEL with your model
for row in MODEL.objects.values(*fields):
    writer.writerow([row[field] for field in fields])

return response
robsco
  • 559
  • 7
  • 13
2

If you don't care about fieldnames and want all the fields, just do this.

with open('file_name.csv', 'w') as csvfile:
    writer = csv.writer(csvfile)
    for obj in YourModel.objects.values_list():
        row = list(obj)
        writer.writerow(row)
anantdd
  • 125
  • 7
1

Here is a potential solution, based on @tomasz-gandor 's answer, but updated to 2020:

"""
 Prints CSV of all fields of a model.
"""

import csv
from django.core.management.base import BaseCommand, CommandError


class Command(BaseCommand):
    help = ("Output the specified model as CSV")


    def add_arguments(self, parser):
        parser.add_argument('model',
                            nargs=1,
                            type=str,
                            help='Model name to export, like <app.model> or "members.Member"')
        parser.add_argument('outfile',
                            nargs=1,
                            type=str,
                            help='Save path, like </path/to/outfile.csv> or "/data/members.csv"')


    def handle(self, *app_labels, **options):
        from django.apps import apps
        app_name, model_name = options['model'][0].split('.')
        model = apps.get_model(app_name, model_name)
        field_names = [f.name for f in model._meta.fields]
        writer = csv.writer(open(options['outfile'][0], 'w'), quoting=csv.QUOTE_ALL, delimiter=',')
        writer.writerow(field_names)
        for instance in model.objects.all():
            writer.writerow([str(getattr(instance, f)) for f in field_names])

Can easily be used with:

python manage.py model2csv members.Member /data/members_export.csv

Stunts
  • 430
  • 6
  • 11
0

I combined some of the previous answers, because I needed to import some data from production and change some of it along the way. So here is my solution, which you can use to override some field values while writing the CSV file.

Export some queryset data into CSV file:

import csv

from myapp.models import MyModel
from user.models import User

# Make some queryset manually using Django shell:
user = User.objects.get(username='peterhil')
queryset = MyModel.objects.filter(user=user)

def query_to_csv(queryset, filename='items.csv', **override):
    field_names = [field.name for field in queryset.model._meta.fields]
    def field_value(row, field_name):
        if field_name in override.keys():
            return override[field_name]
        else:
            return row[field_name]
    with open(filename, 'w') as csvfile:
        writer = csv.writer(csvfile, quoting=csv.QUOTE_ALL, delimiter=',')
        writer.writerow(field_names)  # write the header
        for row in queryset.values(*field_names):
            writer.writerow([field_value(row, field) for field in field_names])

# Example usage:
query_to_csv(queryset, filename='data.csv', user=1, group=1)
peterhil
  • 1,536
  • 1
  • 11
  • 18
0

Use this solution for model csv file.might being helpful

 # Create the HttpResponse object with the appropriate CSV header.
 response = HttpResponse(content_type='text/csv')
 response['Content-Disposition'] = 'attachment; 
 filename="somefilename.csv"'
 writer = csv.writer(response);
 writer.writerow(["username","Email"]);
 for i in User.objects.all():
     writer.writerow([i.username,i.email])
 return response
  • Welcome to Stackoverflow! There are already some high-quality answers to this question. What do you think is added by your answer, that was not already answered before? Consider editing your answer to reflect that, and please explain what (and why) your code is doing. – Qw3ry Aug 20 '20 at 09:52
0

I used the django-queryset-csv package.

Follow these steps:

  1. pip install django-queryset-csv

  2. Your views.py:

import djqscsv
from products.models import Product

def get_csv(request):
    qs = Product.objects.all()
    return djqscsv.render_to_csv_response(qs)
Gokul Raam
  • 31
  • 6