From d7aa2a91b361499ecfcff88b96bf6a790e077869 Mon Sep 17 00:00:00 2001 From: Andrew Dickinson Date: Sun, 2 Jun 2024 16:23:54 -0400 Subject: [PATCH] Integrate django-import-export --- pyproject.toml | 1 + src/meshapi/admin/building.py | 3 ++- src/meshapi/admin/device.py | 3 ++- src/meshapi/admin/install.py | 19 +++++++++++++++++-- src/meshapi/admin/link.py | 3 ++- src/meshapi/admin/member.py | 3 ++- src/meshapi/admin/node.py | 19 +++++++++++++++++-- src/meshapi/admin/sector.py | 3 ++- src/meshdb/settings.py | 4 ++++ 9 files changed, 49 insertions(+), 9 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index cef763a1..457d191f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -34,6 +34,7 @@ dependencies = [ # FIXME: Go back to PyPI when https://github.com/jazzband/django-dbbackup/pull/515 or https://github.com/jazzband/django-dbbackup/pull/511 is merged "django-dbbackup@git+https://github.com/willnilges/django-dbbackup.git@62048411ff5beac4beac1578f686824214f1f33a", "django-storages==1.14.*", + "django-import-export==4.0.*", "boto3==1.34.*", "six==1.16.0", ] diff --git a/src/meshapi/admin/building.py b/src/meshapi/admin/building.py index d21306f3..7b203663 100644 --- a/src/meshapi/admin/building.py +++ b/src/meshapi/admin/building.py @@ -5,6 +5,7 @@ from django.contrib.admin import ModelAdmin from django.db.models import QuerySet from django.http import HttpRequest +from import_export.admin import ExportActionMixin, ImportExportModelAdmin from meshapi.admin.inlines import InstallInline from meshapi.models import Building @@ -49,7 +50,7 @@ class Meta: @admin.register(Building) -class BuildingAdmin(admin.ModelAdmin): +class BuildingAdmin(ImportExportModelAdmin, ExportActionMixin): form = BuildingAdminForm list_display = ["__str__", "street_address", "primary_node"] search_fields = [ diff --git a/src/meshapi/admin/device.py b/src/meshapi/admin/device.py index 0e40782f..fb3035f6 100644 --- a/src/meshapi/admin/device.py +++ b/src/meshapi/admin/device.py @@ -2,6 +2,7 @@ from django.contrib import admin from django.db.models import QuerySet from django.http import HttpRequest +from import_export.admin import ExportActionMixin, ImportExportModelAdmin from meshapi.admin.admin import device_fieldsets from meshapi.admin.inlines import DeviceLinkInline @@ -19,7 +20,7 @@ class Meta: @admin.register(Device) -class DeviceAdmin(admin.ModelAdmin): +class DeviceAdmin(ImportExportModelAdmin, ExportActionMixin): form = DeviceAdminForm search_fields = ["name__icontains", "model__icontains", "ssid__icontains", "notes__icontains"] list_display = [ diff --git a/src/meshapi/admin/install.py b/src/meshapi/admin/install.py index 0973ccb2..8f917165 100644 --- a/src/meshapi/admin/install.py +++ b/src/meshapi/admin/install.py @@ -1,13 +1,27 @@ -from typing import Tuple +from typing import Any, Tuple +import tablib from django import forms from django.contrib import admin from django.db.models import QuerySet from django.http import HttpRequest +from import_export import resources +from import_export.admin import ExportActionMixin, ImportExportModelAdmin from meshapi.models import Install +class InstallImportExportResource(resources.ModelResource): + def before_import(self, dataset: tablib.Dataset, **kwargs: Any) -> None: + if "install_number" not in dataset.headers: + dataset.headers.append("install_number") + super().before_import(dataset, **kwargs) + + class Meta: + model = Install + import_id_fields = ("install_number",) + + class InstallAdminForm(forms.ModelForm): class Meta: model = Install @@ -18,8 +32,9 @@ class Meta: @admin.register(Install) -class InstallAdmin(admin.ModelAdmin): +class InstallAdmin(ImportExportModelAdmin, ExportActionMixin): form = InstallAdminForm + resource_classes = [InstallImportExportResource] list_filter = [ ("node", admin.EmptyFieldListFilter), "status", diff --git a/src/meshapi/admin/link.py b/src/meshapi/admin/link.py index 365bcdfb..908da4fe 100644 --- a/src/meshapi/admin/link.py +++ b/src/meshapi/admin/link.py @@ -1,5 +1,6 @@ from django import forms from django.contrib import admin +from import_export.admin import ExportActionMixin, ImportExportModelAdmin from meshapi.models import Link @@ -14,7 +15,7 @@ class Meta: @admin.register(Link) -class LinkAdmin(admin.ModelAdmin): +class LinkAdmin(ImportExportModelAdmin, ExportActionMixin): form = LinkAdminForm search_fields = [ "from_device__node__name__icontains", diff --git a/src/meshapi/admin/member.py b/src/meshapi/admin/member.py index bce50246..bf23a161 100644 --- a/src/meshapi/admin/member.py +++ b/src/meshapi/admin/member.py @@ -1,5 +1,6 @@ from django import forms from django.contrib import admin +from import_export.admin import ExportActionMixin, ImportExportModelAdmin from meshapi.admin.inlines import InstallInline from meshapi.models import Member @@ -18,7 +19,7 @@ class Meta: @admin.register(Member) -class MemberAdmin(admin.ModelAdmin): +class MemberAdmin(ImportExportModelAdmin, ExportActionMixin): form = MemberAdminForm search_fields = [ # Search by name diff --git a/src/meshapi/admin/node.py b/src/meshapi/admin/node.py index cea291d9..88b9f858 100644 --- a/src/meshapi/admin/node.py +++ b/src/meshapi/admin/node.py @@ -1,7 +1,10 @@ -from typing import Optional +from typing import Any, Optional +import tablib from django import forms from django.contrib import admin +from import_export import resources +from import_export.admin import ExportActionMixin, ImportExportModelAdmin from meshapi.admin.inlines import ( BuildingMembershipInline, @@ -19,6 +22,17 @@ admin.site.index_title = "Welcome to MeshDB Admin Portal" +class NodeImportExportResource(resources.ModelResource): + def before_import(self, dataset: tablib.Dataset, **kwargs: Any) -> None: + if "network_number" not in dataset.headers: + dataset.headers.append("network_number") + super().before_import(dataset, **kwargs) + + class Meta: + model = Node + import_id_fields = ("network_number",) + + class NodeAdminForm(forms.ModelForm): class Meta: model = Node @@ -26,8 +40,9 @@ class Meta: @admin.register(Node) -class NodeAdmin(admin.ModelAdmin): +class NodeAdmin(ImportExportModelAdmin, ExportActionMixin): form = NodeAdminForm + resource_classes = [NodeImportExportResource] search_fields = [ "network_number__iexact", "name__icontains", diff --git a/src/meshapi/admin/sector.py b/src/meshapi/admin/sector.py index 6b01f431..21d51a5f 100644 --- a/src/meshapi/admin/sector.py +++ b/src/meshapi/admin/sector.py @@ -1,5 +1,6 @@ from django import forms from django.contrib import admin +from import_export.admin import ExportActionMixin, ImportExportModelAdmin from meshapi.admin.admin import device_fieldsets from meshapi.admin.inlines import DeviceLinkInline @@ -13,7 +14,7 @@ class Meta: @admin.register(Sector) -class SectorAdmin(admin.ModelAdmin): +class SectorAdmin(ImportExportModelAdmin, ExportActionMixin): form = SectorAdminForm search_fields = ["name__icontains", "model__icontains", "ssid__icontains", "notes__icontains"] list_display = [ diff --git a/src/meshdb/settings.py b/src/meshdb/settings.py index ab4039ff..8585c759 100644 --- a/src/meshdb/settings.py +++ b/src/meshdb/settings.py @@ -94,6 +94,7 @@ "django_filters", "django_jsonform", "dbbackup", + "import_export", ] MIDDLEWARE = [ @@ -299,3 +300,6 @@ "docExpansion": "none", }, } + +IMPORT_EXPORT_IMPORT_PERMISSION_CODE = "add" +IMPORT_EXPORT_EXPORT_PERMISSION_CODE = "view"