0.4.4: Django 3.0 support, actions.export_as_csv_action added. 0.4.4
authorRadek Czajka <rczajka@rczajka.pl>
Thu, 26 Mar 2020 13:25:34 +0000 (14:25 +0100)
committerRadek Czajka <rczajka@rczajka.pl>
Thu, 26 Mar 2020 13:25:34 +0000 (14:25 +0100)
CHANGELOG.md
fnpdjango/actions.py [new file with mode: 0644]
runtests.py
setup.py
tests/admin.py [new file with mode: 0644]
tests/migrations/0001_initial.py [new file with mode: 0644]
tests/migrations/__init__.py [new file with mode: 0644]
tests/tests/__init__.py
tests/tests/test_actions.py [new file with mode: 0644]
tests/urls.py

index 8802439..dac26e0 100644 (file)
@@ -2,6 +2,11 @@
 
 This document records all notable changes to fnpdjango.
 
 
 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)
 
 
 ## 0.4.3 (2019-10-02)
 
diff --git a/fnpdjango/actions.py b/fnpdjango/actions.py
new file mode 100644 (file)
index 0000000..ec0479e
--- /dev/null
@@ -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
+
index 28648cf..bb6ba15 100644 (file)
@@ -28,11 +28,15 @@ if not settings.configured and not os.environ.get('DJANGO_SETTINGS_MODULE'):
         DATABASES={
             'default': {
                 'ENGINE': 'django.db.backends.sqlite3',
         DATABASES={
             'default': {
                 'ENGINE': 'django.db.backends.sqlite3',
+
+                'NAME': 'test.db',
             }
         },
         INSTALLED_APPS=[
             }
         },
         INSTALLED_APPS=[
+            'django.contrib.admin',
             'django.contrib.auth',
             'django.contrib.contenttypes',
             'django.contrib.auth',
             'django.contrib.contenttypes',
+            'django.contrib.messages',
             'django.contrib.sessions',
             'django.contrib.sites',
 
             '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.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',
         ],
         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',
         ],
         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,
             {
                 '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,
             },
         ],
         TEST_LAZY_UGETTEXT_LAZY=_("Lazy setting."),
         USE_I18N=True,
+
+        SECRET_KEY='x',
+        DEBUG=True,
+        SITE_ID=1,
     )
 else:
     media_root = None
     )
 else:
     media_root = None
index d2d3389..7b77c83 100755 (executable)
--- a/setup.py
+++ b/setup.py
@@ -23,7 +23,7 @@ def whole_trees(package_dir, paths):
 
 setup(
     name='fnpdjango',
 
 setup(
     name='fnpdjango',
-    version='0.4.3',
+    version='0.4.4',
     author='Radek Czajka',
     author_email='radekczajka@nowoczesnapolska.org.pl',
     url='',
     author='Radek Czajka',
     author_email='radekczajka@nowoczesnapolska.org.pl',
     url='',
@@ -33,7 +33,7 @@ setup(
         'fnpdjango.management.commands': ['babel.cfg'],
     },
     install_requires=[
         'fnpdjango.management.commands': ['babel.cfg'],
     },
     install_requires=[
-        'Django>=1.4,<2.3',
+        'Django>=1.4,<3.1',
     ],
     extras_require={
         'textile': [
     ],
     extras_require={
         'textile': [
diff --git a/tests/admin.py b/tests/admin.py
new file mode 100644 (file)
index 0000000..24f2d07
--- /dev/null
@@ -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 (file)
index 0000000..c471f53
--- /dev/null
@@ -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 (file)
index 0000000..e69de29
index 7801a8c..ef64f9f 100644 (file)
@@ -8,6 +8,7 @@ in Django<1.6.  The newer django.test.runner.DiscoverRunner finds
 test_* modules by itself.
 
 """
 test_* modules by itself.
 
 """
+from .test_actions import *
 from .test_middleware import *
 from .test_storage import *
 from .test_templatetags_fnp_annoy 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 (file)
index 0000000..b7d19d3
--- /dev/null
@@ -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")
+        
index 15a9417..e9ee19f 100644 (file)
@@ -1,9 +1,14 @@
 from django.conf.urls import url
 from django.conf.urls import url
+from django.contrib import admin
 from fnpdjango.utils.urls import i18n_patterns
 from . import views
 
 
 from fnpdjango.utils.urls import i18n_patterns
 from . import views
 
 
+# For Django < 1.7.
+admin.autodiscover()
+
 urlpatterns = [
 urlpatterns = [
+    url(r'^admin/', admin.site.urls),
     url(r'^ip/$', views.ip),
 ] + i18n_patterns(
     url(r'^$', views.get_lang),
     url(r'^ip/$', views.ip),
 ] + i18n_patterns(
     url(r'^$', views.get_lang),