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!