From f14cc0e39e1d9e2c67121be77bd13f6bff3ec24b Mon Sep 17 00:00:00 2001 From: Tobias Brunner Date: Tue, 20 May 2025 14:26:31 +0200 Subject: [PATCH] add import and export functionality --- hub/services/admin.py | 31 +++++++++++++++++++++++++++++-- hub/settings.py | 4 ++++ pyproject.toml | 1 + uv.lock | 34 ++++++++++++++++++++++++++++++++++ 4 files changed, 68 insertions(+), 2 deletions(-) diff --git a/hub/services/admin.py b/hub/services/admin.py index b150b50..1d7e295 100644 --- a/hub/services/admin.py +++ b/hub/services/admin.py @@ -1,6 +1,10 @@ from django.contrib import admin from django.utils.html import format_html from adminsortable2.admin import SortableAdminMixin +from import_export.admin import ImportExportModelAdmin +from import_export import resources +from import_export.fields import Field +from import_export.widgets import ForeignKeyWidget from .models import ( Category, @@ -188,8 +192,31 @@ class WebsiteFaqAdmin(SortableAdminMixin, admin.ModelAdmin): ordering = ("order",) +class ComputePlanResource(resources.ModelResource): + cloud_provider = Field( + column_name="cloud_provider", + attribute="cloud_provider", + widget=ForeignKeyWidget(CloudProvider, "name"), + ) + + class Meta: + model = ComputePlan + skip_unchanged = True + report_skipped = False + import_id_fields = ["name"] + fields = ( + "name", + "vcpus", + "ram", + "cpu_mem_ratio", + "price_chf", + "cloud_provider", + ) + + @admin.register(ComputePlan) -class ComputePlansAdmin(admin.ModelAdmin): +class ComputePlansAdmin(ImportExportModelAdmin): + resource_class = ComputePlanResource list_display = ("name", "cloud_provider", "vcpus", "ram", "price_chf", "active") - search_fields = ("name", "cloud_provider") + search_fields = ("name", "cloud_provider__name") # Search by cloud_provider name ordering = ("name",) diff --git a/hub/settings.py b/hub/settings.py index dad9ff9..7ee499a 100644 --- a/hub/settings.py +++ b/hub/settings.py @@ -1,5 +1,6 @@ from pathlib import Path from environs import Env +from import_export.formats.base_formats import CSV env = Env() env.read_env() @@ -79,6 +80,7 @@ INSTALLED_APPS = [ "schema_viewer", "nested_admin", "adminsortable2", + "import_export", # local "hub.services", "hub.broker", @@ -245,3 +247,5 @@ JAZZMIN_SETTINGS = { "show_sidebar": True, "navigation_expanded": True, } + +IMPORT_EXPORT_FORMATS = [CSV] diff --git a/pyproject.toml b/pyproject.toml index 84f0dd2..c1cd69f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -7,6 +7,7 @@ requires-python = ">=3.13" dependencies = [ "django>=5.2", "django-admin-sortable2>=2.2.4", + "django-import-export>=4.3.7", "django-jazzmin>=3.0.1", "django-nested-admin>=4.1.1", "django-prose-editor[sanitize]>=0.10.3", diff --git a/uv.lock b/uv.lock index 5184ef3..9f8cfae 100644 --- a/uv.lock +++ b/uv.lock @@ -11,6 +11,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/39/e3/893e8757be2612e6c266d9bb58ad2e3651524b5b40cf56761e985a28b13e/asgiref-3.8.1-py3-none-any.whl", hash = "sha256:3e1e3ecc849832fe52ccf2cb6686b7a55f82bb1d6aee72a58826471390335e47", size = 23828, upload-time = "2024-03-22T14:39:34.521Z" }, ] +[[package]] +name = "diff-match-patch" +version = "20241021" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/0e/ad/32e1777dd57d8e85fa31e3a243af66c538245b8d64b7265bec9a61f2ca33/diff_match_patch-20241021.tar.gz", hash = "sha256:beae57a99fa48084532935ee2968b8661db861862ec82c6f21f4acdd6d835073", size = 39962, upload-time = "2024-10-21T19:41:21.094Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f7/bb/2aa9b46a01197398b901e458974c20ed107935c26e44e37ad5b0e5511e44/diff_match_patch-20241021-py3-none-any.whl", hash = "sha256:93cea333fb8b2bc0d181b0de5e16df50dd344ce64828226bda07728818936782", size = 43252, upload-time = "2024-10-21T19:41:19.914Z" }, +] + [[package]] name = "dj-database-url" version = "2.3.0" @@ -81,6 +90,20 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/48/90/01755e4a42558b763f7021e9369aa6aa94c2ede7313deed56cb7483834ab/django_cache_url-3.4.5-py2.py3-none-any.whl", hash = "sha256:5f350759978483ab85dc0e3e17b3d53eed3394a28148f6bf0f53d11d0feb5b3c", size = 4760, upload-time = "2023-12-04T17:19:44.355Z" }, ] +[[package]] +name = "django-import-export" +version = "4.3.7" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "diff-match-patch" }, + { name = "django" }, + { name = "tablib" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/57/ae/52275e8a49a963468f9f807c24df17416fad0220169a8e5d7bfd4778f17f/django_import_export-4.3.7.tar.gz", hash = "sha256:bd3fe0aa15a2bce9de4be1a2f882e2c4539fdbfdfa16f2052c98dd7aec0f085c", size = 2222150, upload-time = "2025-02-25T12:38:47.076Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9d/ad/b1f4aef18fd4ab86ce68f3f0fff0e9c14e5fd2c866c47f0b1cfb9ccd85c8/django_import_export-4.3.7-py3-none-any.whl", hash = "sha256:5514d09636e84e823a42cd5e79292f70f20d6d2feed117a145f5b64a5b44f168", size = 142815, upload-time = "2025-02-25T12:38:43.654Z" }, +] + [[package]] name = "django-jazzmin" version = "3.0.1" @@ -292,6 +315,7 @@ source = { virtual = "." } dependencies = [ { name = "django" }, { name = "django-admin-sortable2" }, + { name = "django-import-export" }, { name = "django-jazzmin" }, { name = "django-nested-admin" }, { name = "django-prose-editor", extra = ["sanitize"] }, @@ -312,6 +336,7 @@ requires-dist = [ { name = "django", specifier = ">=5.2" }, { name = "django-admin-sortable2", specifier = ">=2.2.4" }, { name = "django-browser-reload", marker = "extra == 'dev'", specifier = "~=1.13" }, + { name = "django-import-export", specifier = ">=4.3.7" }, { name = "django-jazzmin", specifier = ">=3.0.1" }, { name = "django-nested-admin", specifier = ">=4.1.1" }, { name = "django-prose-editor", extras = ["sanitize"], specifier = ">=0.10.3" }, @@ -332,6 +357,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/a9/5c/bfd6bd0bf979426d405cc6e71eceb8701b148b16c21d2dc3c261efc61c7b/sqlparse-0.5.3-py3-none-any.whl", hash = "sha256:cf2196ed3418f3ba5de6af7e82c694a9fbdbfecccdfc72e281548517081f16ca", size = 44415, upload-time = "2024-12-10T12:05:27.824Z" }, ] +[[package]] +name = "tablib" +version = "3.8.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/09/cc/fe19d9c2ac1088794a51fc72f49b7226f88a0361f924fb3d17a9ec80e657/tablib-3.8.0.tar.gz", hash = "sha256:94d8bcdc65a715a0024a6d5b701a5f31e45bd159269e62c73731de79f048db2b", size = 122247, upload-time = "2025-01-22T15:29:27.276Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5c/95/6542f54ebd90539b12ed6189cb54a6550a28407b1c503c2e55190c29a4c9/tablib-3.8.0-py3-none-any.whl", hash = "sha256:35bdb9d4ec7052232f8803908f9c7a9c3c65807188b70618fa7a7d8ccd560b4d", size = 47935, upload-time = "2025-01-22T15:28:44.499Z" }, +] + [[package]] name = "typing-extensions" version = "4.12.2"