diff options
| -rw-r--r-- | archaeological_operations/tests.py | 4 | ||||
| -rw-r--r-- | ishtar_common/admin.py | 64 | ||||
| -rw-r--r-- | ishtar_common/data_importer.py | 8 | ||||
| -rw-r--r-- | ishtar_common/forms_common.py | 16 | ||||
| -rw-r--r-- | ishtar_common/migrations/0260_itemkey_importer_type.py | 62 | ||||
| -rw-r--r-- | ishtar_common/models_common.py | 33 | ||||
| -rw-r--r-- | ishtar_common/models_imports.py | 138 | ||||
| -rw-r--r-- | ishtar_common/serializers.py | 19 | ||||
| -rw-r--r-- | ishtar_common/serializers_utils.py | 97 | 
9 files changed, 336 insertions, 105 deletions
diff --git a/archaeological_operations/tests.py b/archaeological_operations/tests.py index 18e17cca5..26217d7c3 100644 --- a/archaeological_operations/tests.py +++ b/archaeological_operations/tests.py @@ -202,7 +202,7 @@ class ImportTest(BaseImportTest):      def init_ope_targetkey(self, imp):          # doing manually connections -        q = Q(importer=imp) | Q(user=imp.user) +        q = Q(ishtar_import=imp) | Q(user=imp.user) | Q(importer_type=imp.importer_type)          if imp.associated_group:              q |= Q(group=imp.associated_group)          for ik in ItemKey.objects.filter(q).all(): @@ -565,7 +565,7 @@ class ImportOperationTest(ImportTest, TestCase):          other_imp = form.save(self.ishtar_user)          # re-associate with another import -        q = Q(importer=impt) | Q(user=impt.user) +        q = Q(ishtar_import=impt) | Q(user=impt.user) | Q(importer_type=impt.importer_type)          if impt.associated_group:              q |= Q(group=impt.associated_group)          for ik in ItemKey.objects.filter(q).all(): diff --git a/ishtar_common/admin.py b/ishtar_common/admin.py index 7b1aa48d4..a2dfc63aa 100644 --- a/ishtar_common/admin.py +++ b/ishtar_common/admin.py @@ -281,23 +281,25 @@ def serialize_action(dir_name, model_list):          else:              modellist = [modeladmin.model]          opts = modeladmin.model._meta +        if getattr(modeladmin, "serialize_filter_queryset", None): +            queryset = queryset.filter(**modeladmin.serialize_filter_queryset)          result = generic_get_results(              modellist, dir_name, result_queryset={opts.object_name: queryset}          )          basename = str(opts).replace(".", "_")          in_memory = BytesIO() -        zip = zipfile.ZipFile(in_memory, "a") +        current_zip = zipfile.ZipFile(in_memory, "a")          for key in result.keys():              __, model_name = key -            zip.writestr(dir_name + os.sep + model_name + ".json", result[key]) +            current_zip.writestr(dir_name + os.sep + model_name + ".json", result[key])          # info -        zip.writestr("info.json", json.dumps(serialization_info(), indent=2)) +        current_zip.writestr("info.json", json.dumps(serialization_info(), indent=2))          # fix for Linux zip files read in Windows -        for file in zip.filelist: -            file.create_system = 0 -        zip.close() +        for cfile in current_zip.filelist: +            cfile.create_system = 0 +        current_zip.close()          response = HttpResponse(content_type="application/zip")          response["Content-Disposition"] = "attachment; filename={}.zip".format(basename)          in_memory.seek(0) @@ -2124,15 +2126,11 @@ class ImporterGroupImporterInline(admin.TabularInline):      extra = 3 -serialize_importer_group_action = serialize_action("common_imports", IMPORT_MODEL_LIST) -serialize_importer_group_action.short_description = SERIALIZE_DESC - -  @admin.register(models.ImporterGroup, site=admin_site)  class ImporterGroupAdmin(ImportJSONActionAdmin):      list_display = ("name", "importer_types_label", "available")      actions = [ -        serialize_importer_group_action, +        serialize_importer_action,          change_value("available", True, _("Make available")),          change_value("available", False, _("Make unavailable")),      ] @@ -2359,6 +2357,7 @@ class ImportAdmin(admin.ModelAdmin):          "creation_date",      )      autocomplete_fields = ["user"] +    search_fields = ("name", "importer_type__name")  admin_site.register(models.Import, ImportAdmin) @@ -2377,23 +2376,6 @@ class ImportGroupAdmin(admin.ModelAdmin):      autocomplete_fields = ["user"] -class TargetKeyGroupAdmin(admin.ModelAdmin): -    list_display = ("name", "all_user_can_use", "all_user_can_modify", "available") -    search_fields = ("name",) - - -admin_site.register(models.TargetKeyGroup, TargetKeyGroupAdmin) - - -class TargetKeyAdmin(admin.ModelAdmin): -    list_display = ("target", "importer_type", "column_nb", "key", "value", "is_set") -    list_filter = ("is_set", "target__column__importer_type") -    search_fields = ("target__target", "value", "key") - - -admin_site.register(models.TargetKey, TargetKeyAdmin) - -  class OperationTypeAdmin(GeneralTypeAdmin):      extra_list_display = ["order", "preventive"]      model = models.OperationType @@ -2410,12 +2392,30 @@ class SpatialReferenceSystemAdmin(GeneralTypeAdmin):  admin_site.register(models.SpatialReferenceSystem, SpatialReferenceSystemAdmin) -class ItemKeyAdmin(admin.ModelAdmin): -    list_display = ("content_type", "key", "content_object", "importer") -    search_fields = ("key",) +@admin.register(models.ItemKey, site=admin_site) +class ItemKeyAdmin(ImportJSONActionAdmin): +    list_display = ( +        "content_type", "content_type_model", "key", "content_object", "linked_to_all", +        "linked_to_importer_type", "linked_to_importer", "linked_to_user" +    ) +    search_fields = ("key", "content_type__model") +    list_filter = (("content_type", admin.RelatedOnlyFieldListFilter),) +    autocomplete_fields = ["user", "ishtar_import"] +    actions = [ +        serialize_type_action +    ] +    serialize_filter_queryset = { +        "user__isnull": True, +        "group__isnull": True, +        "ishtar_import__isnull": True +    } -admin_site.register(models.ItemKey, ItemKeyAdmin) +@admin.register(models.TargetKey, site=admin_site) +class TargetKeyAdmin(admin.ModelAdmin): +    list_display = ("target", "importer_type", "column_nb", "key", "value", "is_set") +    list_filter = ("is_set", "target__column__importer_type") +    search_fields = ("target__target", "value", "key")  @admin.register(models.ImportColumnValue, site=admin_site) diff --git a/ishtar_common/data_importer.py b/ishtar_common/data_importer.py index defea4da8..0c58e04f7 100644 --- a/ishtar_common/data_importer.py +++ b/ishtar_common/data_importer.py @@ -536,7 +536,7 @@ class StrChoiceFormater(Formater, ChoiceChecker):      def new(self, value):          return -    def add_key(self, obj, value, importer=None): +    def add_key(self, obj, value, ishtar_import=None):          return      def format(self, value): @@ -575,14 +575,14 @@ class TypeFormater(StrChoiceFormater):          if self.import_instance:              for item in model.objects.all():                  self.choices.append((item.pk, str(item))) -                for key in item.get_keys(importer=import_instance): +                for key in item.get_keys(current_import=import_instance):                      self.equiv_dict[key] = item      def prepare(self, value):          return slugify(str(value).strip()) -    def add_key(self, obj, value, importer=None): -        obj.add_key(slugify(value), force=True, importer=importer) +    def add_key(self, obj, value, ishtar_import=None): +        obj.add_key(slugify(value), force=True, ishtar_import=ishtar_import)      def new(self, value):          values = copy.copy(self.defaults) diff --git a/ishtar_common/forms_common.py b/ishtar_common/forms_common.py index 3fa819a8f..187941bb6 100644 --- a/ishtar_common/forms_common.py +++ b/ishtar_common/forms_common.py @@ -605,6 +605,7 @@ class TargetKeyForm(forms.ModelForm):              ]          if self.user.is_superuser:              choices += [("all", _("all users"))] +            choices += [("all-importers", _("all users and importers"))]          self.fields["remember"].choices = choices          self.fields["remember"].widget.attrs["class"] = "auto"          self.remember_choices = choices @@ -625,7 +626,7 @@ class TargetKeyForm(forms.ModelForm):      def save(self, commit=True):          try: -            super(TargetKeyForm, self).save(commit) +            super().save(commit)          except ImporterError:              return          if not self.cleaned_data.get("value") or not self.user: @@ -644,23 +645,28 @@ class TargetKeyForm(forms.ModelForm):          )          remember = self.cleaned_data.get("remember") +        self.instance.associated_import = self.associated_import +        force_importer_type, force_all = False, False          if remember == "import" and self.associated_import: -            self.instance.associated_import = self.associated_import              self.instance.associated_user = None              self.instance.associated_group = None          elif remember == "group" and can_edit_group: -            self.instance.associated_import = None              self.instance.associated_user = None              self.instance.associated_group = self.associated_import.associated_group          elif remember == "all" and self.user.is_superuser: -            self.instance.associated_import = None              self.instance.associated_user = None              self.instance.associated_group = None +            force_importer_type = True +        elif remember == "all-importers" and self.user.is_superuser: +            self.instance.associated_user = None +            self.instance.associated_group = None +            force_all = True          else:              # for me! -            self.instance.associated_import = None              self.instance.associated_user = self.user.ishtaruser              self.instance.associated_group = None +        self.instance.create_itemkey(force_importer_type=force_importer_type, +                                     force_all=force_all)          self.instance.save() diff --git a/ishtar_common/migrations/0260_itemkey_importer_type.py b/ishtar_common/migrations/0260_itemkey_importer_type.py new file mode 100644 index 000000000..789f22162 --- /dev/null +++ b/ishtar_common/migrations/0260_itemkey_importer_type.py @@ -0,0 +1,62 @@ +# Generated by Django 2.2.24 on 2025-01-30 15:55 + +from django.db import migrations, models, connection +import django.db.models.deletion + + +def migrate_import_type(apps, __): +    ItemKey = apps.get_model("ishtar_common", "ItemKey") +    Import = apps.get_model("ishtar_common", "Import") + +    with connection.cursor() as cursor: +        cursor.execute("SELECT id, importer_id FROM ishtar_common_itemkey WHERE importer_id IS NOT NULL;") +        for ik_id, import_id in cursor.fetchall(): +            importer_type_id = Import.objects.get(pk=import_id).importer_type_id +            ik = ItemKey.objects.get(pk=ik_id) +            ik.importer_type_id = importer_type_id +            ik.save() + + +class Migration(migrations.Migration): + +    dependencies = [ +        ('ishtar_common', '0259_ishtarsiteprofile_default_location_for_treatment'), +    ] + +    operations = [ +        migrations.AlterModelOptions( +            name='targetkey', +            options={'ordering': ('target', 'key'), 'verbose_name': 'Import - Target key', 'verbose_name_plural': 'Import - Targets keys'}, +        ), +        migrations.AddField( +            model_name='itemkey', +            name='importer_type', +            field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='ishtar_common.ImporterType'), +        ), +        migrations.AlterField( +            model_name='ishtarsiteprofile', +            name='default_location_for_treatment', +            field=models.ForeignKey(blank=True, help_text='If provided, treatment forms have by default this location set. Furthermore if this location has an organization attached, this organization is set by default.', null=True, on_delete=django.db.models.deletion.SET_NULL, to='archaeological_warehouse.Warehouse', verbose_name='Default location for treatments'), +        ), +        migrations.AlterField( +            model_name='ishtarsiteprofile', +            name='find_external_id', +            field=models.TextField(default='{{get_first_base_find__context_record__external_id}}-{{label}}{% if upstream_count %}-{{upstream_count}}{% endif %}', help_text='Formula to manage find external ID. Change this with care. With incorrect formula, the application might be unusable and import of external data can be destructive.', verbose_name='Find external id'), +        ), +        migrations.AlterField( +            model_name='ishtarsiteprofile', +            name='no_context_button', +            field=models.ForeignKey(blank=True, help_text='If provided, a button is displayed on find add page to create a "No context" find', null=True, on_delete=django.db.models.deletion.SET_NULL, to='archaeological_context_records.ContextRecord', verbose_name='Context record for no context button'), +        ), +        migrations.RunPython(migrate_import_type), +        migrations.RenameField( +            model_name='itemkey', +            old_name='importer', +            new_name='ishtar_import' +        ), +        migrations.AlterField( +            model_name='itemkey', +            name='ishtar_import', +            field=models.ForeignKey(blank=True, help_text='Specific key to an import', null=True, on_delete=django.db.models.deletion.CASCADE, to='ishtar_common.Import'), +        ), +    ] diff --git a/ishtar_common/models_common.py b/ishtar_common/models_common.py index ed7cae234..4bea24ab5 100644 --- a/ishtar_common/models_common.py +++ b/ishtar_common/models_common.py @@ -675,22 +675,28 @@ class GeneralType(Cached, models.Model):          self.generate_key(force=True)          return obj -    def add_key(self, key, force=False, importer=None, group=None, user=None): +    def add_key(self, key, force=False, importer_type=None, ishtar_import=None, +                group=None, user=None):          ItemKey = apps.get_model("ishtar_common", "ItemKey")          content_type = ContentType.objects.get_for_model(self.__class__)          if ( -            not importer +            not ishtar_import              and not force              and ItemKey.objects.filter(key=key, content_type=content_type).count()          ):              return          filtr = {"key": key, "content_type": content_type} -        if group: +        if importer_type: +            filtr["importer_type"] = importer_type +        elif group:              filtr["group"] = group +            filtr["importer_type"] = ishtar_import.importer_type if ishtar_import else None          elif user:              filtr["user"] = user +            filtr["importer_type"] = ishtar_import.importer_type if ishtar_import else None          else: -            filtr["importer"] = importer +            filtr["ishtar_import"] = ishtar_import +            filtr["importer_type"] = ishtar_import.importer_type if ishtar_import else None          if force:              ItemKey.objects.filter(**filtr).exclude(object_id=self.pk).delete()          filtr["object_id"] = self.pk @@ -700,18 +706,23 @@ class GeneralType(Cached, models.Model):          for key in (slugify(self.label), self.txt_idx):              self.add_key(key) -    def get_keys(self, importer): +    def get_keys(self, current_import): +        importer_type = current_import.importer_type          ItemKey = apps.get_model("ishtar_common", "ItemKey")          keys = [self.txt_idx]          content_type = ContentType.objects.get_for_model(self.__class__)          base_q = Q(content_type=content_type, object_id=self.pk) -        subquery = Q(importer__isnull=True, user__isnull=True, group__isnull=True) -        subquery |= Q(user__isnull=True, group__isnull=True, importer=importer) -        if importer.user: -            subquery |= Q(user=importer.user, group__isnull=True, importer=importer) -        if importer.associated_group: +        subquery = Q(importer_type__isnull=True, user__isnull=True, +                     group__isnull=True) +        subquery |= Q(user__isnull=True, group__isnull=True, +                      importer_type=importer_type) +        if current_import.user: +            subquery |= Q(user=current_import.user, group__isnull=True, +                          importer_type=importer_type) +        if current_import.associated_group:              subquery |= Q( -                user__isnull=True, group=importer.associated_group, importer=importer +                user__isnull=True, group=current_import.associated_group, +                importer_type=importer_type              )          q = ItemKey.objects.filter(base_q & subquery)          for ik in q.exclude(key=self.txt_idx).all(): diff --git a/ishtar_common/models_imports.py b/ishtar_common/models_imports.py index a33b599c7..5bd230d6b 100644 --- a/ishtar_common/models_imports.py +++ b/ishtar_common/models_imports.py @@ -964,7 +964,7 @@ class TargetKeyGroup(models.Model):  class TargetKey(models.Model):      """      User's link between import source and ishtar database. -    Also temporary used for GeneralType to point missing link before adding +    Used for GeneralType to point missing link before adding      them in ItemKey table.      A targetkey connection can be create to be applied to one particular      import (associated_import), one particular user (associated_user), @@ -975,17 +975,17 @@ class TargetKey(models.Model):      target = models.ForeignKey(          ImportTarget, related_name="keys", on_delete=models.CASCADE      ) -    key = models.TextField(_("Key")) +    key = models.TextField(_("Key"), blank=True, default="")      value = models.TextField(_("Value"), blank=True, null=True)      is_set = models.BooleanField(_("Is set"), default=False)      associated_import = models.ForeignKey( -        "Import", blank=True, null=True, on_delete=models.SET_NULL +        "Import", blank=True, null=True, on_delete=models.CASCADE      )      associated_user = models.ForeignKey( -        "IshtarUser", blank=True, null=True, on_delete=models.SET_NULL +        "IshtarUser", blank=True, null=True, on_delete=models.CASCADE      )      associated_group = models.ForeignKey( -        TargetKeyGroup, blank=True, null=True, on_delete=models.SET_NULL +        TargetKeyGroup, blank=True, null=True, on_delete=models.CASCADE      )      class Meta: @@ -995,8 +995,8 @@ class TargetKey(models.Model):              "associated_user",              "associated_import",          ) -        verbose_name = _("Importer - Target key") -        verbose_name_plural = _("Importer - Targets keys") +        verbose_name = _("Import - Target key") +        verbose_name_plural = _("Import - Targets keys")          ordering = ("target", "key")      ADMIN_SECTION = _("Imports") @@ -1022,34 +1022,36 @@ class TargetKey(models.Model):              return          return self.value -    def save(self, *args, **kwargs): -        obj = super(TargetKey, self).save(*args, **kwargs) +    def create_itemkey(self, force_importer_type=False, force_all=False):          if not self.value: -            return obj +            return          v = None          associated_model = self.target.associated_model -        if associated_model and hasattr(self.target.associated_model, "add_key"): -            # pk is given +        if not associated_model or not hasattr(self.target.associated_model, "add_key"): +            return +        # pk is given +        try: +            v = self.target.associated_model.objects.get(pk=str(int(self.value))) +        except (ValueError, self.target.associated_model.DoesNotExist): +            # try with txt_idx              try: -                v = self.target.associated_model.objects.get(pk=str(int(self.value))) -            except (ValueError, self.target.associated_model.DoesNotExist): -                # try with txt_idx -                try: -                    v = self.target.associated_model.objects.get( -                        txt_idx=str(self.value) -                    ) -                except self.target.associated_model.DoesNotExist: -                    pass -            if v: -                keys = {} -                if self.associated_group: -                    keys["group"] = self.associated_group -                if self.associated_user: -                    keys["user"] = self.associated_user -                else: -                    keys["importer"] = self.associated_import -                v.add_key(self.key, **keys) -        return obj +                v = self.target.associated_model.objects.get( +                    txt_idx=str(self.value) +                ) +            except self.target.associated_model.DoesNotExist: +                return +        if force_all: +            keys = {} +        elif force_importer_type: +            keys = {"importer_type": self.associated_import.importer_type} +        else: +            keys = {"ishtar_import": self.associated_import} +            if self.associated_user: +                keys["user"] = self.associated_user +            elif self.associated_group: +                keys["group"] = self.associated_group +        v.add_key(self.key, **keys) +        return  TARGET_MODELS = [ @@ -2919,28 +2921,86 @@ class ImportColumnValue(models.Model):  class ItemKey(models.Model): +    """ +    Importers: links Key -> Item +    """      key = models.TextField(_("Key"))      content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE) -    object_id = models.PositiveIntegerField() +    object_id = models.PositiveIntegerField(verbose_name=_("Value ID"))      content_object = GenericForeignKey("content_type", "object_id") -    importer = models.ForeignKey( +    importer_type = models.ForeignKey( +        ImporterType, +        null=True, +        blank=True, +        verbose_name=_("Importer type"), +        on_delete=models.CASCADE, +    ) +    ishtar_import = models.ForeignKey(          Import,          null=True,          blank=True, -        help_text=_("Specific key to an import"), -        on_delete=models.SET_NULL, +        verbose_name=_("Import"), +        on_delete=models.CASCADE,      )      user = models.ForeignKey( -        "IshtarUser", blank=True, null=True, on_delete=models.SET_NULL +        "IshtarUser", blank=True, null=True, on_delete=models.SET_NULL, +        verbose_name=_("User")      )      group = models.ForeignKey( -        TargetKeyGroup, blank=True, null=True, on_delete=models.SET_NULL +        TargetKeyGroup, blank=True, null=True, on_delete=models.SET_NULL, +        verbose_name=_("Group")      )      class Meta: -        verbose_name = _("Import - Item key") -        verbose_name_plural = _("Import - Item keys") +        verbose_name = _("Importer - Item key") +        verbose_name_plural = _("Importer - Item keys")      ADMIN_SECTION = _("Imports")      def __str__(self):          return self.key + +    @property +    def content_slug(self): +        item = self.content_object +        if hasattr(item, "txt_idx"): +            key = "txt_idx" +        elif hasattr(item, "slug"): +            key = "slug" +        else: +            key = "pk" +        return getattr(item, key) + +    def content_type_model(self): +        return self.content_type.model +    content_type_model.short_description = _("Content type model") + +    def linked_to_all(self): +        return not self.importer_type and not self.user and not self.group \ +            and not self.ishtar_import + +    linked_to_all.boolean = True +    linked_to_all.short_description = _("Linked to all") + +    def linked_to_importer_type(self): +        return bool(self.importer_type) + +    linked_to_importer_type.boolean = True +    linked_to_importer_type.short_description = _("Linked to importer") + +    def linked_to_importer(self): +        return bool(self.ishtar_import) + +    linked_to_importer.boolean = True +    linked_to_importer.short_description = _("Linked to import") + +    def linked_to_user(self): +        return bool(self.user) + +    linked_to_user.boolean = True +    linked_to_user.short_description = _("Linked to user") + +    def linked_to_group(self): +        return bool(self.group) + +    linked_to_group.boolean = True +    linked_to_group.short_description = _("Linked to group") diff --git a/ishtar_common/serializers.py b/ishtar_common/serializers.py index 1469e13a1..3cbe20907 100644 --- a/ishtar_common/serializers.py +++ b/ishtar_common/serializers.py @@ -17,11 +17,12 @@ from .models_common import State, Department  from archaeological_operations.models import ActType  from ishtar_common.serializers_utils import ( -    generic_get_results,      archive_serialization, +    CUSTOM_SERIALIZERS, +    generic_get_results,      generic_archive_files, -    SERIALIZATION_VERSION,      get_model_from_filename, +    SERIALIZATION_VERSION,  )  from archaeological_operations.serializers import ( @@ -466,7 +467,15 @@ def restore_serialized(                          or releasing_locks                          or (user and historized)                      ) -                    idx = -1 +                    idx = None +                    model_name = f"{model._meta.app_label}.{model._meta.model_name}" +                    if model_name in CUSTOM_SERIALIZERS: +                        current_serializer = CUSTOM_SERIALIZERS[model_name]() +                        for idx, values in enumerate(json.loads(data)): +                            current_serializer.save(values["fields"]) +                        if idx is not None: +                            result.append((model._meta.verbose_name, idx + 1)) +                        continue                      for idx, obj in enumerate(deserialize("json", data)):                          extra_attrs = {}                          if historized or hasattr(model, "locked"): @@ -514,6 +523,6 @@ def restore_serialized(                                  obj.lock_user = None                              obj._no_move = True                              obj.save() -                    if idx >= 0: -                        result.append((model.__name__, idx + 1)) +                    if idx is not None: +                        result.append((model._meta.verbose_name, idx + 1))          return result diff --git a/ishtar_common/serializers_utils.py b/ishtar_common/serializers_utils.py index ada0a62cf..a4494d9c9 100644 --- a/ishtar_common/serializers_utils.py +++ b/ishtar_common/serializers_utils.py @@ -4,9 +4,12 @@ import datetime  import json  import importlib  import os +from rest_framework.renderers import JSONRenderer +from rest_framework import serializers  import tempfile  from zipfile import ZipFile +from django.contrib.contenttypes.models import ContentType  from django.contrib.sites.models import Site  from django.core.serializers import serialize  from django.db.models import Q @@ -113,6 +116,70 @@ def archive_serialization(      return archive_name +class ItemKeySerializer(serializers.ModelSerializer): +    app = serializers.SerializerMethodField() +    model = serializers.SerializerMethodField() +    content_slug = serializers.SerializerMethodField() +    importer_type = serializers.SerializerMethodField() + +    class Meta: +        model = models.ItemKey +        fields = ['key', 'app', 'model', 'content_slug', 'importer_type'] + +    def get_content_slug(self, obj): +        return obj.content_slug + +    def get_app(self, obj): +        return obj.content_type.app_label + +    def get_model(self, obj): +        return obj.content_type.model + +    def get_importer_type(self, obj): +        return obj.importer_type.slug if obj.importer_type else "" + +    def save(self, data): +        try: +            ct = ContentType.objects.get( +                app_label=data["app"], +                model=data["model"], +            ) +        except ContentType.DoesNotExist: +            return +        model = ct.model_class() +        if hasattr(model, "txt_idx"): +            slug = "txt_idx" +        elif hasattr(model, "slug"): +            slug = "slug" +        else: +            slug = "pk" +        try: +            value = model.objects.get(**{slug: data["content_slug"]}) +        except model.DoesNotExist: +            return +        importer_type = None +        if data["importer_type"]: +            try: +                importer_type = models.ImporterType.objects.get( +                    slug=data["importer_type"] +                ) +            except models.ImporterType.DoesNotExist: +                return +        obj, created = models.ItemKey.objects.get_or_create( +            key=data["key"], +            content_type=ct, +            importer_type=importer_type, +            ishtar_import=None, +            user=None, +            group=None, +            defaults={"object_id": value.pk} +        ) +        if not created: +            obj.object_id = value.pk +            obj.save() +        return obj + +  GENERIC_QUERYSET_FILTER = {      "JsonDataSection": {"JsonDataField": "json_data_field__pk__in"},      "Regexp": { @@ -170,6 +237,8 @@ GENERIC_QUERYSET_FILTER = {      }  } +CUSTOM_SERIALIZERS = {"ishtar_common.itemkey": ItemKeySerializer} +  def generic_get_results(      model_list, dirname, no_geo=True, result_queryset=None, serialization_include=None @@ -212,13 +281,26 @@ def generic_get_results(              q = q.filter(**{recursion + "__isnull": True})          key = (dirname, model_name) -        result[key] = serialize( -            "json", -            q.distinct().all(), -            indent=2, -            use_natural_foreign_keys=True, -            use_natural_primary_keys=True, -        ) +        model_name = f"{model._meta.app_label}.{model._meta.model_name}" +        is_custom = model_name in CUSTOM_SERIALIZERS +        if is_custom: +            current_serializer = CUSTOM_SERIALIZERS[model_name] +            result[key] = JSONRenderer().render( +                [{"model": model_name, +                  "fields": current_serializer(item).data} +                 for item in q.distinct().all()] +            ) +        else: +            result[key] = serialize( +                "json", +                q.distinct().all(), +                indent=2, +                use_natural_foreign_keys=True, +                use_natural_primary_keys=True, +            ) +        if recursion and is_custom: +            # TODO +            raise NotImplementedError("Recursion not managed for this custom serializer")          if recursion:              serialized = [item["id"] for item in q.values("id").all()] @@ -278,6 +360,7 @@ def generic_get_results(              for k in serialization_include[model.__name__]:                  if k in excluded_fields:                      excluded_fields.pop(excluded_fields.index(k)) +          if excluded_fields:              new_result = json.loads(result[key])              for idx in range(len(new_result)):  | 
