在 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
中有model
的GeneralSerializer
定义为None
。我们将在调用 get_serializer_class()
.
时覆盖 model
值
然后在我们的 views.py
文件中,我们定义了一个 GeneralViewSet
并覆盖了 get_queryset()
和 get_serializer_class()
。
在get_queryset()
中,我们从kwargs
和return那个查询集中获得了model
的值。
在get_serializer_class()
中,我们将GeneralSerializer
的model
的值设置为从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
当我在 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
中有model
的GeneralSerializer
定义为None
。我们将在调用 get_serializer_class()
.
model
值
然后在我们的 views.py
文件中,我们定义了一个 GeneralViewSet
并覆盖了 get_queryset()
和 get_serializer_class()
。
在get_queryset()
中,我们从kwargs
和return那个查询集中获得了model
的值。
在get_serializer_class()
中,我们将GeneralSerializer
的model
的值设置为从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