jrief/django-admin-sortable2

How to use SortableAdminMixin together with ImportExportModelAdmin from django-import-export?

micudaj opened this issue · 12 comments

I tried to use ImportExportModelAdmin together with SortableAdminMixin from admin-sortable2, but I get the following error. Any idea how and if these can work together?

join() argument must be str, bytes, or os.PathLike object, not 'list'
Request Method:	GET
Request URL:	http://xxxxx.de/admin/tenant_only/frage/
Django Version:	4.1.5
Exception Type:	TypeError
Exception Value:	
join() argument must be str, bytes, or os.PathLike object, not 'list'
Exception Location:	/usr/local/Cellar/python@3.10/3.10.9/Frameworks/Python.framework/Versions/3.10/lib/python3.10/genericpath.py, line 152, in _check_arg_types
Raised during:	adminsortable2.admin.changelist_view

Error during template rendering
In template /Users/mim/Downloads/sourcecode/django/django-klimawahlen/venv/lib/python3.10/site-packages/adminsortable2/templates/adminsortable2/change_list.html, error at line 1

join() argument must be str, bytes, or os.PathLike object, not 'list'

1	{% extends base_change_list_template %}
2	
3	{% block extrahead %}
4		{{ block.super }}
5		<script type="application/json" id="admin_sortable2_config">
6			{
7				"update_url": "{{ sortable_update_url }}",
8				"current_page": {{ cl.page_num }},
9				"total_pages": {{ cl.paginator.num_pages }}
10			}
11		</script>
jrief commented

Please read the contribution guidelines.

I'm sorry. I am not sure where to look. Do you have own contribution guidelines for questions? Can you please point me to them?

jrief commented

Please read here:
https://django-admin-sortable2.readthedocs.io/en/latest/contributing.html#reporting-bugs

Fork the repository, and adopt that demo to use ImportExportModelAdmin.
This has the benefit that if fixed, we have UI tests and E2E tests.
I am too lazy/busy to learn about another 3rd party library.

I recreated the bug in a fork: https://github.com/micudaj/django-admin-sortable2/tree/import-export-bug
It occurs when you open "Books (ordered by model, no inlines)" in admin.

jrief commented

Okay, thanks!

To get adminsortable2 and import-export working together I have the following:

admin.py

from adminsortable2.admin import SortableAdminMixin
from django.contrib import admin
from import_export.admin import ExportActionMixin, ImportExportMixin

class ExampleAdmin(
    ImportExportMixin, ExportActionMixin, SortableAdminMixin, admin.ModelAdmin
):
    # ...

Then to have both the adminsortable2 drag handles and the import-export buttons on the admin change list create the following template in your project template directory such that it will override the template included in the adminsortable2 package.

my_project/templates/adminsortable2/change_list.html

{% extends 'adminsortable2/change_list.html' %}

{% block object-tools-items %}
  {% include "admin/import_export/change_list_import_item.html" %}
  {% include "admin/import_export/change_list_export_item.html" %}
  {{ block.super }}
{% endblock %}

Thing is it looks like adminsortable2 will extend it's change list template from whatever you set the changelist_template attribute on your model admin to be. So you might think you can set it to one of the change list templates in import-export and adminsortable2 will extend that and the buttons will appear - but... import-export also implements this feature in a very similar way and uses exactly the same variable name when extending the changelist_template base_change_list_template. This results in a TemplateNotFound which is slightly confusing, but a result of the Django template finder system and our now recursive extends.

So I felt it was acceptable to solve this via the template override above. I could have just as easily extended the import-export template and copied the content from adminsortable2 but since import-export provides templates to include for their buttons I feel this way is better.

I'm not sure this needs fixing. Perhaps it could be fixed by renaming the base_change_list_template variable. But then I wonder if I've misunderstood the purpose of this base_change_list_template variable and perhaps it is no coincidence that it is identical in both packages. Perhaps it is meant to facilitate a situation such as this where the change list template should extend a chain of templates from multiple apps and I just don't know how to use it.

I was able to make work sorting with import/export butons just by explicitly setting change_list_template:

class MyAdmin(ImportExportMixin, SortableAdminMixin, admin.ModelAdmin):                                                                                                        
     change_list_template = "admin/import_export/change_list.html"

This seems to be issue of django-import-export which doesn't define change_list_template explicitly.

If the order of mixins is reversed (SortableAdminMixin, ImportExportMixin, admin.ModelAdmin) I think the issue lies in django-adminsortable2 which returns list for the change_list_template property while as defined in Django documentation it should return just the template path:

Path to a custom template, used by changelist_view().

perhaps it is no coincidence that it is identical in both packages

Yes, this is because the change was applied in django-admin-sortable2 and then ported to django-import-export. The purpose was to try to make it easier for the two packages to work together.

I was able to make work sorting with import/export butons just by explicitly setting change_list_template:

class MyAdmin(ImportExportMixin, SortableAdminMixin, admin.ModelAdmin):                                                                                                        
     change_list_template = "admin/import_export/change_list.html"

This seems to be issue of django-import-export which doesn't define change_list_template explicitly.

If the order of mixins is reversed (SortableAdminMixin, ImportExportMixin, admin.ModelAdmin) I think the issue lies in django-adminsortable2 which returns list for the change_list_template property while as defined in Django documentation it should return just the template path:

Path to a custom template, used by changelist_view().

Hi Petr,

when I use you suggestion I can see drag handles and import / export on the page, but the drag handles are not working. I see that they do not get any event handler assigned for drag events. Do you have any idea what I could do?

There are 3 remaining issues in this library which causes this issue:

  • The base_change_list_template variable should has specific name for each project. I have made PR for django-import-export, but to avoid clashes with other libraries I would recommend renaming the variable to something sortable_base_change_list_template.
  • The change_list_template doesn't have setter on which the solution with template mixin overrides depends.
  • The change_list_template does return list of paths witch is something that Django documentation doesn't count for and cannot be used in {% extends base_change_list_template %} of other associated libraries.

Hi, did someone figure out a workaround so far or is there any update on this issue? :)

Solution
step 1: use ImportExportMixin in admin
step 2: create file templates\adminsortable2\change_list.html with body:
`{% extends 'adminsortable2/change_list.html' %}

{% block object-tools-items %}
{% include "admin/import_export/change_list_import_item.html" %}
{% include "admin/import_export/change_list_export_item.html" %}
{{ block.super }}
{% endblock %}`