diff --git a/apis_core/api_routers.py b/apis_core/api_routers.py index 56a87368f..c83d6a372 100644 --- a/apis_core/api_routers.py +++ b/apis_core/api_routers.py @@ -1,6 +1,4 @@ -from dataclasses import dataclass from functools import reduce -from ast import literal_eval try: from apis_ontology.models import * @@ -469,199 +467,3 @@ def dispatch(self, request, *args, **kwargs): # filter_classes = dict() # lst_filter_classes_check = [] generic_serializer_creation_factory() - - -@dataclass -class AdditionalSerializerConfig: - """ - use this class in a list named 'additional_serializers_list' to load custom - serializers - """ - - url: str # For now, it is recommended to prefix the url with 'additional' - name: str # unique name for django - path_structure: dict # see example structure - - -def load_additional_serializers(): - try: - additional_serializers_list = getattr( - importlib.import_module("apis_ontology.additional_serializers"), - "additional_serializers_list", - ) - except: - return [] - # imports had to be done here, because if imported at top, python would mistake class 'InheritanceForwardManyToOneDescriptor' - # from different module apis_metainfo. No idea how this is even possible or could be properly fixed. - from django.db.models.query_utils import DeferredAttribute - from django.db.models.fields.related_descriptors import ( - ForwardManyToOneDescriptor, - ReverseManyToOneDescriptor, - ManyToManyDescriptor, - ) - from django.db.models.base import ModelBase - from apis_core.apis_relations.models import InheritanceForwardManyToOneDescriptor - - def create_additional_viewset(path_structure): - def create_additional_serializer(path_structure): - if len(path_structure.keys()) != 1: - raise Exception() - target = list(path_structure.keys())[0] - model_fields = list(path_structure.values())[0] - if type(target) is ModelBase: - model_class = target - elif ( - type(target) is ForwardManyToOneDescriptor - or type(target) is InheritanceForwardManyToOneDescriptor - or type(target) is ManyToManyDescriptor - ): - model_class = target.field.related_model - elif type(target) is ReverseManyToOneDescriptor: - model_class = target.field.model - else: - raise Exception( - f"Unhandled case. Report to Stefan. type of field is: {type(target)}" - ) - meta_fields = [] - sub_serializers = {} - for field in model_fields: - if ( - type(field) is DeferredAttribute - or type(field) is ForwardManyToOneDescriptor - or type(field) is InheritanceForwardManyToOneDescriptor - ): - meta_fields.append(field.field.name) - # In case the referenced model class by the foreign key is parent class - # of designated target class, use the target class: - if field.field.model is not model_class and issubclass( - field.field.model, model_class - ): - model_class = field.field.model - elif type(field) is ReverseManyToOneDescriptor: - meta_fields.append(field.rel.related_name) - elif type(field) is dict: - target = list(field.keys())[0] - if type(target) is ManyToManyDescriptor: - target_name = target.field.name - is_many = True - elif ( - type(target) is ForwardManyToOneDescriptor - or type(target) is InheritanceForwardManyToOneDescriptor - ): - target_name = target.field.name - is_many = False - elif type(target) is ReverseManyToOneDescriptor: - target_name = target.rel.name - is_many = True - else: - raise Exception( - f"Unhandled case. Report to Stefan. type of field is: {type(field)}" - ) - sub_serializers[target_name] = create_additional_serializer(field)( - read_only=True, many=is_many - ) - field["path_self"] = target_name - meta_fields.append(target_name) - else: - raise Exception( - f"Unhandled case. Report to Stefan. type of field is: {type(field)}" - ) - - class AdditionalSerializer(serializers.ModelSerializer): - for item in sub_serializers.items(): - # Don't use temporary veriables here for the sub-serializer. Otherwise django would mistake - # the temporary variable as belonging to the parent serializer. Hence 'item[1]' - vars()[item[0]] = item[1] - - class Meta: - model = model_class - fields = meta_fields - - AdditionalSerializer.__name__ = ( - AdditionalSerializer.__qualname__ - ) = f"Additional{model_class.__name__.title().replace(' ', '')}Serializer" - - return AdditionalSerializer - - def construct_prefetch_path_set(path_structure): - path_set = set() - if type(path_structure) is dict: - path_current = path_structure.get("path_self") - for path_structure_sub in list(path_structure.values())[0]: - for path_sub in construct_prefetch_path_set(path_structure_sub): - if path_current is not None: - path_set.add(path_current + "__" + path_sub) - else: - path_set.add(path_sub) - if len(path_set) == 0: - if path_current is not None: - path_set.add(path_current) - else: - return set() - - return path_set - - def main(): - additional_serializer_class = create_additional_serializer(path_structure) - - class AdditionalViewSet(viewsets.ModelViewSet): - queryset = additional_serializer_class.Meta.model.objects.all() - for prefetch_path in construct_prefetch_path_set(path_structure): - queryset = queryset.prefetch_related(prefetch_path) - serializer_class = additional_serializer_class - - def get_queryset(self): - # TODO: Improve this param handling by extending the parsing logic - # or by forwarding the params untouched - # The original 'self.request.query_params' could not be forwarded - # directly to django's ORM filter So as a work-around, a dictionary - # is created and its values are casted. Maybe there a possibility - # can be found to forward the params directly? - params = {} - was_parsed = False - for k, v in self.request.query_params.items(): - # check for pagination params: - if k == "limit" or k == "offset": - continue - # check for int - try: - v = int(v) - except: - pass - else: - was_parsed = True - # check for boolean - if not was_parsed: - if v.lower() == "true": - v = True - was_parsed = True - elif v.lower() == "false": - v = False - was_parsed = True - # check for list - if not was_parsed: - if k.endswith("__in"): - try: - v = literal_eval(v) - except: - pass - else: - was_parsed = True - params[k] = v - - return self.queryset.filter(**params) - - AdditionalViewSet.__name__ = ( - AdditionalViewSet.__qualname__ - ) = f"Additional{additional_serializer_class.Meta.model.__name__.title().replace(' ', '')}ViewSet" - - return AdditionalViewSet - - return main() - - for additional_serializer in additional_serializers_list: - additional_serializer.viewset = create_additional_viewset( - additional_serializer.path_structure - ) - - return additional_serializers_list diff --git a/apis_core/urls.py b/apis_core/urls.py index 6faad5779..c120f4441 100644 --- a/apis_core/urls.py +++ b/apis_core/urls.py @@ -7,7 +7,6 @@ from django.views.generic import TemplateView from rest_framework import routers -from apis_core.api_routers import load_additional_serializers from apis_core.api_routers import views # from apis_core.apis_entities.api_views import ( @@ -38,13 +37,6 @@ # inject the manually created UriToObjectViewSet into the api router router.register(r"metainfo/uritoobject", UriToObjectViewSet, basename="uritoobject") -for additional_serializer in load_additional_serializers(): - router.register( - additional_serializer.url, - additional_serializer.viewset, - additional_serializer.name, - ) - # router.register(r"users", UserViewSet) # router.register(r"GeoJsonPlace", PlaceGeoJsonViewSet, "PlaceGeoJson") # router.register(r"NetJson", NetJsonViewSet, "NetJson")