【问题标题】:Django dynamically generated serializerDjango 动态生成序列化器
【发布时间】:2021-02-24 20:01:12
【问题描述】:

有没有办法动态生成 Django REST 框架序列化器?

考虑到这一点:

    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)

我想知道下面的例子是否可行:

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)
       })
    })

或者有没有其他方法可以生成 DRF 序列化器?

【问题讨论】:

    标签: python django django-rest-framework metaclass django-serializer


    【解决方案1】:

    这是为给定的Model 构建ModelSerializer 的函数(使用 Django 3.2、DRF 3.12.4 和 Python 3.8 测试):

    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
    

    如果您确定每个序列化程序只调用一次此函数,则可以省略 @lru_cache 以保留一些内存。

    示例用法:

    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)
    

    将所有模型的序列化程序添加到当前模块的命名空间:

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

    【讨论】:

      【解决方案2】:

      您的方法会奏效 - 但要让 Django 找到这些序列化程序,您必须将它们分配给模块命名空间。

      在您的代码中,您只需调用type - 序列化程序类被创建并立即“丢弃”。即使基础 Django 类 serializers.ModelSerializer 在注册表中保留对它的引用,您也无法导入序列化程序并使用它们。

      您所要做的就是将它们添加到globals() 返回的字典中。同样,您为该类创建的命名空间也必须是一个字典 - 因为您正在调用“type”但实际上并未将其名称指定为“Meta”,所以您创建了一个集合,而不是字典,并且您对 type 的调用将失败就像它期待一本字典一样。

      所以,我没有检查代码是否真的可以工作,但是基于你的想法是:

      from django.apps import apps
      
      models = [model for model in apps.get_models()]
      
      for model in models:
          name = f"{model.__name__}Serializer"
          globals()[name] =  type(name,(serializers.ModelSerializer,),{
             "Meta": type("Meta",(),{
                 "model":model,
                 "fields": get_all_model_fields(model)
             })
          })
      del models, model, name
      

      【讨论】:

      • 谢谢,我试试。 del models, model, name 有什么用?你为什么这样做?
      • 不需要 - 只是因为这些变量是在模块根目录中创建的,作为创建将要使用的类的工具,所以它们在战后就没有用了。使用del 语句,导入模块的人将看不到它们 - 并且命名空间保持清洁。但它确实是化妆品。
      猜你喜欢
      • 2014-08-12
      • 1970-01-01
      • 1970-01-01
      • 2017-09-25
      • 2020-08-30
      • 2020-10-25
      • 1970-01-01
      • 2019-12-13
      • 2020-07-09
      相关资源
      最近更新 更多