在Meta中使用动态模型创建通用序列化程序

Rob*_*wen 13 python django python-3.x django-rest-framework

当我在django-rest0-framework中创建一个基于ModelSerializer的Serializer时,我将不得不在Meta类中传递模型:

class ClientSerializer(ModelSerializer):
    class Meta:
        model = Client
Run Code Online (Sandbox Code Playgroud)

我想创建一个通用的序列化程序,它基于URL动态地包含模型.

我的设置因此包括urls.py和viewset:

urls.py:

 url(r'^api/v1/general/(?P<model>\w+)', kernel_api_views.GeneralViewSet.as_view({'get':'list'}))
Run Code Online (Sandbox Code Playgroud)

和views.py:

class GeneralViewSet(viewsets.ModelViewSet):

     def get_queryset(self):
            # Dynamically get the model class from myapp.models
            queryset = getattr(myapp.models, model).objects.all()
            return queryset

     def get_serializer_class(self):
         return getattr(myapp.serializers, self.kwargs['model']+'Serializer')
Run Code Online (Sandbox Code Playgroud)

其中有:http://127.0.0.1:8000 /api/v1/general/Client将Client.objects.all()作为queryset,将ClientSerializer类作为序列化程序

问题:如何制作它以便我可以调用'GeneralSerializer'并动态分配模型?

Rah*_*pta 17

您可以通过以下方式实现:

serializers.py

class GeneralSerializer(serializers.ModelSerializer):

    class Meta:
        model = None
Run Code Online (Sandbox Code Playgroud)

views.py

class GeneralViewSet(viewsets.ModelViewSet):

     def get_queryset(self):
         model = self.kwargs.get('model')
         return model.objects.all()           

     def get_serializer_class(self):
         GeneralSerializer.Meta.model = self.kwargs.get('model')
         return GeneralSerializer  
Run Code Online (Sandbox Code Playgroud)

serializers.py,我们定义GeneralSerializermodelMeta作为None.我们将model在调用时覆盖该值get_serializer_class().

然后在我们的views.py文件中,我们定义一个GeneralViewSetwith get_queryset()get_serializer_class()overridden.

get_queryset(),我们获取modelfrom 的值kwargs并返回该查询集.

get_serializer_class(),我们将modelfor GeneralSerializer的值设置为从中获得的值kwargs然后返回GeneralSerializer.

  • 对于将来看到此问题的任何人来说,此解决方案不是线程安全的,因为您正在更改“GeneralSerializer.Meta.model”属性并且该属性在执行之间保持不变。如果您使用多个线程运行实例,则可能会遇到一些难以调试的问题。 (4认同)

bra*_*der 6

到目前为止,我知道如果使用模型序列化器,则无法创建通用序列化器,但是可以使用基类并从该基类派生所有模型来获得相同的解决方案。实现一种方法以返回序列化器,然后使用该方法生成动态序列化器。在过去的两年中,我一直在使用这项技术,并且对我来说还算不错-

class BaseModel(models.Model):
    class Meta:
         abstract = True # define abstract so that it does not cause any problem with model hierarchy in database

    @classmethod
    def get_serializer(cls):
         class BaseSerializer(serializers.ModelSerializer):
               class Meta:
                    model = cls # this is the main trick here, this is how I tell the serializer about the model class

         return BaseSerializer #return the class object so we can use this serializer
Run Code Online (Sandbox Code Playgroud)

现在从中导出模型-

class Derived1(BaseModel):
    pass

class Derived2(BaseModel):
    pass
Run Code Online (Sandbox Code Playgroud)

如果要覆盖串行器,则只需在所需的序列器中进行即可。例如 -

class DerivedOverride(BaseModel):
    @classmethod
    def get_serializer(cls):
         super_serializer = BaseModel.get_serializer() # this important to not to break the serializing hierarchy
         class BaseSerializer(super_serializer):
               class Meta:
                    model = cls # this is the main trick here, this is how I tell the serializer about the model class

         return BaseSerializer
Run Code Online (Sandbox Code Playgroud)

就是这样,现在每个类都有自己的动态序列化器,但我们只是在一个地方定义了它。

现在在视图集中使用序列化器-

class Derive1ViewSet(ModelViewSet):
    serializer_class = Derived1.get_serializer()

class Derive2ViewSet(ModelViewSet):
    serializer_class = Derived2.get_serializer()
Run Code Online (Sandbox Code Playgroud)

然后从那里继续。