Оптимизация вложенных сериализаторов DRF

Господа, помогите, плиз, оптимизировать монструозный код в сериализаторе, который очень тормозит... Как я понимаю самая главная проблема в нескольких SerializerMethodField, где вызываются get_tasks(). Подскажите, пожалуйста, как правильно здесь поступить - в init подгрузить в self.tasks все таски, которые есть. И в методе to_representation все 4 ключа вызовов SerializerMethodField - equipment_types, recovery_days, completed_days, work_in_progress_days? И самое главное - как правильно оптимизировать вызов вложенных сериалайзеров?

class MonthPlanViewSerializer(serializers.HyperlinkedModelSerializer):
    year_plan_id = serializers.PrimaryKeyRelatedField(queryset=YearPlan.objects.all(), source='year_plan.id')
    equipment_id = serializers.PrimaryKeyRelatedField(queryset=Equipment.objects.all(), source='equipment.id',
                                                      required=False)
    equipment = EquipmentSerializer(many=False, read_only=True)
    transport_id = serializers.PrimaryKeyRelatedField(queryset=Transport.objects.all(), source='transport.id',
                                                      default=None)
    transport = TransportSerializer(many=False, read_only=True)
    equipment_types = serializers.SerializerMethodField()
    tasks = SimpleTaskSerializer(many=True, read_only=True)
    recovery_days = serializers.SerializerMethodField()
    completed_days = serializers.SerializerMethodField()
    work_in_progress_days = serializers.SerializerMethodField()

    @staticmethod
    def get_tasks(instance: MonthPlan):
        return instance.tasks.all()

    @staticmethod
    def get_work_in_progress_days(instance: MonthPlan) -> set:
        tasks = MonthPlanViewSerializer.get_tasks(instance)
        return set(int(task.planned_date.strftime("%d")) for task in tasks if task.work_in_progress)

    @staticmethod
    def get_completed_days(instance: MonthPlan) -> list:
        return MonthPlanViewSerializer.get_common_days(instance, 'is_completed')

    @staticmethod
    def get_recovery_days(instance: MonthPlan) -> list:
        return MonthPlanViewSerializer.get_common_days(instance, 'is_broken')

    @staticmethod
    def get_common_days(instance: MonthPlan, attribute: str) -> list:
        tasks = MonthPlanViewSerializer.get_tasks(instance)
        days = set()
        for task in tasks:
            for item in task.maintenance_executions:
                if getattr(item, attribute):
                    if task.closing_date:
                        days.add(int(task.closing_date.strftime("%d")))
                    elif task.planned_date:
                        days.add(int(task.planned_date.strftime("%d")))
        return list(days)

    @staticmethod
    def get_equipment_types(instance: MonthPlan):
        tasks = MonthPlanViewSerializer.get_tasks(instance)
        equipment_types = EquipmentType.objects.filter(
            id__in=Equipment.objects.filter(transport_id=instance.transport_id).values_list('equipment_type_id'))

        return EquipmentTypeSerializer(equipment_types, many=True).data

    class Meta:
        model = MonthPlan
        fields = [
            'id', 'year_plan_id', 'month', 'enabled',
            'equipment_id', 'equipment',
            'required_maintenance_quantity',
            'scheduled_maintenance_quantity', 'scheduled_days', 'scheduled_maintenance_quantity_over_year',
            'completed_maintenance_quantity', 'completed_days', 'completed_maintenance_quantity_over_year',
            'recovery_days', 'transport', 'transport_id',
        ]


class MaintenanceCheckTemplateSerializer(serializers.ModelSerializer):
    class Meta:
        model = MaintenanceCheckTemplate
        fields = ['id', 'title', 'description']


class MaintenanceExecutionCheckViewSerializer(serializers.ModelSerializer):
    check = MaintenanceCheckTemplateSerializer(many=False, source='template', read_only=True)

    class Meta:
        model = MaintenanceExecutionCheck
        fields = ['id', 'check', 'value']
        

class SimpleTaskSerializer(serializers.HyperlinkedModelSerializer):
    team_id = serializers.PrimaryKeyRelatedField(
        queryset=Team.objects.all(), source='team.id', allow_null=True
    )
    location = LocationSerializer(many=False, allow_null=True)
    state_id = serializers.PrimaryKeyRelatedField(queryset=State.objects.all(), source='state.id')
    equipment_type_id = serializers.PrimaryKeyRelatedField(
        queryset=EquipmentType.objects.all(), source="equipment_type.id", allow_null=True
    )
    equipment_type = EquipmentTypeSerializer(many=False, allow_null=True)
    maintenance_execution = MaintenanceExecutionSerializer(many=True, read_only=True)
    maintenance_executor = EmployeeSerializer(many=False, read_only=True)

    class Meta:
        model = Task
        fields = [
            'id', 'number', 'description', 'type', 'urgency',
            'location_id', 'location', 'service', 'state_id',
            'equipment_type_id', 'equipment_type', 'team_id', 'maintenance_execution', 'maintenance_executor'
        ]


class MaintenanceExecutionSerializer(serializers.ModelSerializer):
    equipment_type = EquipmentTypeSerializer(many=False, read_only=True)
    checklist = MaintenanceExecutionCheckViewSerializer(many=True)

    class Meta:
        model = MaintenanceExecution
        fields = ['id', 'equipment_type', 'is_completed', 'is_broken', 'comment', 'used_materials', 'checklist']


class EquipmentSerializer(serializers.ModelSerializer):
    location_id = serializers.PrimaryKeyRelatedField(queryset=Location.objects.all(), source='location.id')
    location = LocationSerializer(many=False)
    transport = TransportSerializer(many=False)

    class Meta:
        model = Equipment
        fields = ['id', 'name', 'location_id', 'location', 'transport']


class TransportSerializer(serializers.ModelSerializer):
    location = LocationSerializer(many=False, read_only=True)
    location_id = serializers.PrimaryKeyRelatedField(queryset=Location.objects.all(), source='location.id')

    class Meta:
        model = Transport
        fields = ['id', 'name', 'description', 'manufacturer', 'model', 'status', 'not_working_since', 'location',
                  'location_id']

    def create(self, validated_data):
        for key in ['location']:
            validated_data[key] = validated_data[key]['id']
        instance = Transport.objects.create(**validated_data)
        return instance


class LocationSerializer(serializers.ModelSerializer):
    class Meta:
        model = Location
        fields = ['id', 'name']


class EquipmentType(models.Model):
    name = models.CharField(max_length=200, verbose_name='Наименование')
    maintenance_quantity = models.IntegerField(verbose_name='Норматив обслуживания на 1 шт/год', default=1)
    is_mounted = models.BooleanField(verbose_name='Навесное оборудование', default=False)
    system = models.ForeignKey(System, on_delete=models.RESTRICT, verbose_name='Система', blank=True, null=True)

    class Meta:
        verbose_name = 'тип оборудования'
        verbose_name_plural = 'Типы оборудования'
        ordering = ['name']

    def __str__(self):
        return self.name

И модели -

class Location(models.Model):
    unit = models.ForeignKey(
        Unit, on_delete=models.RESTRICT, null=True,
        verbose_name='Организационная единица'
    )
    name = models.CharField(max_length=200, verbose_name='Наименование')

    class Meta:
        verbose_name = 'местоположение'
        verbose_name_plural = 'Местоположения'
        ordering = ['name']

    def __str__(self):
        return f'{self.name} ({self.unit.name})'
    

class TaskManager(models.Manager):
    def create(self, *args, **kwargs):
        task = super().create(*args, **kwargs)
        if task.type == task.TypeEnum.MAINTENANCE and task.transport:
            for item in task.transport.equipments.values('equipment_type_id').distinct():
                task.maintenance_execution.create(equipment_type_id=item['equipment_type_id'])
        return task


class Task(models.Model):
    class TypeEnum(models.TextChoices):
        MAINTENANCE = 'MAINTENANCE', 'Техническое обслуживание'
        RECOVERY = 'RECOVERY', 'Аварийно-восстановительные работы'
        PREVENTIVE = 'PREVENTIVE', 'Планово-предупредительный ремонт'
        OTHER = 'OTHER', 'Прочее'

    class UrgencyEnum(models.IntegerChoices):
        LOW = 0, 'Низкая'
        NORMAL = 1, 'Средняя'
        HIGH = 2, 'Высокая'
        CRITICAL = 3, 'Критичная'

    number = models.CharField(max_length=20, verbose_name='Номер')
    description = models.CharField(max_length=200, verbose_name='Описание')
    type = models.CharField(
        max_length=12, verbose_name='Тип',
        choices=TypeEnum.choices, default=TypeEnum.RECOVERY,
        null=True
    )
    urgency = models.IntegerField(
        verbose_name='Срочность',
        choices=UrgencyEnum.choices, default=UrgencyEnum.NORMAL,
        null=True
    )
    location = models.ForeignKey(
        'catalog.Location', on_delete=models.RESTRICT, verbose_name='Местоположение', blank=True, null=True
    )
    service = models.CharField(max_length=100, verbose_name='Услуга', blank=True, null=True)
    equipment_type = models.ForeignKey(
        'catalog.EquipmentType', on_delete=models.RESTRICT, verbose_name='Тип оборудования', blank=True, null=True
    )
    state = models.ForeignKey(State, on_delete=models.RESTRICT, verbose_name='Состояние')
    deadline = models.DateTimeField(verbose_name='Срок', blank=True, null=True)
    team = models.ForeignKey(
        'staff.Team', on_delete=models.RESTRICT, related_name='tasks', verbose_name='Бригада', blank=True, null=True
    )
    planned_date = models.DateField(verbose_name='Запланированная дата', blank=True, null=True)
    effort = models.FloatField(verbose_name='Трудоемкость', blank=True, null=True)
    unit = models.ForeignKey(
        'staff.Unit', on_delete=models.RESTRICT, related_name='task_unit', verbose_name='Подразделение', blank=True,
        null=True
    )
    transport = models.ForeignKey(Transport, on_delete=models.RESTRICT, verbose_name='Транспорт', blank=True, null=True)

    maintenance_executor = models.ForeignKey(Employee, on_delete=models.RESTRICT, verbose_name='Исполнитель',
                                             blank=True, null=True)
    closing_date = models.DateField(verbose_name='Дата закрытия задачи', blank=True, null=True)

    elapsed_time = models.DurationField(verbose_name='Затраченное время', blank=True, null=True)

    objects = TaskManager()

    @property
    def maintenance_executions(self) -> list:
        return list(self.maintenance_execution.all())

    class Meta:
        verbose_name = 'задача'
        verbose_name_plural = 'Задачи'
        ordering = ['number']

    def __str__(self):
        return f'#{self.number}: {self.description}'
    

class LocationSerializer(serializers.ModelSerializer):
    class Meta:
        model = Location
        fields = ['id', 'name']
        

class Unit(models.Model):
    name = models.CharField(max_length=200, verbose_name='Наименование')

    class Meta:
        verbose_name = 'организационная единица'
        verbose_name_plural = 'Организационные единицы'

    def __str__(self):
        return self.name


class Employee(models.Model):
    unit = models.ForeignKey(Unit, on_delete=models.RESTRICT, verbose_name='Организационная единица', related_name='employee')
    personnel_number = models.CharField(max_length=20, verbose_name='Табельный номер')
    last_name = models.CharField(max_length=100, verbose_name='Фамилия', default='')
    first_name = models.CharField(max_length=100, verbose_name='Имя', default='', blank=True)
    middle_name = models.CharField(max_length=100, verbose_name='Отчество', default='', blank=True)
    position = models.CharField(max_length=200, verbose_name='Должность')
    is_foreman = models.BooleanField(verbose_name='Прораб', default=False)
    working_mode = models.CharField(max_length=10, verbose_name='Режим работы', blank=True)

    class Meta:
        verbose_name = 'сотрудник'
        verbose_name_plural = 'Сотрудники'

    def full_name(self):
        return f'{self.last_name} {self.first_name} {self.middle_name}'
    full_name.short_description = 'ФИО'

    def __str__(self):
        return f'{self.last_name} ({self.position})'


class EquipmentType(models.Model):
    name = models.CharField(max_length=200, verbose_name='Наименование')
    maintenance_quantity = models.IntegerField(verbose_name='Норматив обслуживания на 1 шт/год', default=1)
    is_mounted = models.BooleanField(verbose_name='Навесное оборудование', default=False)
    system = models.ForeignKey(System, on_delete=models.RESTRICT, verbose_name='Система', blank=True, null=True)

    class Meta:
        verbose_name = 'тип оборудования'
        verbose_name_plural = 'Типы оборудования'
        ordering = ['name']

    def __str__(self):
        return self.name


class Transport(models.Model):
    class StatusEnum(models.TextChoices):
        IN_WORK = 'IN_WORK', 'Работает'
        NOT_IN_WORK = 'NOT_IN_WORK', 'Не работает'

    name = models.CharField(max_length=200, verbose_name='Наименование')
    description = models.TextField(verbose_name='Описание', blank=True, default='')
    transport_type = models.ForeignKey(
        TransportType, on_delete=models.RESTRICT,
        verbose_name='Тип транспорта', blank=True, null=True
    )
    manufacturer = models.CharField(max_length=100, verbose_name='Производитель', blank=True, null=True)
    model = models.CharField(max_length=100, verbose_name='Модель', blank=True, null=True)
    household_number = models.CharField(max_length=100, verbose_name='Хоз номер', blank=True, null=True)
    inventory_number = models.CharField(max_length=100, verbose_name='Инв номер', blank=True, null=True)
    status = models.CharField(
        max_length=12, verbose_name='Статус',
        choices=StatusEnum.choices, default=StatusEnum.IN_WORK
    )
    not_working_since = models.DateField(verbose_name='Не работает с...', blank=True, null=True)
    location = models.ForeignKey(
        Location, on_delete=models.RESTRICT, verbose_name='Расположение',
        blank=True, null=False
    )

    class Meta:
        verbose_name = 'транспорт'
        verbose_name_plural = 'Транспорт'
        ordering = ['name']

    def __str__(self):
        return self.name


class Equipment(models.Model):
    name = models.CharField(max_length=200, verbose_name='Наименование')
    equipment_type = models.ForeignKey(EquipmentType, on_delete=models.RESTRICT, verbose_name='Тип оборудования')
    location = models.ForeignKey(Location, on_delete=models.RESTRICT, verbose_name='Расположение')
    transport = models.ForeignKey(Transport, on_delete=models.RESTRICT, related_name='equipments',
                                  verbose_name='Транспорт', blank=True, null=True)

    class Meta:
        verbose_name = 'оборудование'
        verbose_name_plural = 'Оборудование'
        ordering = ['name']

    def __str__(self):
        return self.name
Вернуться на верх