【问题标题】:Create a generic serializer with a dynamic model in Meta在 Meta 中创建具有动态模型的通用序列化程序
【发布时间】:2015-08-30 03:43:56
【问题描述】:

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

class ClientSerializer(ModelSerializer):
    class Meta:
        model = Client

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

到目前为止,我的设置包括 urls.py 和视图集:

urls.py:

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

和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')

注意事项:http://127.0.0.1:8000/api/v1/general/Client 将 Client.objects.all() 作为查询集,将 ClientSerializer 类作为序列化程序

问题:我如何才能调用“GeneralSerializer”并在其中动态分配模型?

【问题讨论】:

    标签: python django python-3.x django-rest-framework


    【解决方案1】:

    您可以通过以下方式做到这一点:

    serializers.py

    class GeneralSerializer(serializers.ModelSerializer):
    
        class Meta:
            model = None
    

    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  
    

    serializers.py 中,我们将GeneralSerializer 定义为Meta 中的modelNone。我们将在调用 get_serializer_class() 时覆盖 model 值。

    然后在我们的views.py 文件中,我们定义一个GeneralViewSet 并覆盖get_queryset()get_serializer_class()

    get_queryset() 中,我们从kwargs 获取model 的值并返回该查询集。

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

    【讨论】:

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

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

    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
    

    现在从中导出你的模型 -

    class Derived1(BaseModel):
        pass
    
    class Derived2(BaseModel):
        pass
    

    如果您想覆盖序列化程序,那么只需在您需要的那个中执行即可。例如 -

    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
    

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

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

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

    然后从那里继续。

    【讨论】:

      【解决方案3】:

      以 Rahul 的回答为基础,这对我有用:

      urls.py

      url(r'^api/(?P<app_label>\w+)/(?P<model_name>\w+)', GeneralViewSet.as_view({'get': 'list'}))
      

      序列化器.py

      from rest_framework import serializers
      class GeneralSerializer(serializers.ModelSerializer):
      
          class Meta:
              model = None
      

      views.py

      from django.apps import apps        
      class GeneralViewSet(viewsets.ModelViewSet):
      
          @property
          def model(self):
              return apps.get_model(app_label=str(self.kwargs['app_label']), model_name=str(self.kwargs['model_name']))
      
          def get_queryset(self):
              model = self.model
              return model.objects.all()           
      
          def get_serializer_class(self):
              GeneralSerializer.Meta.model = self.model
              return GeneralSerializer
      

      【讨论】:

        【解决方案4】:

        除了@Rahul 和@btal,如果我们想使用APIView,我们可以在ModelSerializer 上使用装饰器模式。

        def getGenericSerializer(model_arg):
            class GenericSerializer(serializers.ModelSerializer):
                class Meta:
                    model = model_arg
                    fields = '__all__'
        
            return GenericSerializer
        

        并在 APIView 中像这样使用它:

        class MyView(APIView):
            def get(self, request, format=None):
                #...
                GenericSzl = getGenericSerializer(model)
                serializer = GenericSzl(objs, many=True)
                return Response(serializer.data)
        

        希望这对那些不想使用ModelViewSet 的人有所帮助。

        【讨论】:

          【解决方案5】:

          对于 Django 3.0+

          from yourapp import models
          
          class GeneralViewSet(viewsets.ModelViewSet):
          
               def get_queryset(self):
                   model = self.kwargs.get('model')
                   return getattr(models, model).objects.all()         
          
               def get_serializer_class(self):
                   model = self.kwargs.get('model')
                   GeneralSerializer.Meta.model =  getattr(models, model)
                   return GeneralSerializer  
          

          【讨论】:

            【解决方案6】:

            Meta 中创建不带model 的通用序列化程序:

            class GeneralModelSerializer(serializers.ModelSerializer):
                ...
            

            model添加到``:

            中的序列化程序中
            class GenericViewSet(viewsets.ModelViewSet):
            
                def get_serializer_class(self):
                    serializer_class = GeneralModelSerializer
                    serializer_class.Meta.model = YourModel
                    return serializer_class
            

            【讨论】:

              【解决方案7】:

              我的解决方案:

              创建带有或不带有模型的通用序列化程序,在 url 中发送模型名称

              插件:django-geojson==2.12.0 django:2.0.6 蟒蛇:3.6.7

              api.py

              from djgeojson.serializers import Serializer as GeoJSONSerializer
              from django.http import HttpResponse
              from django.db import connection
              
              
              def to_geojson(request):
                      model = request.GET['model']
                      print(model)
                      lista = []
                      with connection.cursor() as cursor:
                          cursor.execute("SELECT * FROM %s" % (model))
                          # to dict
                          lista = dictfetchall(cursor)
                          # Serialize
                          geo_lista = GeoJSONSerializer().serialize(lista)
                      return HttpResponse(geo_lista)
              
              
                  def dictfetchall(cursor):
                      "Return all rows from a cursor as a dict"
                      columns = [col[0] for col in cursor.description]
                      return [
                          dict(zip(columns, row))
                          for row in cursor.fetchall()
                      ]
              

              urls.py

              url(r'^api/geo/', to_geojson, name='to_geojson')
              

              我调用 API 的网址: 在models.py中使用模型

              http://127.0.0.1:8000/data/api/geo/?model=data_pointcloud
              

              models.py 中没有模型

              http://127.0.0.1:8000/data/api/geo/?model="schema".table_name
              

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2016-11-21
                • 1970-01-01
                • 1970-01-01
                • 2017-01-14
                • 2023-03-08
                相关资源
                最近更新 更多