Оптимизация вложенных сериализаторов 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