├── django_csv_exports ├── views.py ├── models.py ├── __init__.py ├── tests.py └── admin.py ├── AUTHORS ├── .gitignore ├── MANIFEST.in ├── .hgignore ├── tox.ini ├── runtests.py ├── LICENSE ├── setup.py └── README.rst /django_csv_exports/views.py: -------------------------------------------------------------------------------- 1 | # Create your views here. 2 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | Victor Rocha 2 | @dek (original django snippet) 3 | -------------------------------------------------------------------------------- /django_csv_exports/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | # Create your models here. 4 | -------------------------------------------------------------------------------- /django_csv_exports/__init__.py: -------------------------------------------------------------------------------- 1 | "" 2 | 3 | __version__ = '1.0.5' 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.py[co] 2 | .project 3 | .pydevproject 4 | *~ 5 | *.db 6 | *.orig 7 | *.DS_Store 8 | .coverage 9 | .tox 10 | *.egg-info/* 11 | docs/_build/* 12 | dist/* 13 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include AUTHORS 2 | include LICENSE 3 | include README.rst 4 | recursive-include django_csv_exports/static * 5 | recursive-include django_csv_exports/templates * 6 | -------------------------------------------------------------------------------- /.hgignore: -------------------------------------------------------------------------------- 1 | # use glob syntax. 2 | syntax: glob 3 | *.py[co] 4 | .project 5 | .pydevproject 6 | *~ 7 | *.db 8 | *.orig 9 | *.DS_Store 10 | .coverage 11 | .tox/* 12 | *.egg-info/* 13 | docs/_build/* 14 | dist/* 15 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | downloadcache = {toxworkdir}/_download/ 3 | envlist = py26-trunk,py26-1.4.X,py26-1.3.X 4 | 5 | [testenv] 6 | commands = {envpython} runtests.py 7 | 8 | [testenv:py26-trunk] 9 | basepython = python2.6 10 | deps = https://github.com/django/django/zipball/master 11 | 12 | [testenv:py26-1.4.X] 13 | basepython = python2.6 14 | deps = django>=1.4,<1.5 15 | 16 | [testenv:py26-1.3.X] 17 | basepython = python2.6 18 | deps = django>=1.3,<1.4 19 | -------------------------------------------------------------------------------- /django_csv_exports/tests.py: -------------------------------------------------------------------------------- 1 | """ 2 | This file demonstrates writing tests using the unittest module. These will pass 3 | when you run "manage.py test". 4 | 5 | Replace this with more appropriate tests for your application. 6 | """ 7 | 8 | from django.test import TestCase 9 | 10 | 11 | class SimpleTest(TestCase): 12 | def test_basic_addition(self): 13 | """ 14 | Tests that 1 + 1 always equals 2. 15 | """ 16 | self.assertEqual(1 + 1, 2) 17 | -------------------------------------------------------------------------------- /runtests.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import sys 3 | 4 | from django.conf import settings 5 | 6 | 7 | if not settings.configured: 8 | settings.configure( 9 | DATABASES={ 10 | 'default': { 11 | 'ENGINE': 'django.db.backends.sqlite3', 12 | 'NAME': ':memory:', 13 | } 14 | }, 15 | INSTALLED_APPS=( 16 | 'django_csv_exports', 17 | ), 18 | SITE_ID=1, 19 | SECRET_KEY='this-is-just-for-tests-so-not-that-secret', 20 | ) 21 | 22 | 23 | from django.test.utils import get_runner 24 | 25 | 26 | def runtests(): 27 | TestRunner = get_runner(settings) 28 | test_runner = TestRunner(verbosity=1, interactive=True, failfast=False) 29 | failures = test_runner.run_tests(['django_csv_exports', ]) 30 | sys.exit(failures) 31 | 32 | 33 | if __name__ == '__main__': 34 | runtests() 35 | 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2010-2015, Rochapp, LLC 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, 5 | are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, 8 | this list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 15 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 16 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 18 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 19 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 20 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 21 | ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 23 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | import os 2 | from setuptools import setup, find_packages 3 | 4 | 5 | def read_file(filename): 6 | """Read a file into a string""" 7 | path = os.path.abspath(os.path.dirname(__file__)) 8 | filepath = os.path.join(path, filename) 9 | try: 10 | return open(filepath).read() 11 | except IOError: 12 | return '' 13 | 14 | 15 | setup( 16 | name='django-csv-exports', 17 | version=__import__('django_csv_exports').__version__, 18 | author='Victor Rocha', 19 | author_email='victor@rochapps.com', 20 | packages=find_packages(), 21 | include_package_data=True, 22 | url='https://github.com/victor-rocha/django-csv-exports', 23 | license='BSD', 24 | description=u' '.join(__import__('django_csv_exports').__doc__.splitlines()).strip(), 25 | classifiers=[ 26 | 'Topic :: Internet :: WWW/HTTP :: Dynamic Content', 27 | 'Intended Audience :: Developers', 28 | 'Programming Language :: Python', 29 | 'Programming Language :: Python :: 2.6', 30 | 'Programming Language :: Python :: 2.7', 31 | 'Framework :: Django', 32 | 'Development Status :: 4 - Beta', 33 | 'Operating System :: OS Independent', 34 | ], 35 | long_description=read_file('README.rst'), 36 | test_suite="runtests.runtests", 37 | zip_safe=False, 38 | install_requires=[ 39 | 'pandas', 40 | 'numpy', 41 | 'python-dateutil', 42 | 'pytz' 43 | ] 44 | ) 45 | -------------------------------------------------------------------------------- /django_csv_exports/admin.py: -------------------------------------------------------------------------------- 1 | import csv 2 | 3 | import pandas 4 | 5 | import django 6 | from django.conf import settings 7 | from django.contrib import admin 8 | from django.http import HttpResponse, HttpResponseForbidden 9 | from builtins import str as text 10 | 11 | 12 | def export_as_csv(admin_model, request, queryset): 13 | """ 14 | Generic csv export admin action. 15 | based on http://djangosnippets.org/snippets/1697/ 16 | """ 17 | # everyone has perms to export as csv unless explicitly defined 18 | if getattr(settings, 'DJANGO_EXPORTS_REQUIRE_PERM', None): 19 | admin_opts = admin_model.opts 20 | codename = '%s_%s' % ('csv', admin_opts.object_name.lower()) 21 | has_csv_permission = request.user.has_perm("%s.%s" % (admin_opts.app_label, codename)) 22 | else: 23 | has_csv_permission = admin_model.has_csv_permission(request) \ 24 | if (hasattr(admin_model, 'has_csv_permission') and callable(getattr(admin_model, 'has_csv_permission'))) \ 25 | else True 26 | if has_csv_permission: 27 | opts = admin_model.model._meta 28 | if getattr(admin_model, 'csv_fields', None): 29 | field_names = admin_model.csv_fields 30 | else: 31 | field_names = [field.name for field in opts.fields] 32 | field_names.sort() 33 | 34 | if django.VERSION[0] == 1 and django.VERSION[1] <= 5: 35 | response = HttpResponse(mimetype='text/csv') 36 | else: 37 | response = HttpResponse(content_type='text/csv') 38 | response['Content-Disposition'] = 'attachment; filename=%s.csv' % text(opts).replace('.', '_') 39 | 40 | queryset = queryset.values_list(*field_names) 41 | pandas.DataFrame(list(queryset), columns=field_names).to_csv(response, index=False, encoding='utf-8') 42 | return response 43 | return HttpResponseForbidden() 44 | export_as_csv.short_description = "Export selected objects as csv file" 45 | 46 | 47 | class CSVExportAdmin(admin.ModelAdmin): 48 | def get_actions(self, request): 49 | actions = super(CSVExportAdmin, self).get_actions(request) 50 | if self.has_csv_permission(request): 51 | actions['export_as_csv'] = (export_as_csv, 'export_as_csv', "Export selected objects as csv file") 52 | return actions 53 | 54 | def has_csv_permission(self, request, obj=None): 55 | """ 56 | Returns True if the given request has permission to add an object. 57 | Can be overridden by the user in subclasses. By default, we assume 58 | all staff users can use this action unless `DJANGO_EXPORTS_REQUIRE_PERM` 59 | is set to True in your django settings. 60 | """ 61 | if getattr(settings, 'DJANGO_EXPORTS_REQUIRE_PERM', None): 62 | opts = self.opts 63 | codename = '%s_%s' % ('csv', opts.object_name.lower()) 64 | return request.user.has_perm("%s.%s" % (opts.app_label, codename)) 65 | return True 66 | 67 | 68 | if getattr(settings, 'DJANGO_CSV_GLOBAL_EXPORTS_ENABLED', True): 69 | admin.site.add_action(export_as_csv) 70 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | Django CSV Exports 2 | ======================== 3 | 4 | An admin action that allows you to export your models as CSV files without 5 | having to write a single line of code --besides installation, of course. 6 | 7 | Features 8 | ----------------------------------- 9 | 10 | - Easy installation 11 | - High level of customizability 12 | - Created with permissions in mind 13 | - Sane defaults 14 | 15 | Installation 16 | ---------------------------------- 17 | 18 | - Python 2.7, 3.3+ 19 | - `Django `_ >= 1.5 20 | - Pandas 21 | - Numpy 22 | - python-dateutil 23 | - pytz 24 | 25 | To install:: 26 | 27 | pip install django-csv-exports 28 | 29 | Next add `django_exports` to your `INSTALLED_APPS` to include the related css/js:: 30 | 31 | INSTALLED_APPS = ( 32 | # Other apps here 33 | 'django_csv_exports', 34 | ) 35 | 36 | 37 | Configuration 38 | ----------------------------------- 39 | There are two django settings that you can use to configure who can use the export action:: 40 | 41 | # Use if you want to check user level permissions only users with the can_csv_ 42 | # will be able to download csv files. 43 | DJANGO_EXPORTS_REQUIRE_PERM = True 44 | # Use if you want to disable the global django admin action. This setting is set to True by default. 45 | DJANGO_CSV_GLOBAL_EXPORTS_ENABLED = False 46 | 47 | 48 | Fields to export 49 | --------------------------------- 50 | By default, all of the fields available in a model ar ordered and exported. You can override this behavior 51 | at the admin model level. Define the following attribute in your AdminModel:: 52 | 53 | class ClientAdmin(CSVExportAdmin): 54 | csv_fields = ['first_name', 'last_name', 'email', 'phone_number',] 55 | 56 | 57 | Permission 58 | -------------------------------- 59 | There are two ways to limit who can export data as CSV files. 60 | 61 | Model level permissions: create a new model permission and assign it only to 62 | user who should have access to the export action in the admin. 63 | 64 | class Client(models.Model): 65 | class Meta: 66 | permissions = (("can_csv_client", "Can export list of clients as CSV file"),) 67 | 68 | AdminModel Level permissions: define a `has_csv_permission` and return True if a user should have access:: 69 | 70 | class ClientAdmin(admin.AdminModel): 71 | search_fields = ('name', 'id', 'email') 72 | csv_fields = ['name', 'id'] 73 | 74 | def has_csv_permission(self, request): 75 | """Only super users can export as CSV""" 76 | if request.user.is_superuser: 77 | return True 78 | 79 | 80 | Selective Installation 81 | ------------------------- 82 | Sometimes, you don't want to allow all of your admin models to be exported. For this, you will need to 83 | set `DJANGO_CSV_GLOBAL_EXPORTS_ENABLED` to False, and have your AdminModels extend our `CSVExportAdmin` 84 | admin class:: 85 | 86 | from django_csv_exports.admin import CSVExportAdmin 87 | 88 | class ClientAdmin(CSVExportAdmin): 89 | pass 90 | 91 | 92 | Running the Tests 93 | ------------------------------------ 94 | 95 | You can run the tests with via:: 96 | 97 | python setup.py test 98 | 99 | or:: 100 | 101 | python runtests.py 102 | --------------------------------------------------------------------------------