Drf throttling restart when POST

Viewed 216

I created some app and want to set up different throttling for GET/POST,PUT,PATCH. When I make a GET request, it works fine. But when I perfom POST/PUT/PATCH it looks like cache is restarted. I want to create also extra headers so that's why I think this is actually the problem.

Throttling.py

class SetHeaders:
    def throttle_success(self):
        self.history.insert(0, self.now)
        self.cache.set(self.key, self.history, self.duration)
        print(self.key, self.history, self.duration)
        print(f"REMAIN TIME {self.duration - (self.now - self.history[-1])}")
        print(f'LIMIT {self.get_rate()}')
        print(f'REMAIN RATE {self.num_requests - len(self.history) + 1}')
        return True

class UserPostRateThrottle(SetHeaders, throttling.UserRateThrottle):
    scope = "user_post"

    def allow_request(self, request, view):
        if request.method == "GET":
            return True
        return super().allow_request(request, view)
        
class UserGetRateThrottle(SetHeaders, throttling.UserRateThrottle):
    scope = "user_get"

    def allow_request(self, request, view):
        if request.method in ("POST", "PUT", "PATCH"):
            return True
        return super().allow_request(request, view)

views.py

class TruckViewSet(viewsets.ModelViewSet):
    queryset = Truck.confirmed.all()
    serializer_class = TruckSerializer
    filterset_class = TruckFilter
    permission_classes = (
        permissions.DjangoModelPermissions,
        IsOwnerOrReadOnly,
    )
    throttle_classes = (UserGetRateThrottle, UserPostRateThrottle)

    def _get_images(self, request):
        for img in request.data.getlist("image"):
            image_serializer = TruckImageSerializer(data={"image": img})
            image_serializer.is_valid(raise_exception=True)

    def create(self, request, *args, **kwargs):
        if request.data.get("image"):
            self._get_images(request)
        return super(TruckViewSet, self).create(request, *args, **kwargs)

    def perform_create(self, serializer):
        serializer.save(owner=self.request.user)

    def update(self, request, *args, **kwargs):
        if request.data.get("image"):
            self._get_images(request)
        return super(TruckViewSet, self).update(request, *args, **kwargs)

serializers.py

class TruckSerializer(serializers.ModelSerializer):
    location = LocationSerializer(
        read_only=True,
    )
    owner = serializers.PrimaryKeyRelatedField(
        read_only=True,
    )
    name = serializers.CharField(
        max_length=50,
        validators=[
            validators.UniqueValidator(queryset=Truck.confirmed.all())
        ],
    )
    images = serializers.SerializerMethodField()
    payment_methods = serializers.SerializerMethodField()

    class Meta:
        model = Truck
        fields = (
            "id",
            "owner",
            "name",
            "phone",
            "email",
            "facebook",
            "instagram",
            "page_url",
            "description",
            "city",
            "payment_methods",
            "images",
            "updated",
            "location",
        )

    def _get_payments(self, data):
        new_payments = []
        payments = data.get("payment_methods")
        for payment in payments.split(", "):
            try:
                filtered_payment = PaymentMethod.objects.get(
                    payment_name__iexact=payment
                ).id
            except PaymentMethod.DoesNotExist:
                raise serializers.ValidationError(
                    "Given payment method does not match"
                )
            new_payments.append(filtered_payment)
        return new_payments

    def create(self, validated_data):
        data = self.context.get("view").request.data
        new_payments = data.get("payment_methods", {})
        if new_payments:
            new_payments = self._get_payments(data)
        truck = Truck.objects.create(**validated_data)
        if data.get("image"):
            for image_data in data.getlist("image"):
                TruckImage.objects.create(truck=truck, image=image_data)
        truck.payment_methods.add(*new_payments)
        return truck

    def update(self, instance, validated_data):
        data = self.context.get("view").request.data
        method = self.context.get("view").request.method
        new_payments = data.get("payment_methods", {})
        if new_payments:
            new_payments = self._get_payments(data)
        if data.get("image"):
            images = instance.images.all()
            if images.exists():
                instance.images.all().delete()
            for image_data in data.getlist("image"):
                TruckImage.objects.create(truck=instance, image=image_data)
        if method == "PUT" or method == "PATCH" and new_payments:
            instance.payment_methods.clear()
        instance.payment_methods.add(*new_payments)
        return super(TruckSerializer, self).update(instance, validated_data)

    def get_images(self, obj):
        images = obj.images.all()
        return [img.image.url for img in images]

    def get_payment_methods(self, obj):
        return obj.payment_methods.values_list("payment_name", flat=True)

settings.py

REST_FRAMEWORK = {
    "DEFAULT_THROTTLE_CLASSES": [
        'rest_framework.throttling.ScopedRateThrottle',
        # 'rest_framework.throttling.UserRateThrottle'
    ],
    "DEFAULT_THROTTLE_RATES": {
        "user_get": "6/day",
        "user_post": "4/day",
        # 'user': '5/day'
    },
    "DEFAULT_PAGINATION_CLASS": "rest_framework.pagination.LimitOffsetPagination",
    "PAGE_SIZE": 10,
    "DEFAULT_FILTER_BACKENDS": [
        "django_filters.rest_framework.DjangoFilterBackend"
    ],
    "DEFAULT_VERSIONING_CLASS": "rest_framework.versioning.NamespaceVersioning",
}

So when GET requested is performed I get:

>>>REMAIN TIME 86399.02145123482
>>>LIMIT 6/day
>>>REMAIN RATE 3
and so on...

But in case of other request i get the same info all the time

>>>REMAIN TIME 86400.0
>>>LIMIT 4/day
>>>REMAIN RATE 4

What i've tried? I've tried to change to simple UserRateThrottle, but nothing changed. So I think custom throttle is made correctly. Also I was changing between default Django cache/Redis server/Dummy cache and still not working as desired. Thanks in advance!

0 Answers
Related