Модель с внешними ключами занимает ~90 секунд на запрос (проблема модели с внешними ключами / сериализатора, я думаю)
У меня проблемы как с сериализатором, так и с моделями для таблицы, использующей внешние ключи. У меня есть представление для таблицы Cost (см. ниже), при запросе которого я получаю следующий результат примерно через 300-400 мс :
[
{
"id": 12,
"hours1": 10,
"hours2": 0,
"hours3": 0,
"hours4": 0,
"date": "2021-07-12",
"employee": 14,
"job1": 417,
"job2": 671,
"job3": 671,
"job4": 671
},
{
"id": 13,
"hours1": 8,
"hours2": 0,
"hours3": 0,
"hours4": 0,
"date": "2021-07-12",
"employee": 10,
"job1": 411,
"job2": 671,
"job3": 671,
"job4": 671
}
]
Поля employee, job1, job2, job3, job4 являются идентификаторами внешних ключей, для которых я хочу увидеть их основное/первичное значение (в данном случае имена!). Я использовал сериализатор для достижения этой цели, но проблема в том, что на один запрос уходит около 90 секунд и продолжает увеличиваться!
[
{
"id": 12,
"employee": {
"employee_name": "Person 1"
},
"job1": {
"No": "30201"
},
"job2": {
"No": "N/A"
},
"job3": {
"No": "N/A"
},
"job4": {
"No": "N/A"
},
"hours1": 10,
"hours2": 0,
"hours3": 0,
"hours4": 0,
"date": "2021-07-12"
},
{
"id": 13,
"employee": {
"employee_name": "Person 2"
},
"job1": {
"No": "30101"
},
"job2": {
"No": "N/A"
},
"job3": {
"No": "N/A"
},
"job4": {
"No": "N/A"
},
"hours1": 8,
"hours2": 0,
"hours3": 0,
"hours4": 0,
"date": "2021-07-12"
}
]
Моя схема и сериализаторы:
class Employee(models.Model):
employee_name = models.CharField(max_length=200, unique=True)
employee_email = models.CharField(max_length=200)
employee_navID = models.CharField(max_length=200)
employee_department = models.CharField(max_length=200)
joining_date = models.DateField()
leaving_date = models.DateField()
weekly_salary = models.IntegerField(default=0)
car_allowance = models.IntegerField()
national_insurance = models.IntegerField()
pension = models.IntegerField()
created_at = models.DateField(auto_now_add=True)
@property
def status(self):
if(self.leaving_date >= date.today()):
return "active"
else:
return "inactive"
@property
def employee_cost(self):
return self.weekly_salary + self.car_allowance + self.national_insurance + self.pension
class Meta:
verbose_name_plural = "Employees"
def __str__(self):
return self.employee_name
class ExcelJobsList(models.Model):
No = models.CharField(max_length=200)
Description = models.CharField(max_length=200)
Custom_No = models.CharField(max_length=200)
Sell_to_Name = models.CharField(max_length=200)
Status = models.CharField(max_length=200)
Person_Responsible = models.CharField(max_length=200)
Region_Code = models.CharField(max_length=200)
Market_Code = models.CharField(max_length=200)
Agreement_Form_Code = models.CharField(max_length=200)
Technology_Code = models.CharField(max_length=200)
secondappr_Code = models.CharField(max_length=200)
Search_Description = models.CharField(max_length=200)
Work_in_Progress_Status = models.CharField(max_length=200)
Job_Category = models.CharField(max_length=200)
Project_Manager = models.CharField(max_length=200)
Sales_Person = models.CharField(max_length=200)
Payment_Terms_Code = models.CharField(max_length=200)
First_Agreement_No = models.CharField(max_length=200)
No_of_Service_Agreements = models.CharField(max_length=200)
CRM_Reference = models.CharField(max_length=200)
class Meta:
verbose_name_plural = "Jobs"
def __str__(self):
return self.No
Они заполняются уникальным списком сотрудников и рабочих мест соответственно. Затем я использую это для создания базовой формы табеля учета рабочего времени, в которой пользователи могут назначать часы на работу (до 4). Это размещается в приведенной ниже модели, которая имеет внешние ключи к таблице сотрудников и вакансий.
class Cost(models.Model):
employee = models.ForeignKey(
Employee, default=1, on_delete=SET_DEFAULT)
job1 = models.ForeignKey(ExcelJobsList, default=0, on_delete=SET_DEFAULT)
hours1 = models.IntegerField()
job2 = models.ForeignKey(
ExcelJobsList, default=0, on_delete=SET_DEFAULT, related_name="job2")
hours2 = models.IntegerField()
job3 = models.ForeignKey(
ExcelJobsList, default=0, on_delete=SET_DEFAULT, related_name="job3")
hours3 = models.IntegerField()
job4 = models.ForeignKey(
ExcelJobsList, default=0, on_delete=SET_DEFAULT, related_name="job4")
hours4 = models.IntegerField()
date = models.DateField()
После выбора нужных мне полей из сериализатора получается следующий результат:
class JobModelSerializer(serializers.ModelSerializer):
class Meta:
model = ExcelJobsList
fields = ['No']
class EmployeeModelSerializer(serializers.ModelSerializer):
class Meta:
model = Employee
fields = ['employee_name']
class CostModelSerializer(serializers.ModelSerializer):
employee = EmployeeModelSerializer()
job1 = JobModelSerializer()
job2 = JobModelSerializer()
job3 = JobModelSerializer()
job4 = JobModelSerializer()
class Meta:
model = Cost
fields = ('__all__')
class CostListFilter(filters.FilterSet):
class Meta:
model = Cost
fields = {
'employee': ['exact'],
}
class JoinedFilterCostList(generics.ListAPIView):
queryset = Cost.objects.filter()
serializer_class = CostModelSerializer
filter_backends = [DjangoFilterBackend]
filterset_class = CostListFilter
Я пробовал использовать to_field= 'xyz', однако это возвращает null, так что либо я что-то упускаю в своей модели, либо сериализатор неправильно настроен для этого... либо и то, и другое!
Любая помощь будет очень признательна.
Замедление вызвано тем, что приходится обращаться к базе данных 4 раза для каждого экземпляра Cost
, чтобы получить все связанные ExcelJobsList
.
Чтобы избежать этого и сделать его более эффективным, вы можете использовать select_related
к связанным ExcelJobsList
следующим образом:
class JoinedFilterCostList(generics.ListAPIView):
...
queryset = Cost.objects.select_related('job1', 'job2', 'job3', 'job4')
...
В итоге должен получиться один запрос, с внутренними соединениями на связанных ExcelJobsList
.