diff options
| -rw-r--r-- | ishtar_common/admin.py | 205 | ||||
| -rw-r--r-- | ishtar_common/models_common.py | 17 | 
2 files changed, 185 insertions, 37 deletions
| diff --git a/ishtar_common/admin.py b/ishtar_common/admin.py index 5ca7ef105..7a621f601 100644 --- a/ishtar_common/admin.py +++ b/ishtar_common/admin.py @@ -836,6 +836,172 @@ class PermissionRequestAdmin(admin.ModelAdmin):      form = PermissionRequestAdminForm +def get_content_types_with_sheet(with_empty=False): +    choices = [] +    for ct in ContentType.objects.all(): +        klass = ct.model_class() +        if not klass: +            continue +        if getattr(klass, "SHOW_URL", None): +            choices.append((ct.pk, ct.name)) +    lst = list( +        sorted(choices, key=lambda x: x[1]) +    ) +    if with_empty: +        lst = [("", "-" * 9)] + lst +    return lst + + +class ContentTypeChoice: +    def set_content_types_choices(self, field_name): +        self.fields[field_name].choices = get_content_types_with_sheet() + + +class ContentTypeListFilter(admin.SimpleListFilter): +    # Only display content types with sheet attached +    title = _("content type") +    parameter_name = 'content_type' + +    def lookups(self, request, model_admin): +        return get_content_types_with_sheet(with_empty=False) + +    def queryset(self, request, queryset): +        value = self.value() +        if value: +            query = {f"{self.parameter_name}_id": self.value()} +            return queryset.filter(**query) + + +class BaseSheetFilterForm(forms.ModelForm, ContentTypeChoice): +    content_type_field = "" +    key = forms.CharField( +        label=_("Key"), +        initial="-", +        help_text=_("Save first to choose a key"), +        max_length=200, +    ) + +    def _get_content_type_model(self, instance): +        raise NotImplementedError() + +    def __init__(self, *args, **kwargs): +        super().__init__(*args, **kwargs) +        self.set_content_types_choices(self.content_type_field) +        instance = kwargs.get("instance") +        if not instance: +            return +        if args: +            query_dict = args[0] +            query_dict._mutable = True +            query_dict.setlist( +                self.content_type_field, +                [getattr(instance, self.content_type_field).pk] +            ) +            query_dict._mutable = False +        self.fields[self.content_type_field].widget.attrs = {"disabled": "disabled"} +        keys = instance.get_keys() +        model = self._get_content_type_model(instance) +        help_text = [] +        for key in keys: +            try: +                field = model._meta.get_field(key) +            except FieldDoesNotExist: +                field = None +            if field and getattr(field, "verbose_name", None): +                key += f" ({field.verbose_name})" +            help_text.append(key) +        self.fields["key"].help_text = str(_("Available keys: ")) + " ; ".join( +            help_text +        ) + + +class SheetFilterForm(BaseSheetFilterForm): +    class Meta: +        model = models_common.SheetFilter +        exclude = [] + +    content_type_field = "content_type" + +    def _get_content_type_model(self, instance): +        return instance.content_type.model_class() + + +class ContentTypeSearchAdmin: +    def search_results(self, queryset, search_term): +        # search with real model name +        # not efficient but searching only in ~10 models +        search_terms = search_term.split(" ") +        for ct in ContentType.objects.all(): +            klass = ct.model_class() +            if not klass: +                continue +            if getattr(klass, "SHOW_URL", None): +                name = ct.name.lower() +                for idx, k in enumerate(reversed(search_terms[:])): +                    if k in name: +                        queryset |= self.model.objects.filter( +                            content_type_id=ct.pk) +        return queryset + +    def get_search_results(self, request, queryset, search_term): +        queryset, may_have_duplicates = super().get_search_results( +            request, +            queryset, +            search_term, +        ) +        queryset = self.search_results(queryset, search_term) +        return queryset, may_have_duplicates + + +@admin.register(models_common.SheetFilter, site=admin_site) +class SheetFilterAdmin(ContentTypeSearchAdmin, admin.ModelAdmin): +    form = SheetFilterForm +    model = models_common.SheetFilter +    list_display = ("content_type", "exclude_or_include", "key") +    list_filter = (ContentTypeListFilter,) +    search_fields = ("key",) + + +class FilteredSheetForm(forms.ModelForm, ContentTypeChoice): +    class Meta: +        model = models_common.FilteredSheet +        exclude = [] + +    def __init__(self, *args, **kwargs): +        super().__init__(*args, **kwargs) +        self.set_content_types_choices("content_type") + +    def clean(self): +        filters = self.cleaned_data.get("filters", []) +        content_type = self.cleaned_data.get("content_type", None) +        if not filters: +            return self.cleaned_data +        exc = None +        for filtr in filters.all(): +            if content_type != filtr.content_type: +                raise forms.ValidationError( +                    _("Bad filter configuration. " +                      "Only use filters of the associacted content type.") +                ) +            if not exc: +                exc = filtr.exclude_or_include +            elif exc != filtr.exclude_or_include: +                raise forms.ValidationError( +                    _("You cannot mix exclude and include filters.") +                ) +        return self.cleaned_data + + +@admin.register(models_common.FilteredSheet, site=admin_site) +class FilteredSheetAdmin(ContentTypeSearchAdmin, admin.ModelAdmin): +    form = FilteredSheetForm +    model = models_common.FilteredSheet +    list_display = ("name", "content_type") +    list_filter = (ContentTypeListFilter,) +    autocomplete_fields = ("filters",) +    search_fields = ("name",) + +  MAIN_ITEM_READONLY_FIELDS = [      "history_creator",      "history_modifier", @@ -1659,7 +1825,7 @@ admin_site.register(models.Area, AreaAdmin)  class ProfileTypeAdmin(GeneralTypeAdmin):      model = models.ProfileType      filter_vertical = ("groups",) -    autocomplete_fields = ("permission_requests",) +    autocomplete_fields = ("permission_requests", "filtered_sheets")      def save_related(self, request, form, formsets, change):          super().save_related(request, form, formsets, change) @@ -2927,47 +3093,20 @@ class ApiKeyMatchAdmin(admin.ModelAdmin):  admin_site.register(models_rest.ApiKeyMatch, ApiKeyMatchAdmin) -class ApiSheetFilterForm(forms.ModelForm): +class ApiSheetFilterForm(BaseSheetFilterForm):      api_search_model = forms.ModelChoiceField(          models_rest.ApiSearchModel.objects,          label=_("API - Remote access - Search model"),      ) -    key = forms.CharField( -        label=_("Key"), -        initial="-", -        help_text=_("Save first to choose a key"), -        max_length=200, -    )      class Meta:          model = models_rest.ApiSheetFilter          fields = ["api_search_model", "key"] -    def __init__(self, *args, **kwargs): -        super().__init__(*args, **kwargs) -        instance = kwargs.get("instance") -        if not instance: -            return -        if args: -            query_dict = args[0] -            query_dict._mutable = True -            query_dict.setlist("api_search_model", [instance.api_search_model.pk]) -            query_dict._mutable = False -        self.fields["api_search_model"].widget.attrs = {"disabled": "disabled"} -        keys = instance.get_keys() -        model = instance.api_search_model.content_type.model_class() -        help_text = [] -        for key in keys: -            try: -                field = model._meta.get_field(key) -            except FieldDoesNotExist: -                field = None -            if field and getattr(field, "verbose_name", None): -                key += f" ({field.verbose_name})" -            help_text.append(key) -        self.fields["key"].help_text = str(_("Available keys: ")) + " ; ".join( -            help_text -        ) +    content_type_field = "api_search_model" + +    def _get_content_type_model(self, instance): +        return instance.api_search_model.content_type.model_class()  class ApiSheetFilterAdmin(admin.ModelAdmin): diff --git a/ishtar_common/models_common.py b/ishtar_common/models_common.py index 613c54fac..cd74ca068 100644 --- a/ishtar_common/models_common.py +++ b/ishtar_common/models_common.py @@ -885,10 +885,16 @@ class SheetFilter(BaseSheetFilter):      )      class Meta: -        verbose_name = _("Sheet filter") -        verbose_name_plural = _("Sheet filters") +        verbose_name = _("Filtered sheet - Filter") +        verbose_name_plural = _("Filtered sheet - Filters")      ADMIN_SECTION = _("Account") +    def __str__(self): +        exc = _("exclude") +        if self.exclude_or_include == "I": +            exc = _("include") +        return f"{self.content_type.model_class()._meta.verbose_name} | {exc} | {self.key}" +      def get_template(self):          ct = self.content_type          model = apps.get_model(ct.app_label, ct.model) @@ -907,10 +913,13 @@ class FilteredSheet(models.Model):      )      class Meta: -        verbose_name = _("Filtered sheet") -        verbose_name_plural = _("Filtered sheets") +        verbose_name = _("Filtered sheet - Sheet") +        verbose_name_plural = _("Filtered sheet - Sheets")      ADMIN_SECTION = _("Account") +    def __str__(self): +        return f"{self.content_type.model_class()._meta.verbose_name} | {self.name}" +  class FullSearch(models.Model):      search_vector = SearchVectorField( | 
