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

Create a generic serializer with a dynamic model in Meta

当我在 django-rest0-framework 中基于 ModelSerializer 创建序列化程序时,我将不得不在 Meta 中传递模型 class:

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 class 作为序列化程序

问题:我怎样才能调用'GeneralSerializer'并在其中动态分配模型?

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

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

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中,我们将Meta中有modelGeneralSerializer定义为None。我们将在调用 get_serializer_class().

时覆盖 model

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

get_queryset()中,我们从kwargs和return那个查询集中获得了model的值。

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

到目前为止,我知道如果您使用模型序列化器,您将无法创建通用序列化器,但您可以使用基 class 并从该基 class 派生所有模型来获得相同的解决方案。实现 return 序列化程序的方法,然后使用该方法生成动态序列化程序。在过去的 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 都有自己的动态序列化器,但我们只是在一个地方定义它。

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

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

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

然后从那里继续。

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

urls.py

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

serializers.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

我的解决方案:

创建带或不带模型的通用序列化程序,在 url

中发送模型名称

插件:django-geojson==2.12.0 姜戈:2.0.6 python: 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')

我的 url 打电话给 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

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

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

对于 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