Как обрабатывать параллельные запросы PUT / PATCH в Django Rest Framework? (Перезаписать вопрос) - PullRequest
1 голос
/ 16 марта 2019

Я разрабатываю веб-приложение для универ-проекта, и оно касается аэропортов и перевозчиков ...

Проблема

Проблема в том, что когда я пытаюсь перейти на базу данных, яделать параллельно, потому что в файле json, который я использую в качестве ссылки, есть много записей о перевозчиках и аэропортах.

Когда вы обновляете (PUT и PATCH) по одному последовательно, этоработает как надо, но когда я пытаюсь выполнить параллельные запросы PUT / PATCH, экземпляр в БД каждый раз перезаписывается.

В каждом аэропорту есть список перевозчиков, и когда я пытался добавить в него больше перевозчиковэтот список через PUT или PATCH параллельно перезаписывает предыдущий список носителей экземпляра

Мои модели:

class Carrier(models.Model):
    code = models.CharField(max_length=10, primary_key=True)
    name = models.TextField()

    def __str__(self):
        return self.name

class Airport(models.Model):
    code = models.CharField(max_length=10, primary_key=True)
    name = models.TextField()
    carriers = models.ManyToManyField(Carrier, related_name='airports')

    def __str__(self):
        return self.name

Сериализаторы:

class AirportListSerializer(serializers.ModelSerializer):
    url = serializers.HyperlinkedIdentityField(view_name='airport-detail')

    class Meta:
        model = models.Airport
        fields = ('code', 'name', 'url')

class AirportDetailSerializer(serializers.ModelSerializer):
    url = serializers.HyperlinkedIdentityField(view_name='airport-detail')

    class Meta:
        model = models.Airport
        fields = ('code', 'name', 'url', 'carriers')

И у меня возникли проблемы с моим методом обновления, мне пришлось его переопределить, потому что для добавления новых данных в массив носителей экземпляра аэропорта:

def update(self, request, *args, **kwargs):
        instance = self.get_object()

        serializer = serializers.AirportDetailSerializer(
            instance=instance,
            data=request.data,
            context={'request': request}
        )

        if serializer.is_valid(raise_exception=True):
            # Getting the user inputed carriers after it was validated by the serializer
            carriers = set(dict(request.data)['carriers'])    
            # Adding new carriers to the current airport list of carriers without deleting the old ones
            for carrier in serializer.validated_data['carriers']:
                print(carrier)
                carriers.add(carrier)
            print('Carriers %s' % carriers)
            # Saving alterations to the db
            serializer.save(carriers=carriers)

        # Overriding the original data for more features
        data = serializer.data
        # Creating the carrier links 
        data['carriers'] = ['http://%s/api/carriers/%s/' % (request.get_host(), carrier) for carrier in data['carriers']]

        return Response(data)

Пример записи в DRF Browsable API: enter image description here

1 Ответ

1 голос
/ 16 марта 2019

Каждый раз, когда вы звоните serializer.save(carriers=carriers), вы сохраняете экземпляр Airport только с операторами из вашего конкретного запроса PUT / PATCH ... вместо добавления операторов в ваш Airport экземпляр.

Вы должны посмотреть документы для записываемых вложенных сериализаторов и использовать отдельный CarrierSerializer.

В соответствующей заметке ваша логика update лучше подходит как часть AirportSerializer, не ваш взгляд.Это делает его более пригодным для повторного использования.Этот раздел документации по DRF является идеальным примером для вас.

Примерно такой псевдокод, адаптированный из примера "Альбомы / дорожки":

class AirportSerializer(serializers.ModelSerializer):
    carriers = CarrierSerializer(many=True)

    class Meta:
        model = Airport
        fields = ......

    def update(self, instance, validated_data):
        carriers_data = validated_data.pop('carriers')
        for carrier in carriers_data:
            Carrier.objects.update_or_create(airport=instance, defaults=carrier_data)
        return instance
...