Создайте универсальный сериализатор с динамической моделью в Meta
когда я создаю сериализатор в django-rest0-framework на основе ModelSerializer, мне нужно будет передать модель в мета-класс:
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() в качестве набора запросов и класс ClientSerializer в качестве сериализатора
Вопрос: Как я могу сделать так, чтобы я мог вызвать 'GeneralSerializer' и динамически назначить модель в нем?
Ответы
Ответ 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
с model
в Meta
как None
. Мы переопределим значение model
во время вызова get_serializer_class()
.
Тогда в нашем views.py
файле мы определяем GeneralViewSet
с get_queryset()
и get_serializer_class()
переопределены.
В get_queryset()
мы получаем значение model
из kwargs
и возвращаем этот запрос.
В get_serializer_class()
мы устанавливаем значение model
для GeneralSerializer
в значение, полученное из kwargs
а затем возвращаем GeneralSerializer
.
Ответ 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
То есть, теперь у каждого класса есть свой собственный динамический сериализатор, но мы просто определили его в одном месте.
Теперь используйте сериализатор в представлении set -
class Derive1ViewSet(ModelViewSet):
serializer_class = Derived1.get_serializer()
class Derive2ViewSet(ModelViewSet):
serializer_class = Derived2.get_serializer()
и продолжайте оттуда.
Ответ 3
Чтобы построить ответ Рахула, это то, что сработало для меня:
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
Ответ 4
Создать общий сериализатор без 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
Мое решение:
Создать общий сериализатор с моделью или без нее, отправить название модели в URL
плагин: django-geojson == 2.12.0
Джанго: 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')
мои 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
Ответ 6
В дополнение к @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
.