diff options
| author | Jamie Matthews | 2012-09-26 13:05:21 +0100 |
|---|---|---|
| committer | Jamie Matthews | 2012-09-26 13:05:21 +0100 |
| commit | 01770c53cd9045e6ea054f32b1e40b5d2ff7fe44 (patch) | |
| tree | 657cb66f92d78add3b2f587754387832043168e6 /djangorestframework/throttling.py | |
| parent | f6488cb0589d3b11fb8d831e00d1389f3fff74b6 (diff) | |
| parent | 09a445b257532be69ffab69a3f62b84bfa90463d (diff) | |
| download | django-rest-framework-01770c53cd9045e6ea054f32b1e40b5d2ff7fe44.tar.bz2 | |
Merge branch 'restframework2' of git://github.com/tomchristie/django-rest-framework into improved-view-decorators
* 'restframework2' of git://github.com/tomchristie/django-rest-framework: (56 commits)
Bits of cleanup
Add request.QUERY_PARAMS
Add readonly 'id' field
Tweak browseable API
Don't display readonly fields
Fix some bits of serialization
Add csrf note
Fix incorrect bit of tutorial
Added tox.ini
Tweak media_type -> accepted_media_type. Need to document, but marginally less confusing
Tweak media_type -> accepted_media_type. Need to document, but marginally less confusing
Tweak media_type -> accepted_media_type. Need to document, but marginally less confusing
Clean up bits of templates etc
Hack out bunch of unneccesary private methods on View class
Clean up template tags
Remove dumbass __all__ variables
Remove old 'djangorestframework directories
Change package name: djangorestframework -> rest_framework
Dont strip final '/'
Use get_script_prefix to play nicely if not installed at the root.
...
Conflicts:
rest_framework/decorators.py
Diffstat (limited to 'djangorestframework/throttling.py')
| -rw-r--r-- | djangorestframework/throttling.py | 217 |
1 files changed, 0 insertions, 217 deletions
diff --git a/djangorestframework/throttling.py b/djangorestframework/throttling.py deleted file mode 100644 index 6249bd42..00000000 --- a/djangorestframework/throttling.py +++ /dev/null @@ -1,217 +0,0 @@ -from django.core.cache import cache -from djangorestframework.settings import api_settings -import time - - -class BaseThrottle(object): - """ - Rate throttling of requests. - """ - - def __init__(self, view=None): - """ - All throttles hold a reference to the instantiating view. - """ - self.view = view - - def allow_request(self, request): - """ - Return `True` if the request should be allowed, `False` otherwise. - """ - raise NotImplementedError('.allow_request() must be overridden') - - def wait(self): - """ - Optionally, return a recommeded number of seconds to wait before - the next request. - """ - return None - - -class SimpleRateThottle(BaseThrottle): - """ - A simple cache implementation, that only requires `.get_cache_key()` - to be overridden. - - The rate (requests / seconds) is set by a :attr:`throttle` attribute - on the :class:`.View` class. The attribute is a string of the form 'number of - requests/period'. - - Period should be one of: ('s', 'sec', 'm', 'min', 'h', 'hour', 'd', 'day') - - Previous request information used for throttling is stored in the cache. - """ - - timer = time.time - settings = api_settings - cache_format = 'throtte_%(scope)s_%(ident)s' - scope = None - - def __init__(self, view): - super(SimpleRateThottle, self).__init__(view) - rate = self.get_rate_description() - self.num_requests, self.duration = self.parse_rate_description(rate) - - def get_cache_key(self, request): - """ - Should return a unique cache-key which can be used for throttling. - Must be overridden. - - May return `None` if the request should not be throttled. - """ - raise NotImplementedError('.get_cache_key() must be overridden') - - def get_rate_description(self): - """ - Determine the string representation of the allowed request rate. - """ - try: - return self.rate - except AttributeError: - return self.settings.DEFAULT_THROTTLE_RATES.get(self.scope) - - def parse_rate_description(self, rate): - """ - Given the request rate string, return a two tuple of: - <allowed number of requests>, <period of time in seconds> - """ - assert rate, "No throttle rate set for '%s'" % self.__class__.__name__ - num, period = rate.split('/') - num_requests = int(num) - duration = {'s': 1, 'm': 60, 'h': 3600, 'd': 86400}[period[0]] - return (num_requests, duration) - - def allow_request(self, request): - """ - Implement the check to see if the request should be throttled. - - On success calls `throttle_success`. - On failure calls `throttle_failure`. - """ - self.key = self.get_cache_key(request) - self.history = cache.get(self.key, []) - self.now = self.timer() - - # Drop any requests from the history which have now passed the - # throttle duration - while self.history and self.history[-1] <= self.now - self.duration: - self.history.pop() - if len(self.history) >= self.num_requests: - return self.throttle_failure() - return self.throttle_success() - - def throttle_success(self): - """ - Inserts the current request's timestamp along with the key - into the cache. - """ - self.history.insert(0, self.now) - cache.set(self.key, self.history, self.duration) - return True - - def throttle_failure(self): - """ - Called when a request to the API has failed due to throttling. - """ - return False - - def wait(self): - """ - Returns the recommended next request time in seconds. - """ - if self.history: - remaining_duration = self.duration - (self.now - self.history[-1]) - else: - remaining_duration = self.duration - - available_requests = self.num_requests - len(self.history) + 1 - - return remaining_duration / float(available_requests) - - -class AnonRateThrottle(SimpleRateThottle): - """ - Limits the rate of API calls that may be made by a anonymous users. - - The IP address of the request will be used as the unqiue cache key. - """ - scope = 'anon' - - def get_cache_key(self, request): - if request.user.is_authenticated(): - return None # Only throttle unauthenticated requests. - - ident = request.META.get('REMOTE_ADDR', None) - - return self.cache_format % { - 'scope': self.scope, - 'ident': ident - } - - -class UserRateThrottle(SimpleRateThottle): - """ - Limits the rate of API calls that may be made by a given user. - - The user id will be used as a unique cache key if the user is - authenticated. For anonymous requests, the IP address of the request will - be used. - """ - scope = 'user' - - def get_cache_key(self, request): - if request.user.is_authenticated(): - ident = request.user.id - else: - ident = request.META.get('REMOTE_ADDR', None) - - return self.cache_format % { - 'scope': self.scope, - 'ident': ident - } - - -class ScopedRateThrottle(SimpleRateThottle): - """ - Limits the rate of API calls by different amounts for various parts of - the API. Any view that has the `throttle_scope` property set will be - throttled. The unique cache key will be generated by concatenating the - user id of the request, and the scope of the view being accessed. - """ - - scope_attr = 'throttle_scope' - - def __init__(self, view): - """ - Scope is determined from the view being accessed. - """ - self.scope = getattr(self.view, self.scope_attr, None) - super(ScopedRateThrottle, self).__init__(view) - - def parse_rate_description(self, rate): - """ - Subclassed so that we don't fail if `view.throttle_scope` is not set. - """ - if not rate: - return (None, None) - return super(ScopedRateThrottle, self).parse_rate_description(rate) - - def get_cache_key(self, request): - """ - If `view.throttle_scope` is not set, don't apply this throttle. - - Otherwise generate the unique cache key by concatenating the user id - with the '.throttle_scope` property of the view. - """ - if not self.scope: - return None # Only throttle views if `.throttle_scope` is set. - - if request.user.is_authenticated(): - ident = request.user.id - else: - ident = request.META.get('REMOTE_ADDR', None) - - return self.cache_format % { - 'scope': self.scope, - 'ident': ident - } |
