Skip to content

Commit

Permalink
add support for ModelAdmin callable - #181
Browse files Browse the repository at this point in the history
  • Loading branch information
saxix committed Nov 1, 2023
1 parent c3a8704 commit 02c4a8c
Show file tree
Hide file tree
Showing 8 changed files with 55 additions and 14 deletions.
2 changes: 1 addition & 1 deletion CHANGES
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ Release <dev>
* add `dry_run` option to bulk_update
* bulk_update now returns a page with pre/post action field values
* renamed `get_export_form` as `get_aa_export_form` ( @see https://github.com/saxix/django-adminactions/issues/217)

* add support for ModelAdmin callable (@see https://github.com/saxix/django-adminactions/issues/181)

Release 2.1
===========
Expand Down
23 changes: 16 additions & 7 deletions src/adminactions/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,13 @@ def write(self, value):


def export_as_csv( # noqa: max-complexity: 20
queryset, fields=None, header=None, filename=None, options=None, out=None
queryset,
fields=None,
header=None,
filename=None,
options=None,
out=None,
modeladmin=None,
): # noqa
"""
Exports a queryset as csv from a queryset with the given fields.
Expand Down Expand Up @@ -169,7 +175,6 @@ def export_as_csv( # noqa: max-complexity: 20
config.update(options)
if fields is None:
fields = [f.name for f in queryset.model._meta.fields + queryset.model._meta.many_to_many]

if streaming_enabled:
buffer_object = Echo()
else:
Expand Down Expand Up @@ -201,7 +206,7 @@ def yield_rows():
for obj in queryset:
row = []
for fieldname in fields:
value = get_field_value(obj, fieldname)
value = get_field_value(obj, fieldname, modeladmin=modeladmin)
if isinstance(value, datetime.datetime):
try:
value = dateformat.format(
Expand Down Expand Up @@ -249,7 +254,7 @@ def yield_rows():


def export_as_xls2( # noqa: max-complexity: 24
queryset, fields=None, header=None, filename=None, options=None, out=None # noqa
queryset, fields=None, header=None, filename=None, options=None, out=None, modeladmin=None # noqa
):
# sheet_name=None, header_alt=None,
# formatting=None, out=None):
Expand Down Expand Up @@ -334,7 +339,9 @@ def _get_qs_formats(queryset):
for col_idx, fieldname in enumerate(fields):
fmt = formats.get(col_idx, "general")
try:
value = get_field_value(row, fieldname, usedisplay=use_display, raw_callable=False)
value = get_field_value(
row, fieldname, usedisplay=use_display, raw_callable=False, modeladmin=modeladmin
)
if callable(fmt):
value = xlwt.Formula(fmt(value))
if hash(fmt) not in _styles:
Expand Down Expand Up @@ -382,7 +389,7 @@ def _get_qs_formats(queryset):


def export_as_xls3( # noqa: max-complexity: 23
queryset, fields=None, header=None, filename=None, options=None, out=None # noqa
queryset, fields=None, header=None, filename=None, options=None, out=None, modeladmin=None # noqa
): # pragma: no cover
# sheet_name=None, header_alt=None,
# formatting=None, out=None):
Expand Down Expand Up @@ -457,7 +464,9 @@ def _get_qs_formats(queryset):
for idx, fieldname in enumerate(fields):
fmt = formats.get(fieldname, formats["_general_"])
try:
value = get_field_value(row, fieldname, usedisplay=use_display, raw_callable=False)
value = get_field_value(
row, fieldname, usedisplay=use_display, raw_callable=False, modeladmin=modeladmin
)
if callable(fmt):
value = fmt(value)
if isinstance(value, (list, tuple)):
Expand Down
1 change: 1 addition & 0 deletions src/adminactions/export.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,7 @@ def base_export(
header=form.cleaned_data.get("header", False),
filename=filename,
options=form.cleaned_data,
modeladmin=modeladmin,
)
except Exception as e:
logger.exception(e)
Expand Down
6 changes: 4 additions & 2 deletions src/adminactions/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ def getattr_or_item(obj, name):
return ret


def get_field_value(obj, field, usedisplay=True, raw_callable=False):
def get_field_value(obj, field, usedisplay=True, raw_callable=False, modeladmin=None):
"""
returns the field value or field representation if get_FIELD_display exists
Expand All @@ -117,7 +117,9 @@ def get_field_value(obj, field, usedisplay=True, raw_callable=False):
else:
raise ValueError("Invalid value for parameter `field`: Should be a field name or a Field instance")

if usedisplay and hasattr(obj, "get_%s_display" % fieldname):
if modeladmin and hasattr(modeladmin, fieldname):
value = getattr(modeladmin, fieldname)(obj)
elif usedisplay and hasattr(obj, "get_%s_display" % fieldname):
value = getattr(obj, "get_%s_display" % fieldname)()
else:
value = getattr_or_item(obj, fieldname)
Expand Down
10 changes: 7 additions & 3 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
import logging
import os
import shutil
import sys
import tempfile
from pathlib import Path

import django_webtest
import pytest
Expand Down Expand Up @@ -61,9 +63,11 @@ def pytest_addoption(parser):


def pytest_configure(config):
# import warnings
# enable this to remove deprecations
# warnings.simplefilter('once', DeprecationWarning)
here = Path(__file__).parent
sys.path.insert(0, here)
sys.path.insert(0, here.parent / "src")
os.environ["DJANGO_SETTINGS_MODULE"] = "demo.settings"

from django.conf import settings

if (
Expand Down
10 changes: 10 additions & 0 deletions tests/demo/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,16 +63,26 @@ class UserDetailModelAdmin(ExtraUrlMixin, ModelAdmin):
list_display = [f.name for f in UserDetail._meta.fields]


def export_one(_modeladmin, _request, queryset):
from adminactions.api import export_as_csv

return export_as_csv(queryset, fields=["get_custom_field"], modeladmin=_modeladmin)


class DemoModelAdmin(ExtraUrlMixin, ModelAdmin):
# list_display = ('char', 'integer', 'logic', 'null_logic',)
list_display = [f.name for f in DemoModel._meta.fields]
actions = (export_one,)

@button()
def import_fixture(self, request):
from adminactions.helpers import import_fixture as _import_fixture

return _import_fixture(self, request)

def get_custom_field(self, instance):
return f"model-attribute-{instance.pk}"


class DemoOneToOneAdmin(ExtraUrlMixin, AdminActionPermMixin, ModelAdmin):
pass
Expand Down
14 changes: 14 additions & 0 deletions tests/test_exports.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import io
import time
import unittest
from unittest.mock import Mock

import mock
import xlrd
Expand Down Expand Up @@ -442,3 +443,16 @@ def test_faster_export(self):
6.5,
"Response should return under 6.5 " "seconds, was %.2f" % res_time,
)

def test_modeladmin_attributes(self):
from demo.models import DemoModel
from django.contrib.admin import site

from adminactions.api import export_as_csv

def export_model_admin_attr(_modeladmin, _request, queryset):
return export_as_csv(queryset, fields=["get_custom_field"], modeladmin=_modeladmin)

recs = DemoModel.objects.order_by("pk")[:1]
res = export_model_admin_attr(site._registry[DemoModel], Mock(), recs)
assert res.content.decode() == f'"model-attribute-{recs[0].pk}"\r\n'
3 changes: 2 additions & 1 deletion tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ envlist = d{32,42}-py{39,310,311}
;skip_missing_interpreters = true
;
[pytest]
DJANGO_SETTINGS_MODULE = demo.settings
;DJANGO_SETTINGS_MODULE = demo.settings
django_find_project = false
norecursedirs = demo .tox
addopts =
-v
Expand Down

0 comments on commit 02c4a8c

Please sign in to comment.