Создайте универсальный сериализатор с динамической моделью в 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.