diff --git a/.gitignore b/.gitignore index 5689b0d..cc1c02f 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,4 @@ dist/ .idea/ *.python-version .coverage +*.sw[po] diff --git a/AUTHORS b/AUTHORS index f6f86ca..4e50728 100644 --- a/AUTHORS +++ b/AUTHORS @@ -104,3 +104,4 @@ The following is a list of much appreciated contributors: * pacotole (Pacotole) * KenyaDonDraper * andrew-bro (Andrei Loskutov) +* ZuluPro (Anthony Monthe) diff --git a/import_export/admin.py b/import_export/admin.py index 68834de..4ec968d 100644 --- a/import_export/admin.py +++ b/import_export/admin.py @@ -31,7 +31,7 @@ from .forms import ( from .resources import ( modelresource_factory, ) -from .formats import base_formats +from .formats.base_formats import DEFAULT_FORMATS from .results import RowResult from .tmp_storages import TempFolderStorage from .signals import post_export, post_import @@ -55,19 +55,6 @@ if isinstance(TMP_STORAGE_CLASS, six.string_types): msg = "Could not import '%s' for import_export setting 'IMPORT_EXPORT_TMP_STORAGE_CLASS'" % TMP_STORAGE_CLASS raise ImportError(msg) -#: These are the default formats for import and export. Whether they can be -#: used or not is depending on their implementation in the tablib library. -DEFAULT_FORMATS = ( - base_formats.CSV, - base_formats.XLS, - base_formats.XLSX, - base_formats.TSV, - base_formats.ODS, - base_formats.JSON, - base_formats.YAML, - base_formats.HTML, -) - class ImportExportMixinBase(object): def get_model_info(self): diff --git a/import_export/formats/base_formats.py b/import_export/formats/base_formats.py index da00774..fb71b11 100644 --- a/import_export/formats/base_formats.py +++ b/import_export/formats/base_formats.py @@ -223,3 +223,16 @@ class XLSX(TablibFormat): row_values = [cell.value for cell in row] dataset.append(row_values) return dataset + +#: These are the default formats for import and export. Whether they can be +#: used or not is depending on their implementation in the tablib library. +DEFAULT_FORMATS = ( + CSV, + XLS, + XLSX, + TSV, + ODS, + JSON, + YAML, + HTML, +) diff --git a/import_export/mixins.py b/import_export/mixins.py new file mode 100644 index 0000000..5e44c19 --- /dev/null +++ b/import_export/mixins.py @@ -0,0 +1,88 @@ +from django.http import HttpResponse +from django.views.generic.edit import FormView +from django.utils.timezone import now + +from .formats import base_formats +from .resources import modelresource_factory +from .signals import post_export +from .forms import ExportForm + + +class ExportViewMixin(object): + formats = base_formats.DEFAULT_FORMATS + form_class = ExportForm + resource_class = None + + def get_export_formats(self): + """ + Returns available export formats. + """ + return [f for f in self.formats if f().can_export()] + + def get_resource_class(self): + if not self.resource_class: + return modelresource_factory(self.model) + return self.resource_class + + def get_export_resource_class(self): + """ + Returns ResourceClass to use for export. + """ + return self.get_resource_class() + + def get_resource_kwargs(self, request, *args, **kwargs): + return {} + + def get_export_resource_kwargs(self, request, *args, **kwargs): + return self.get_resource_kwargs(request, *args, **kwargs) + + def get_export_data(self, file_format, queryset, *args, **kwargs): + """ + Returns file_format representation for given queryset. + """ + resource_class = self.get_export_resource_class() + data = resource_class(**self.get_export_resource_kwargs(self.request))\ + .export(queryset, *args, **kwargs) + export_data = file_format.export_data(data) + return export_data + + def get_export_filename(self, file_format): + date_str = now().strftime('%Y-%m-%d') + filename = "%s-%s.%s" % (self.model.__name__, + date_str, + file_format.get_extension()) + return filename + + def get_context_data(self, **kwargs): + context = super(ExportViewMixin, self).get_context_data(**kwargs) + return context + + def get_form_kwargs(self): + kwargs = super(ExportViewMixin, self).get_form_kwargs() + kwargs['formats'] = self.get_export_formats() + return kwargs + + +class ExportViewFormMixin(ExportViewMixin, FormView): + def form_valid(self, form): + formats = self.get_export_formats() + file_format = formats[ + int(form.cleaned_data['file_format']) + ]() + if hasattr(self, 'get_filterset'): + queryset = self.get_filterset(self.get_filterset_class()).qs + else: + queryset = self.get_queryset() + export_data = self.get_export_data(file_format, queryset) + content_type = file_format.get_content_type() + # Django 1.7 uses the content_type kwarg instead of mimetype + try: + response = HttpResponse(export_data, content_type=content_type) + except TypeError: + response = HttpResponse(export_data, mimetype=content_type) + response['Content-Disposition'] = 'attachment; filename=%s' % ( + self.get_export_filename(file_format), + ) + + post_export.send(sender=None, model=self.model) + return response diff --git a/tests/core/templates/core/category_list.html b/tests/core/templates/core/category_list.html new file mode 100644 index 0000000..3b2171d --- /dev/null +++ b/tests/core/templates/core/category_list.html @@ -0,0 +1,5 @@ +{{ form }} + +{% for obj in object_list %} + {{ obj }} +{% endfor %} diff --git a/tests/core/tests/mixins.py b/tests/core/tests/mixins.py new file mode 100644 index 0000000..aad8ba2 --- /dev/null +++ b/tests/core/tests/mixins.py @@ -0,0 +1,29 @@ +from __future__ import unicode_literals +from django.test.testcases import TestCase +try: + from django.urls import reverse +except ImportError: + from django.core.urlresolvers import reverse +from core.models import Category + + +class ExportViewMixinTest(TestCase): + + def setUp(self): + self.url = reverse('export-category') + self.cat1 = Category.objects.create(name='Cat 1') + self.cat2 = Category.objects.create(name='Cat 2') + + def test_get(self): + response = self.client.get(self.url) + self.assertContains(response, self.cat1.name, status_code=200) + self.assertTrue(response['Content-Type'], 'text/html') + + def test_post(self): + data = { + 'file_format': '0', + } + response = self.client.post(self.url, data) + self.assertContains(response, self.cat1.name, status_code=200) + self.assertTrue(response.has_header("Content-Disposition")) + self.assertTrue(response['Content-Type'], 'text/csv') diff --git a/tests/core/tests/test.py b/tests/core/tests/test.py index 23e7085..0188b58 100644 --- a/tests/core/tests/test.py +++ b/tests/core/tests/test.py @@ -7,3 +7,4 @@ from .instance_loaders_tests import * from .admin_integration_tests import * from .base_formats_tests import * from .tmp_storages_tests import * +from .mixins import * diff --git a/tests/core/views.py b/tests/core/views.py new file mode 100644 index 0000000..7c657d9 --- /dev/null +++ b/tests/core/views.py @@ -0,0 +1,7 @@ +from django.views.generic.list import ListView +from import_export import mixins +from . import models + + +class CategoryExportView(mixins.ExportViewFormMixin, ListView): + model = models.Category diff --git a/tests/urls.py b/tests/urls.py index 90c73d5..9a7f076 100644 --- a/tests/urls.py +++ b/tests/urls.py @@ -3,13 +3,16 @@ from __future__ import unicode_literals from django.conf.urls import url, include from django.contrib.staticfiles.urls import staticfiles_urlpatterns - from django.contrib import admin admin.autodiscover() +from core import views + urlpatterns = [ url(r'^admin/', admin.site.urls), + url(r'^export/category/', views.CategoryExportView.as_view(), + name='export-category'), ] urlpatterns += staticfiles_urlpatterns()