feat: Add support for flexible throttling intervals

This commit introduces the ability to set custom time intervals for throttling,
allowing users to specify intervals like per 5 minutes, per 2 hours, and per 5 days.
This enhancement provides more flexibility in controlling request rates.
This commit is contained in:
Pravin Kamble 2023-08-20 23:21:03 +05:30
parent 40eccb0d6c
commit f23c37e76a
3 changed files with 65 additions and 2 deletions

View File

@ -196,6 +196,41 @@ For example, given the following views...
User requests to either `ContactListView` or `ContactDetailView` would be restricted to a total of 1000 requests per-day. User requests to `UploadView` would be restricted to 20 requests per day.
---
## Customizing Throttling Time
You can now set custom and flexible time periods for the throttling classes. This enhancement allows you to specify a time unit for throttling, giving you more control over how the rate limits are applied.
To set custom and flexible time periods for throttling, you can use the throttle_duration parameter. The throttle_duration parameter accepts a string that combines a numeric quantity and a time unit, similar to the style used in other Django settings. For example, you can set a throttling duration of "10s" for 10 seconds or "5m" for 5 minutes.
## Examples
1. Custom Time Period for User Throttling
To limit the rate of requests for authenticated users to 5 requests every 15 minutes, you can use the throttle_duration parameter as follows:
REST_FRAMEWORK = {
'DEFAULT_THROTTLE_RATES': {
'user': '5/15m', # Allow 5 requests every 15 minutes for each user
}
}
2. Scoped Throttling with Custom Time
You can also apply custom throttling rates to specific views using the ScopedRateThrottle class. For example, to limit requests to the "write" scope to 3 requests every 30 seconds:
REST_FRAMEWORK = {
'DEFAULT_THROTTLE_CLASSES': [
'rest_framework.throttling.ScopedRateThrottle',
],
'DEFAULT_THROTTLE_RATES': {
'user': '10/minute', # Default rate for all users
'write': '3/30s', # Allow 3 requests every 30 seconds for "write" scope
'custom_scope': '20/1h', # Allow 20 requests every 1 hour for a custom scope
}
}
With these enhancements, you can have more granular control over how you limit the rate of incoming requests to your API views in Django REST framework.
# Custom throttles

View File

@ -94,6 +94,24 @@ class SimpleRateThrottle(BaseThrottle):
msg = "No default throttle rate set for '%s' scope" % self.scope
raise ImproperlyConfigured(msg)
def parse_quantity_and_unit(self, quantity_unit_string):
"""
Parse a combined quantity and unit string and return a tuple with parsed values.
Returns:
tuple: A tuple containing the parsed values (quantity, unit).
"""
i = 0
while i < len(quantity_unit_string) and quantity_unit_string[i].isnumeric():
i += 1
if i == 0:
return (1, quantity_unit_string)
else:
quantity = int(quantity_unit_string[:i])
unit = quantity_unit_string[i:]
return (quantity, unit)
def parse_rate(self, rate):
"""
Given the request rate string, return a two tuple of:
@ -102,9 +120,11 @@ class SimpleRateThrottle(BaseThrottle):
if rate is None:
return (None, None)
num, period = rate.split('/')
quantity, unit = self.parse_quantity_and_unit(period)
num_requests = int(num)
duration = {'s': 1, 'm': 60, 'h': 3600, 'd': 86400}[period[0]]
return (num_requests, duration)
duration = {'s': 1, 'm': 60, 'h': 3600, 'd': 86400}[unit[0]]
total_duration = duration * int(quantity)
return (num_requests, total_duration)
def allow_request(self, request, view):
"""

View File

@ -466,6 +466,14 @@ class SimpleRateThrottleTests(TestCase):
rate = SimpleRateThrottle().parse_rate(None)
assert rate == (None, None)
def test_parse_quantity_and_unit_parses_correctly(self):
result = SimpleRateThrottle().parse_quantity_and_unit("5min")
assert result == (5, 'min')
result = SimpleRateThrottle().parse_quantity_and_unit("h")
assert result == (1, 'h')
result = SimpleRateThrottle().parse_quantity_and_unit("123s")
assert result == (123, 's')
def test_allow_request_returns_true_if_rate_is_none(self):
assert SimpleRateThrottle().allow_request(request={}, view={}) is True