[Fixed]-Returning CSV format from django-rest-framework?


Got it. The trick is to install djangorestframework-csv, then add the following in settings:


And then scrap the JSONResponse function in views.py and just do return Response(serializer.data) instead. Very easy in the end.


If you just need to download CSV (without Model serialization etc)

import csv
from django.http import HttpResponse
from rest_framework.views import APIView

class CSVviewSet(APIView):

    def get(self, request, format=None):
        response = HttpResponse(content_type='text/csv')
        response['Content-Disposition'] = 'attachment; filename="export.csv"'
        writer = csv.DictWriter(response, fieldnames=['emp_name', 'dept', 'birth_month'])
        writer.writerow({'emp_name': 'John Smith', 'dept': 'Accounting', 'birth_month': 'November'})
        writer.writerow({'emp_name': 'Erica Meyers', 'dept': 'IT', 'birth_month': 'March'})
        return response


This is an old post, but I’ve seen the accepted answer sets the CSVRenderer as one of the defaults, which is not usually wanted.

I would implement the view this way:

from rest_framework.viewsets import ModelViewSet
from rest_framework.settings import api_settings
from rest_framework_csv.renderers import CSVRenderer
from .... import OrgSerializer

class OrganizationViewSet(ModelViewSet):

    queryset = Organisation.objects.all()
    http_method_names = ['get', '...list all the other verbs you want']
    serializer_class = OrgSerializer
    renderer_classes = tuple(api_settings.DEFAULT_RENDERER_CLASSES) + (CSVRenderer,)

    def get_queryset(self):
        if 'code' in self.request.GET:
            code = self.request.GET['code']
            return Organisation.objects.filter(code=code)
        return Organisation.objects.all()

Of course, having the django-rest-framework-csv installed and OrgSerializer defined somewhere.

Then you can just set 'rest_framework.renderers.JSONRenderer' as your default renderer in settings and the rest framework will automatically return csv content if you request it on the HTTP_ACCEPT header – just for this view.



I think that a StreamingHttpResponse should be the preferred way. With django-storages and ViewSet it looks more or less like this.

    @action(detail=True, methods=["get"])
    def download(self, request, pk=None):
        f = self.get_object()
        response = StreamingHttpResponse(
            streaming_content=f.file.chunks(), content_type="text/csv"
        ] = f'attachment; filename="{f.name}.csv"'
        response.status_code = status.HTTP_200_OK
        return response

Also, FileResponse is basically a StreamingHttpResponse with some headers etc so both are great for this use case.

Leave a comment