Source code for apis_core.apis_entities.filtersets

import django_filters
from django.conf import settings
from django.contrib.contenttypes.models import ContentType
from django.db import models
from django.db.models import Case, Q, Value, When
from django.db.models.functions import Concat
from django.forms import DateInput
from simple_history.utils import get_history_manager_for_model

from apis_core.apis_entities.utils import get_entity_classes
from apis_core.apis_metainfo.models import RootObject
from apis_core.apis_relations.models import Property, Triple
from apis_core.generic.filtersets import GenericFilterSet, GenericFilterSetForm
from apis_core.generic.helpers import generate_search_filter

ABSTRACT_ENTITY_COLUMNS_EXCLUDE = [
    "rootobject_ptr",
    "self_contenttype",
    "triple_set_from_subj",
    "triple_set_from_obj",
    "uri",
]

ABSTRACT_ENTITY_FILTERS_EXCLUDE = ABSTRACT_ENTITY_COLUMNS_EXCLUDE + [
    "review",
    "start_date",
    "start_start_date",
    "start_end_date",
    "end_date",
    "end_start_date",
    "end_end_date",
    "notes",
    "text",
    "published",
    "status",
    "references",
]


[docs] class PropertyChoiceField(django_filters.fields.ModelChoiceField):
[docs] def label_from_instance(self, obj): if obj.forward: targets = [ct.name for ct in obj.obj_class.all()] else: targets = [ct.name for ct in obj.subj_class.all()] return obj.name + " | " + ", ".join(targets)
[docs] class PropertyFilter(django_filters.ModelChoiceFilter): """ A child of ModelChoiceFilter that only works with Properties, but in return it filters those so that only the Properties are listed that can be connected to the `model` given as argument. """ field_class = PropertyChoiceField def __init__(self, *args, **kwargs): model = kwargs.pop("model", None) super().__init__(*args, **kwargs) if model is not None: ct = ContentType.objects.get_for_model(model) self.queryset = ( Property.objects.all() .filter(Q(subj_class=ct) | Q(obj_class=ct)) .annotate( name=Case( When( obj_class=ct, subj_class=ct, then=Concat("name_forward", Value(" / "), "name_reverse"), ), When(obj_class=ct, then="name_reverse"), When(subj_class=ct, then="name_forward"), ), forward=Case( When(obj_class=ct, then=Value(False)), When(subj_class=ct, then=Value(True)), ), ) .order_by("name") .distinct() )
[docs] def filter(self, queryset, value): if value: p = Property.objects.get(name_forward=value) queryset = queryset.filter(triple_set_from_subj__prop=p).distinct() return queryset
[docs] class ModelSearchFilter(django_filters.CharFilter): """ This filter is a customized CharFilter that uses the `generate_search_filter` method to adapt the search filter to the model that is searched. It also extracts sets the help text based on the fields searched. """ def __init__(self, *args, **kwargs): model = kwargs.pop("model", None) super().__init__(*args, **kwargs) if model is not None and "help_text" not in self.extra: if hasattr(model, "_default_search_fields"): fields = model._default_search_fields else: modelfields = model._meta.fields fields = [ field.name for field in modelfields if isinstance(field, (models.CharField, models.TextField)) ] fields = ", ".join(fields) self.extra["help_text"] = f"Search in fields: {fields}"
[docs] def filter(self, qs, value): return qs.filter(generate_search_filter(qs.model, value))
[docs] def changed_since(queryset, name, value): history = get_history_manager_for_model(queryset.model) ids = history.filter(history_date__gt=value).values_list("id", flat=True) return queryset.filter(pk__in=ids)
[docs] class AbstractEntityFilterSetForm(GenericFilterSetForm): columns_exclude = ABSTRACT_ENTITY_COLUMNS_EXCLUDE
[docs] class AbstractEntityFilterSet(GenericFilterSet): related_entity = django_filters.CharFilter( method=related_entity, label="Related entity contains" )
[docs] class Meta(GenericFilterSet.Meta): form = AbstractEntityFilterSetForm exclude = ABSTRACT_ENTITY_FILTERS_EXCLUDE filter_overrides = { models.CharField: { "filter_class": django_filters.CharFilter, "extra": lambda f: { "lookup_expr": "icontains", }, }, }
def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.filters["related_property"] = PropertyFilter( label="Related Property", model=getattr(self.Meta, "model", None) ) if model := getattr(self.Meta, "model", False): self.filters["search"] = ModelSearchFilter(model=model) self.filters.move_to_end("search", False) if "apis_core.history" in settings.INSTALLED_APPS: self.filters["changed_since"] = django_filters.DateFilter( label="Changed since", widget=DateInput(attrs={"type": "date"}), method=changed_since, )