From 90b65123f6740eb6c5683f92f0c4d304c656c37b Mon Sep 17 00:00:00 2001 From: Étienne Loks Date: Tue, 18 Feb 2025 12:25:56 +0100 Subject: ✨ admin: improve Next/Previous buttons, importer columns - set default importer type and col number MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- archaeological_warehouse/models.py | 18 +-- ishtar_common/admin.py | 157 ++++++++++++++----------- ishtar_common/templates/admin/change_form.html | 2 + 3 files changed, 103 insertions(+), 74 deletions(-) diff --git a/archaeological_warehouse/models.py b/archaeological_warehouse/models.py index aa97e412a..be4349121 100644 --- a/archaeological_warehouse/models.py +++ b/archaeological_warehouse/models.py @@ -75,14 +75,14 @@ class DivisionContainer(DashboardFormItem): @property def pk(self): # of course implemented by models.Model - raise NotImplemented + raise NotImplementedError() def get_max_division_number(self): - raise NotImplemented + raise NotImplementedError() @property def start_division_number(self): - raise NotImplemented + raise NotImplementedError() @property def division_labels(self): @@ -812,8 +812,8 @@ class ContainerTree(models.Model): WHERE c.parent_id is NOT NULL UNION ALL SELECT p.container_id AS container_id, - c.parent_id as container_parent_id, - p.level + 1 + c.parent_id as container_parent_id, + p.level + 1 FROM archaeological_warehouse_container c, rel_tree p WHERE c.id = p.container_parent_id AND c.parent_id is NOT NULL @@ -1291,7 +1291,9 @@ class Container( return self.cached_label or "" def post_save_geo(self, save=True): - q_check_town = Warehouse.objects.filter(pk=self.location_id, precise_town_id__isnull=False) + q_check_town = Warehouse.objects.filter( + pk=self.location_id, precise_town_id__isnull=False + ) return warehouse_post_save_geo(self, q_check_town, save=save) @property @@ -1391,7 +1393,9 @@ class Container( if not self.pk: return False if self.calculated_weight != self._calculate_weight(): - Container.objects.filter(pk=self.pk).update(calculated_weight=self.calculated_weight) + Container.objects.filter(pk=self.pk).update( + calculated_weight=self.calculated_weight + ) return True return False diff --git a/ishtar_common/admin.py b/ishtar_common/admin.py index 556e7572d..c9393e57e 100644 --- a/ishtar_common/admin.py +++ b/ishtar_common/admin.py @@ -49,6 +49,7 @@ from django.contrib.gis.geos.error import GEOSException from django.core.cache import cache from django.core.exceptions import FieldError, FieldDoesNotExist from django.core.serializers import serialize +from django.db import connection from django.db.models import Q from django.db.models.fields import ( BooleanField, @@ -73,7 +74,7 @@ from ishtar_common import models, models_common, models_rest from ishtar_common.apps import admin_site from ishtar_common.model_merging import merge_model_objects from ishtar_common.utils import API_MAIN_CONTENT_TYPES, get_cache, create_slug,\ - get_person_gdpr_log + get_person_gdpr_log, InlineClass from ishtar_common import forms as common_forms, forms_common as other_common_forms from ishtar_common.serializers import restore_serialized, IMPORT_MODEL_LIST @@ -1534,69 +1535,23 @@ class TownAdmin(ImportGEOJSONActionAdmin, ImportActionAdmin): admin_site.register(models_common.Town, TownAdmin) -class GeneralTypeAdmin(ChangeParentAdmin, ImportActionAdmin, ImportJSONActionAdmin): - search_fields = ( - "label", - "txt_idx", - "comment", - ) - list_filter = ("available",) - save_on_top = True - actions = [ - export_as_csv_action(), - serialize_type_action, - change_value("available", True, _("Make available")), - change_value("available", False, _("Make unavailable")), - ] - prepopulated_fields = {"txt_idx": ("label",)} - LIST_DISPLAY = ["label", "txt_idx", "available", "comment"] - extra_list_display = [] - CSV_FIELD_ORDER = [ - "id", - "label", - "txt_idx", - "parent", - "order", - "available", - "comment", - ] - - def get_list_display(self, request): - list_display = list(self.LIST_DISPLAY)[:] - if hasattr(self.model, "parent"): - list_display.insert(2, "parent") - return list_display + self.extra_list_display - - @csrf_protect_m +class PreviousNextAdmin: def get_changelist_queryset(self, request): - """ - Get the changelist queryset to be used in the change view. - Used by previous and next button. - Mainly a copy from: - django/contrib/admin/options.py ModelAdmin->changelist_view - """ - list_display = self.get_list_display(request) - list_display_links = self.get_list_display_links(request, list_display) - list_filter = self.get_list_filter(request) - search_fields = self.get_search_fields(request) - list_select_related = self.get_list_select_related(request) - sortable_by = self.get_sortable_by(request) - - cl = ChangeListForChangeView( - request, - self.model, - list_display, - list_display_links, - list_filter, - self.date_hierarchy, - search_fields, - list_select_related, - self.list_per_page, - self.list_max_show_all, - self.list_editable, - self, - sortable_by, - ) + # adapt GET to a changelist_view style + get = request.GET.get("_changelist_filters", []) + if isinstance(get, str): + get = [get] + self._current_query = {} + for value in get: + if "=" not in value: + continue + k, v = value.split("=") + self._current_query[k] = v + fake_request = InlineClass( + {"GET": self._current_query, "user": request.user, + "resolver_match": request.resolver_match} + ) # only set attributes needed + cl = self.get_changelist_instance(fake_request) return cl.get_queryset(request) def change_view(self, request, object_id, form_url="", extra_context=None): @@ -1631,10 +1586,42 @@ class GeneralTypeAdmin(ChangeParentAdmin, ImportActionAdmin, ImportJSONActionAdm # on modify current object do not match current criteria # next is the first item extra_context["next_item"] = first + return super().change_view(request, object_id, form_url, extra_context) - return super(GeneralTypeAdmin, self).change_view( - request, object_id, form_url, extra_context - ) + +class GeneralTypeAdmin(PreviousNextAdmin, ChangeParentAdmin, ImportActionAdmin, + ImportJSONActionAdmin): + search_fields = ( + "label", + "txt_idx", + "comment", + ) + list_filter = ("available",) + save_on_top = True + actions = [ + export_as_csv_action(), + serialize_type_action, + change_value("available", True, _("Make available")), + change_value("available", False, _("Make unavailable")), + ] + prepopulated_fields = {"txt_idx": ("label",)} + LIST_DISPLAY = ["label", "txt_idx", "available", "comment"] + extra_list_display = [] + CSV_FIELD_ORDER = [ + "id", + "label", + "txt_idx", + "parent", + "order", + "available", + "comment", + ] + + def get_list_display(self, request): + list_display = list(self.LIST_DISPLAY)[:] + if hasattr(self.model, "parent"): + list_display.insert(2, "parent") + return list_display + self.extra_list_display general_models = [ @@ -2295,7 +2282,7 @@ class ImportTargetInline(admin.TabularInline): form = ImportTargetForm -class ImporterColumnAdmin(admin.ModelAdmin): +class ImporterColumnAdmin(PreviousNextAdmin, admin.ModelAdmin): list_display = ( "label", "importer_type", @@ -2318,6 +2305,42 @@ class ImporterColumnAdmin(admin.ModelAdmin): inlines = (ImportTargetInline, ImporterDuplicateFieldInline) actions = [duplicate_importercolumn, shift_left, shift_right] + def get_changeform_initial_data(self, request): + """ + Get current importer type and the first available column + """ + initial = super().get_changeform_initial_data(request) + base_query = self.get_changelist_queryset(request) + importer_type_id = None + if base_query.exists(): + importer_type_id = base_query.values_list( + "importer_type_id", flat=True).all()[0] + if not importer_type_id: + importer_type_id = self._current_query.get("importer_type__id__exact", None) + if importer_type_id and base_query.exclude( + importer_type_id=importer_type_id).exists(): + # not only one importer_type_id in current queryset + return initial + try: + initial["importer_type"] = models.ImporterType.objects.get( + id=importer_type_id) + except models.ImporterType.DoesNotExist: + return initial + + with connection.cursor() as cursor: + query = """ + SELECT min(col_number) + 1 FROM ishtar_common_importercolumn ic_a + WHERE importer_type_id = %s AND NOT EXISTS ( + SELECT col_number FROM ishtar_common_importercolumn ic_b + WHERE ic_b.col_number = ic_a.col_number + 1 + AND importer_type_id = %s + )""" + cursor.execute(query, [importer_type_id, importer_type_id]) + row = cursor.fetchone() + if row: + initial["col_number"] = row[0] + return initial + admin_site.register(models.ImporterColumn, ImporterColumnAdmin) diff --git a/ishtar_common/templates/admin/change_form.html b/ishtar_common/templates/admin/change_form.html index f5c83faa6..9195a3245 100644 --- a/ishtar_common/templates/admin/change_form.html +++ b/ishtar_common/templates/admin/change_form.html @@ -19,6 +19,7 @@ {% trans "History" %} {% if has_absolute_url %}
  • {% trans "View on site" %}
  • {% endif %} + {% if previous_item or next_item %}
  • {% if previous_item %} {% trans "Previous" %} @@ -36,4 +37,5 @@ {% endif %}
  • + {% endif %} {% endblock %} -- cgit v1.2.3