From c7680f63416bfb4e05c97d024e767ab7501c944d Mon Sep 17 00:00:00 2001 From: Radek Czajka Date: Thu, 26 Mar 2020 14:25:34 +0100 Subject: [PATCH] 0.4.4: Django 3.0 support, actions.export_as_csv_action added. --- CHANGELOG.md | 5 ++++ fnpdjango/actions.py | 51 ++++++++++++++++++++++++++++++++ runtests.py | 20 +++++++++++++ setup.py | 4 +-- tests/admin.py | 13 ++++++++ tests/migrations/0001_initial.py | 22 ++++++++++++++ tests/migrations/__init__.py | 0 tests/tests/__init__.py | 1 + tests/tests/test_actions.py | 26 ++++++++++++++++ tests/urls.py | 5 ++++ 10 files changed, 145 insertions(+), 2 deletions(-) create mode 100644 fnpdjango/actions.py create mode 100644 tests/admin.py create mode 100644 tests/migrations/0001_initial.py create mode 100644 tests/migrations/__init__.py create mode 100644 tests/tests/test_actions.py diff --git a/CHANGELOG.md b/CHANGELOG.md index 8802439..dac26e0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,11 @@ This document records all notable changes to fnpdjango. +## 0.4.4 (2020-03-26) + +- Support for Django up do 3.0. +- Added `actions.export_as_csv_action`. + ## 0.4.3 (2019-10-02) diff --git a/fnpdjango/actions.py b/fnpdjango/actions.py new file mode 100644 index 0000000..ec0479e --- /dev/null +++ b/fnpdjango/actions.py @@ -0,0 +1,51 @@ +# Source: https://gist.github.com/jeremyjbowers/e8d007446155c12033e6 +from __future__ import unicode_literals + +import csv +from django.http import HttpResponse + +try: + unicode +except NameError: + pass +else: + str = unicode + + +def export_as_csv_action(description="Export selected objects as CSV file", fields=None, exclude=None, header=True): + """ + This function returns an export csv action + 'fields' and 'exclude' work like in django ModelForm + 'header' is whether or not to output the column names as the first row + """ + def export_as_csv(modeladmin, request, queryset): + """ + Generic csv export admin action. + based on http://djangosnippets.org/snippets/1697/ + """ + opts = modeladmin.model._meta + field_names = [field.name for field in opts.fields] + + if fields: + for f in fields: + if f not in field_names: + field_names.append(f) + + elif exclude: + field_names = [f for f in field_names if f not in exclude] + + response = HttpResponse(content_type='text/csv') + response['Content-Disposition'] = 'attachment; filename=%s.csv' % str(opts).replace('.', '_') + + writer = csv.writer(response) + + if header: + writer.writerow(field_names) + for obj in queryset: + writer.writerow([str(getattr(obj, field)) for field in field_names]) + + return response + + export_as_csv.short_description = description + return export_as_csv + diff --git a/runtests.py b/runtests.py index 28648cf..bb6ba15 100644 --- a/runtests.py +++ b/runtests.py @@ -28,11 +28,15 @@ if not settings.configured and not os.environ.get('DJANGO_SETTINGS_MODULE'): DATABASES={ 'default': { 'ENGINE': 'django.db.backends.sqlite3', + + 'NAME': 'test.db', } }, INSTALLED_APPS=[ + 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', + 'django.contrib.messages', 'django.contrib.sessions', 'django.contrib.sites', @@ -46,11 +50,17 @@ if not settings.configured and not os.environ.get('DJANGO_SETTINGS_MODULE'): 'django.middleware.common.CommonMiddleware', 'fnpdjango.middleware.URLLocaleMiddleware', 'fnpdjango.middleware.SetRemoteAddrFromXRealIP', + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', ], MIDDLEWARE=[ 'django.middleware.common.CommonMiddleware', 'fnpdjango.middleware.URLLocaleMiddleware', 'fnpdjango.middleware.SetRemoteAddrFromXRealIP', + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', ], FNPDJANGO_REALIP = True, ROOT_URLCONF='tests.urls', @@ -58,10 +68,20 @@ if not settings.configured and not os.environ.get('DJANGO_SETTINGS_MODULE'): { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'APP_DIRS': True, + 'OPTIONS': { + 'context_processors': [ + 'django.contrib.auth.context_processors.auth', + 'django.contrib.messages.context_processors.messages', + ] + } }, ], TEST_LAZY_UGETTEXT_LAZY=_("Lazy setting."), USE_I18N=True, + + SECRET_KEY='x', + DEBUG=True, + SITE_ID=1, ) else: media_root = None diff --git a/setup.py b/setup.py index d2d3389..7b77c83 100755 --- a/setup.py +++ b/setup.py @@ -23,7 +23,7 @@ def whole_trees(package_dir, paths): setup( name='fnpdjango', - version='0.4.3', + version='0.4.4', author='Radek Czajka', author_email='radekczajka@nowoczesnapolska.org.pl', url='', @@ -33,7 +33,7 @@ setup( 'fnpdjango.management.commands': ['babel.cfg'], }, install_requires=[ - 'Django>=1.4,<2.3', + 'Django>=1.4,<3.1', ], extras_require={ 'textile': [ diff --git a/tests/admin.py b/tests/admin.py new file mode 100644 index 0000000..24f2d07 --- /dev/null +++ b/tests/admin.py @@ -0,0 +1,13 @@ +from django.contrib import admin +from . import models +from fnpdjango import actions + + +class SomeModelAdmin(admin.ModelAdmin): + actions = [ + actions.export_as_csv_action("CSV") + ] + + +admin.site.register(models.SomeModel, SomeModelAdmin) + diff --git a/tests/migrations/0001_initial.py b/tests/migrations/0001_initial.py new file mode 100644 index 0000000..c471f53 --- /dev/null +++ b/tests/migrations/0001_initial.py @@ -0,0 +1,22 @@ +# Generated by Django 2.2.11 on 2020-03-26 13:53 + +from django.db import migrations, models +import fnpdjango.storage + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='SomeModel', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('attachment', models.FileField(null=True, storage=fnpdjango.storage.BofhFileSystemStorage(), upload_to='test')), + ], + ), + ] diff --git a/tests/migrations/__init__.py b/tests/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/tests/__init__.py b/tests/tests/__init__.py index 7801a8c..ef64f9f 100644 --- a/tests/tests/__init__.py +++ b/tests/tests/__init__.py @@ -8,6 +8,7 @@ in Django<1.6. The newer django.test.runner.DiscoverRunner finds test_* modules by itself. """ +from .test_actions import * from .test_middleware import * from .test_storage import * from .test_templatetags_fnp_annoy import * diff --git a/tests/tests/test_actions.py b/tests/tests/test_actions.py new file mode 100644 index 0000000..b7d19d3 --- /dev/null +++ b/tests/tests/test_actions.py @@ -0,0 +1,26 @@ +from django.test import TestCase +from django.contrib.auth.models import User +from ..models import SomeModel + + +class ActionsTests(TestCase): + def test_csv(self): + u = User(username='user', is_superuser=True, is_staff=True) + u.set_password('test') + u.save() + + SomeModel.objects.create() + + self.client.login(username='user', password='test') + + response = self.client.post( + '/admin/tests/somemodel/', + { + 'action': 'export_as_csv', + '_selected_action': '1', + } + ) + self.assertEqual( + response.content, + b"id,attachment\r\n1,\r\n") + diff --git a/tests/urls.py b/tests/urls.py index 15a9417..e9ee19f 100644 --- a/tests/urls.py +++ b/tests/urls.py @@ -1,9 +1,14 @@ from django.conf.urls import url +from django.contrib import admin from fnpdjango.utils.urls import i18n_patterns from . import views +# For Django < 1.7. +admin.autodiscover() + urlpatterns = [ + url(r'^admin/', admin.site.urls), url(r'^ip/$', views.ip), ] + i18n_patterns( url(r'^$', views.get_lang), -- 2.20.1