Search code examples
pythondjangodjango-rest-frameworkmetaclassdjango-serializer

Django dynamically generated serializer


Is there a way how to dynamically generate a Django rest framework serializers?

Considering this:

    class BlogSerializer(serializers.ModelSerializer):
    
        class Meta:
            model = models.Blog
            fields = get_all_model_fields(models.Blog)
    
    class PostSerializer(serializers.ModelSerializer):
    
        class Meta:
            model = models.Post
            fields = get_all_model_fields(models.Post)
     
    
    class UserSerializer(serializers.ModelSerializer):
    
        class Meta:
            model = models.User
            fields = get_all_model_fields(models.User)

I am wondering if something like following example could be possible:

from django.apps import apps

models = [model for model in apps.get_models()]

for model in models:
    type(model.__name__+'Serializer',(serializers.ModelSerializer,),{
       type("Meta",(),{
           "model":model,
           "fields": get_all_model_fields(model)
       })
    })

Or is there any other way how to generate DRF serializers?


Solution

  • Here's a function to build a ModelSerializer for a given Model (tested with Django 3.2, DRF 3.12.4 and Python 3.8):

    import types
    from functools import lru_cache
    from typing import Type
    
    from django.db import models
    from rest_framework import serializers
    
    @lru_cache(maxsize=0)
    def model_serializer(model: Type[models.Model]) -> Type[serializers.ModelSerializer]:
        meta_class = types.new_class("Meta")
        setattr(meta_class, "model", model)
        setattr(meta_class, "fields", "__all__")
        result = types.new_class(
            model.__name__ + "Serializer", (serializers.ModelSerializer,), {}
        )
        setattr(result, "Meta", meta_class)
        return result
    

    If you are certain that you will call this function only once for each serializer, you can omit the @lru_cache to preserve some memory.

    Example usage:

    class MyModel(models.Model):
        some = models.CharField(max_length=123)
        other = models.IntegerField()
    
    MyModelSerializer = model_serializer(MyModel)
    my_serializer = MyModelSerializer({"some": "abc", "other": 1234})
    my_serializer.is_valid(True)
    

    To add the serializers for all your models to the current module's namespace:

    from django.apps import apps
    
    for model in apps.get_models():
        serializer_type = model_serializer(model)
        globals()[serializer_type.__name__] = serializer_type