-
graphene-django
Build powerful, efficient, and flexible GraphQL APIs with seamless Django integration.
-
Scout Monitoring
Free Django app performance insights with Scout Monitoring. Get Scout setup in minutes, and let us sweat the small stuff. A couple lines in settings.py is all you need to start monitoring your apps. Sign up for our free tier today.
-
drf-flex-fields
Dynamically set fields and expand nested resources in Django REST Framework serializers.
it swallows errors, making debugging a mess
import difflib import importlib import logging from functools import lru_cache from django.db.models import ForeignKey, ManyToManyField from rest_flex_fields.serializers import FlexFieldsModelSerializer SERIALIZERS_DIR = "myapp.api.serializers" class MagicFlexFieldsModelSerializer(FlexFieldsModelSerializer): """ Magically expands all related fields by inspecting related fields for a model. """ def __init__(self, *args, **kwargs): self.expandable_fields = discover_expandable_fields(self.Meta.model) super().__init__(*args, **kwargs) def discover_expandable_fields(model): """ Magically assigns `expandable_fields` from all related model fields. Discovers the serializer class based on the related model name. Assigns expandable_fields: The end result looks like this: ``` { 'created_by': backend.api.serializers.user_serializer.UserSerializer, 'workspace': backend.api.serializers.workspace_serializer.WorkspaceSerializer, 'users': (backend.api.serializers.user_serializer.UserSerializer, {'many': True}), 'archived_by': (backend.api.serializers.user_serializer.UserSerializer, {'many': True}), 'notified': (backend.api.serializers.user_serializer.UserSerializer, {'many': True}), 'conversation_messages': (backend.api.serializers.message_serializer.MessageSerializer, {'many': True}) } ``` See the example at: https://github.com/rsinger86/drf-flex-fields?tab=readme-ov-file#quick-start """ expandable_fields = {} serializer_classes = find_serializer_classes() for field in model._meta.get_fields(): if isinstance(field, (ForeignKey, ManyToManyField)): related_model_name = field.related_model.__name__ serializer_cls = find_closest_matching_serializer_by_name( related_model_name, serializer_classes ) if field.related_model != serializer_cls.Meta.model: logging.debug( f"Unable to find the correct serializer class for: {field.related_model}" ) continue if isinstance(field, ForeignKey): expandable_fields[field.name] = serializer_cls else: expandable_fields[field.name] = ( serializer_cls, {"many": True}, ) for field in model._meta.related_objects: related_model_name = field.related_model.__name__ serializer_cls = find_closest_matching_serializer_by_name( related_model_name, serializer_classes ) if field.related_model != serializer_cls.Meta.model: logging.debug( f"Unable to find the correct serializer class for: {field.related_model}" ) continue accessor_name = field.get_accessor_name() expandable_fields[accessor_name] = (serializer_cls, {"many": True}) return expandable_fields def find_closest_matching_serializer_by_name(input_str, serializer_classes): matcher = difflib.SequenceMatcher(None, input_str, str(serializer_classes[0])) closest_match = serializer_classes[0] max_similarity = matcher.ratio() for cls in serializer_classes[1:]: matcher = difflib.SequenceMatcher(None, input_str, str(cls)) similarity = matcher.ratio() if similarity > max_similarity: closest_match = cls max_similarity = similarity return closest_match @lru_cache(maxsize=None) def find_serializer_classes(): """ Technically you could get this from the DRF router object via the registered views. """ init_module = importlib.import_module(SERIALIZERS_DIR) serializer_classes = [ getattr(init_module, name) for name in dir(init_module) if callable(getattr(init_module, name)) and "Serializer" in name ] return serializer_classes
Below is an example of an app with a user model and a many-to-many relation to an organization model (a user could be a member of many organizations). I've created an example repository to demo the implementation, but below is a snippet.