Объединять и фильтровать несвязанные модели - PullRequest
0 голосов
/ 21 апреля 2019

У меня есть несколько типов транспортных средств (автомобили, самолеты, поезда) с разными моделями, и я хочу иметь конечную точку API, которая даст мне фильтруемый список всех моделей вместе взятых.

Я изменил метод списка ViewSet, чтобы объединить сериализованные результаты каждой модели ModelViewSet и обработать их через одну конечную точку. Формат ответа правильный, но я хочу его отфильтровать.

Я опробовал конечную точку с примерно 50 000 случайно сгенерированных транспортных средств (равномерно распределенных по моделям), и нефильтрованный ответ может быть очень медленным.

Текущий ответ Формат:

{
  "Car": [
    {
      "url": "http://127.0.0.1:8000/myapp/api/cars/1/",
      "name": "Mitsuoka",
      "ground_clearance": null,
      "road_legal": true,
      "active": true,
      "bought_by": null
    }
  ],
  "Train": [
    {
      "url": "http://127.0.0.1:8000/myapp/api/trains/1/",
      "name": "What",
      "freight_capacity": null,
      "wheel_config": null,
      "active": false,
      "bought_by": null
    },
    {
      "url": "http://127.0.0.1:8000/myapp/api/trains/2/",
      "name": "Manufacturer",
      "freight_capacity": null,
      "wheel_config": null,
      "active": false,
      "bought_by": null
    }
  ],
  "Plane": []
}

Пример желаемых фильтров:

  • Только автомобили
  • Только транспортные средства (автомобили, самолеты и поезда) со статусом = Актив

Код

models.py

from django.db import models

class Buyer(models.Model):
    name = models.CharField(unique=True, max_length = 20)

class Car(models.Model):
    name = models.CharField(unique=True, max_length = 50)
    ground_clearance = models.CharField(unique=True, max_length = 50, null=True, blank=True)
    road_legal = models.BooleanField(default=True)

    active = models.BooleanField(default=True)
    bought_by = models.ForeignKey(Buyer, null=True, blank=True, to_field="name", related_name="cars")

class Plane(models.Model):
    name = models.CharField(unique=True, max_length = 50)
    max_altitude = models.CharField(unique=True, max_length = 50, null=True, blank=True)
    cruising_speed = models.CharField(unique=True, max_length = 50, null=True, blank=True)

    active = models.BooleanField(default=True)
    bought_by = models.ForeignKey(Buyer, null=True, blank=True, to_field="name", related_name="planes")

class Train(models.Model):
    name = models.CharField(unique=True, max_length = 50)
    freight_capacity = models.CharField(unique=True, max_length = 50, null=True, blank=True)
    wheel_config = models.CharField(unique=True, max_length = 50, null=True, blank=True)

    active = models.BooleanField(default=True)
    bought_by = models.ForeignKey(Buyer, null=True, blank=True, to_field="name", related_name="trains")

views.py

from rest_framework import viewsets
from rest_framework.response import Response
from myapp import models, serializers

class ListVehicles(viewsets.ViewSet):

    def list(self, request, format=None):

        models_serializers = {
          models.Car: serializers.CarSerializer,
          models.Plane: serializers.PlaneSerializer,
          models.Train: serializers.TrainSerializer,
        }

        cars = models.Car.objects.select_related('bought_by')
        trains = models.Train.objects.select_related('bought_by')
        planes = models.Plane.objects.select_related('bought_by')

        queries_list = list()
        queries_list.extend((cars, planes, trains))

        serializer_context = {'request': request}

        serialized_querysets = dict()

        for queryset in queries_list:
          for model, serializers in models_serializers.items():
            if queryset.model == model:
            serialized_querysets[model.__name] = serializer(queryset, context=serializer_context, many=True).data

        return Response(serialized_querysets)

...
# for each vehicle there also exists an individual ModelViewSet, e.g.,:
class CarViewSet(viewsets.ModelViewSet):
    '''
    API endpoint that allows Cars to be viewed or edited.
    '''

    queryset = models.Car.objects.select_related('bought_by')
    serializer_class = serializers.CarSerializer
    filterset_fields = ('bought_by','active')
...
...

Я изменил этот метод , чтобы выполнить итерацию по Queryset и добавить в словарь serialized_querysets вместо итерации по каждому экземпляру модели и добавления в список serialized_querysets. При использовании больших наборов данных итерации по каждому экземпляру модели для трех типов транспортных средств становятся очень медленными.

serializers.py

from rest_framework import serializers
from myapp import models

class CarSerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
        model = models.Car
        fields = '__all__'
        extra_kwargs = {'url': {'view_name': 'myapp:car-detail'}}

class TrainSerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
        model = models.Train
        fields = '__all__'
        extra_kwargs = {'url': {'view_name': 'myapp:train-detail'}}

class PlaneSerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
        model = models.Plane
        fields = '__all__'
        extra_kwargs = {'url': {'view_name': 'myapp:plane-detail'}}

urls.py

from myapp import views
from django.urls import include, path
from rest_framework import routers

router = routers.DefaultRouter()
router.register(r'vehicles', views.ListVehicles, base_name='vehicles')

...
...
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...