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

时间:2015-06-14 16:22:15

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

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

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

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

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

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()作为queryset,将ClientSerializer类作为序列化程序

问题:我怎样才能这样做,以便我可以打电话给GeneralSerializer&#39;并动态分配模型?

7 个答案:

答案 0 :(得分:12)

您可以通过以下方式完成此操作:

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 model Meta定义为None model。我们会在致电get_serializer_class()时覆盖views.py值。

然后在我们的GeneralViewSet文件中,我们定义get_queryset(),其中get_serializer_class()get_queryset()被覆盖。

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

model中,我们将GeneralSerializer的{​​{1}}值设置为从kwargs获取的值,然后返回GeneralSerializer

答案 1 :(得分:4)

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

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

然后继续。

答案 2 :(得分:2)

以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

答案 3 :(得分:1)

除了@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的人有所帮助。

答案 4 :(得分:0)

model中创建没有Meta的常规序列化器:

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

答案 5 :(得分:0)

我的解决方案:

创建带有或不带有模型的常规序列化器,在url中发送模型名称

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

我的网址来调用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

答案 6 :(得分:0)

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