Сохранение с минимальным количеством запросов к базе данных (минимизация вызова метода save) - PullRequest
5 голосов
/ 30 апреля 2020

Я обрабатываю json файл с большим количеством вложенной информации. Для этого в классе UploadElementFromExcelFile(APIView) я использую вложенные циклы, в самом конце которых я вызываю метод serializer.save(). Затем в сериализаторе ElementCommonInfoSerializer в методе create() я сохраняю / обновляю данные, полученные из сериализатора. Кроме того, я создаю / обновляю модель RfiParticipationStatus, которая связана только с самым высоким уровнем вложенности (использует переменную parent_category). Но так как метод create вызывается для самого нижнего элемента l oop, я делаю много бесполезных запросов к базе данных для модели RfiParticipationStatus. То же самое с сохранением company_information. Это дополнительная информация из общего файла json. Он не относится к объекту сериализации, и мне нужно сохранить company_information только один раз в самом начале вызова метода post. В моей реализации кода запрос на сохранение происходит сотни раз в зависимости от глубины и содержания для l oop.

json выборка данных

[
    {
        "Company_info": [
            {
                "question": "Company name",
                "answer": "Test"
            },
            {
                "question": "Parent company (if applicable)",
                "answer": "2test"
            },
            {....},
            {....}
        ]
    },
    {
        "Parent Category": " rtS2P",
        "Category": [
            {
                "Analytics": [
                    {
                        "Data Schema": [
                            {   '....': "",
                                "Attachments/Supporting Docs and Location/Link": "tui",
                                "SM score": 4,
                                "Analyst notes": "tytyt"
                            },
                            {   '....': "",
                                "Attachments/Supporting Docs and Location/Link": null,
                                "SM score": null,
                                "Analyst notes": null
                            },
                        ]
                    },
                    {
                        "Data Management": [
                            {
                                '....': "",
                                "Attachments/Supporting Docs and Location/Link": null,
                                "SM score": null,
                                "Analyst notes": null
                            },
                            {....}
                        ]
                    }
                ]
            },
            {
                "Configurability": [...]
            }
        ]
    },
    {
        "Parent Category": "DFG",
        "Category": [
            {
                "Contingent Workforce / Services Procurement": [...]
            },
            {
                "Performance Management": [...]
            }
        ]
    },
        "Parent Category": "...",
         .....
]

views.py

class UploadElementFromExcelFile(APIView):
    serializer_class = ElementCommonInfoSerializer

    def post(self, request, *args, **kwargs):
        context = {'rfiid': kwargs.get('rfiid'), 'vendor': kwargs.get('vendor'), 'analyst': kwargs.get('analyst')}
        data = request.data  # data is list of dict
        company_information = next(iter(data))
        context.update(company_information)

        try:
            with transaction.atomic():
                for pc_data in data[1:]:  # from dict get PC and Category participate data, exclude firs element - CI
                    parent_category = pc_data.get('Parent Category')
                    category_data = pc_data.get('Category')
                    for data in category_data:
                        for category, values in data.items():  # Get category name
                            for subcats in values:
                                for subcat, element_list in subcats.items():  # Get subcategory name
                                    for num, element in enumerate(element_list, 1):  # Get element info
                                        # some logic
                                        data = {......, ......,}
                                        serializer = ElementCommonInfoSerializer(data=data, context=context)
                                        serializer.is_valid(raise_exception=True)
                                        serializer.save()
        except ValidationError:
            return Response({"errors": (serializer.errors,)},
                            status=status.HTTP_400_BAD_REQUEST)
        else:
            return Response(request.data, status=status.HTTP_200_OK)

serializer.py

class ElementCommonInfoSerializer(serializers.ModelSerializer):
    .....
    .....
    def create(self, validated_data):
        ....

        # !!!! Question is in the  rfi_part_status variable below

        rfi_part_status, _ = RfiParticipationStatus.objects.update_or_create(status=pc_status, vendor=vendor, rfi=round,
                                                                             pc=parent_category.first(),
                                                defaults={'last_vendor_response': lvr, 'last_analyst_response': lar})

        # And question else in company_information save
                company_information = self.context.get('Company_info')
        for ci in company_information:
            ciq, _ = CompanyGeneralInfoQuestion.objects.get_or_create(question=ci.get('question'), rfi=round)
            cia, _ = CompanyGeneralInfoAnswers.objects.get_or_create(vendor=vendor, question=ciq,
                                                                 answer=ci.get('answer'))

        #another crete logic
        .....
        .....

        return self

Вопрос в том, как назвать создание объекта rfi_part_status только в момент прохождения через самый верхний элемент вложенного l oop (for pc_data in data[1:]:). И такая же ситуация с company information

UPD (в соответствии с вопросом Линовии) !! models.py

class RfiParticipationStatus(models.Model):
    status = models.CharField(max_length=50, choices=STATUS_NAME)
    vendor = models.ForeignKey('Vendors', models.DO_NOTHING, related_name='to_vendor_status')
    rfi = models.ForeignKey('Rfis', models.DO_NOTHING, related_name='to_rfis_status')
    pc = models.ForeignKey(ParentCategories, models.DO_NOTHING, blank=True, null=True)
    last_vendor_response = models.IntegerField(blank=True, null=True)
    last_analyst_response = models.IntegerField(blank=True, null=True)

, когда для объекта RfiParticipationStatus создается только значение pc (родительская категория), полученное из данных сериализатора. Все остальные значения рассчитываются в процессе.

1 Ответ

2 голосов
/ 05 мая 2020

вы должны сохранить бизнес-логику c на уровне модели Django, переопределить метод save и использовать вызов super () для метода переопределения, который вы использовали для своего привычного поведения.

...