aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.gitignore19
-rw-r--r--.travis.yml12
-rw-r--r--README.md2
-rw-r--r--docs/api-guide/fields.md5
-rw-r--r--docs/api-guide/routers.md2
-rw-r--r--docs/api-guide/viewsets.md2
-rw-r--r--docs/topics/3.0-announcement.md2
-rw-r--r--docs/topics/third-party-resources.md4
-rw-r--r--docs/tutorial/1-serialization.md2
-rw-r--r--docs/tutorial/2-requests-and-responses.md2
-rw-r--r--docs/tutorial/5-relationships-and-hyperlinked-apis.md2
-rw-r--r--requirements.txt2
-rw-r--r--rest_framework/authentication.py2
-rw-r--r--rest_framework/decorators.py11
-rw-r--r--rest_framework/pagination.py4
-rw-r--r--rest_framework/request.py21
-rw-r--r--rest_framework/response.py3
-rw-r--r--rest_framework/serializers.py6
-rw-r--r--rest_framework/templatetags/rest_framework.py4
-rw-r--r--rest_framework/throttling.py4
-rwxr-xr-xsetup.py7
-rw-r--r--tests/test_authentication.py9
-rw-r--r--tests/test_relations_hyperlink.py4
-rw-r--r--tests/test_renderers.py9
-rw-r--r--tests/test_request.py22
-rw-r--r--tests/test_response.py9
-rw-r--r--tests/test_throttling.py8
-rw-r--r--tests/test_versioning.py9
-rw-r--r--tox.ini3
29 files changed, 125 insertions, 66 deletions
diff --git a/.gitignore b/.gitignore
index 4f2b0ddf..3d5f1043 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,19 +3,14 @@
*~
.*
-site/
-htmlcov/
-coverage/
-build/
-dist/
-*.egg-info/
+/site/
+/htmlcov/
+/coverage/
+/build/
+/dist/
+/*.egg-info/
+/env/
MANIFEST
-bin/
-include/
-lib/
-local/
-env/
-
!.gitignore
!.travis.yml
diff --git a/.travis.yml b/.travis.yml
index 28ebfc00..4f929785 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -25,18 +25,6 @@ env:
- TOX_ENV=py33-django18alpha
- TOX_ENV=py32-django18alpha
- TOX_ENV=py27-django18alpha
- - TOX_ENV=py34-djangomaster
- - TOX_ENV=py33-djangomaster
- - TOX_ENV=py32-djangomaster
- - TOX_ENV=py27-djangomaster
-
-matrix:
- fast_finish: true
- allow_failures:
- - env: TOX_ENV=py34-djangomaster
- - env: TOX_ENV=py33-djangomaster
- - env: TOX_ENV=py32-djangomaster
- - env: TOX_ENV=py27-djangomaster
install:
- pip install tox
diff --git a/README.md b/README.md
index a03517f4..eec80977 100644
--- a/README.md
+++ b/README.md
@@ -34,7 +34,7 @@ There is a live example API for testing purposes, [available here][sandbox].
# Requirements
* Python (2.6.5+, 2.7, 3.2, 3.3, 3.4)
-* Django (1.4.11+, 1.5.6+, 1.6.3+, 1.7)
+* Django (1.4.11+, 1.5.6+, 1.6.3+, 1.7, 1.8-alpha)
# Installation
diff --git a/docs/api-guide/fields.md b/docs/api-guide/fields.md
index 9c33974f..f113bb23 100644
--- a/docs/api-guide/fields.md
+++ b/docs/api-guide/fields.md
@@ -591,6 +591,10 @@ The [drf-compound-fields][drf-compound-fields] package provides "compound" seria
The [drf-extra-fields][drf-extra-fields] package provides extra serializer fields for REST framework, including `Base64ImageField` and `PointField` classes.
+## djangrestframework-recursive
+
+the [djangorestframework-recursive][djangorestframework-recursive] package provides a `RecursiveField` for serializing and deserializing recursive structures
+
## django-rest-framework-gis
The [django-rest-framework-gis][django-rest-framework-gis] package provides geographic addons for django rest framework like a `GeometryField` field and a GeoJSON serializer.
@@ -607,6 +611,7 @@ The [django-rest-framework-hstore][django-rest-framework-hstore] package provide
[iso8601]: http://www.w3.org/TR/NOTE-datetime
[drf-compound-fields]: http://drf-compound-fields.readthedocs.org
[drf-extra-fields]: https://github.com/Hipo/drf-extra-fields
+[djangorestframework-recursive]: https://github.com/heywbj/django-rest-framework-recursive
[django-rest-framework-gis]: https://github.com/djangonauts/django-rest-framework-gis
[django-rest-framework-hstore]: https://github.com/djangonauts/django-rest-framework-hstore
[django-hstore]: https://github.com/djangonauts/django-hstore
diff --git a/docs/api-guide/routers.md b/docs/api-guide/routers.md
index 592f7d66..222b6cd2 100644
--- a/docs/api-guide/routers.md
+++ b/docs/api-guide/routers.md
@@ -304,7 +304,7 @@ The [wq.db package][wq.db] provides an advanced [Router][wq.db-router] class (an
The [`DRF-extensions` package][drf-extensions] provides [routers][drf-extensions-routers] for creating [nested viewsets][drf-extensions-nested-viewsets], [collection level controllers][drf-extensions-collection-level-controllers] with [customizable endpoint names][drf-extensions-customizable-endpoint-names].
[cite]: http://guides.rubyonrails.org/routing.html
-[route-decorators]: viewsets.html#marking-extra-actions-for-routing
+[route-decorators]: viewsets.md#marking-extra-actions-for-routing
[drf-nested-routers]: https://github.com/alanjds/drf-nested-routers
[wq.db]: http://wq.io/wq.db
[wq.db-router]: http://wq.io/docs/app.py
diff --git a/docs/api-guide/viewsets.md b/docs/api-guide/viewsets.md
index b09dfc9e..bbf92c6c 100644
--- a/docs/api-guide/viewsets.md
+++ b/docs/api-guide/viewsets.md
@@ -201,7 +201,7 @@ Note that you can use any of the standard attributes or method overrides provide
def get_queryset(self):
return self.request.user.accounts.all()
-Note however that upon removal of the `queryset` property from your `ViewSet`, any associated [router][routers] will be unable to derive the base_name of your Model automatically, and so you you will have to specify the `base_name` kwarg as part of your [router registration][routers].
+Note however that upon removal of the `queryset` property from your `ViewSet`, any associated [router][routers] will be unable to derive the base_name of your Model automatically, and so you will have to specify the `base_name` kwarg as part of your [router registration][routers].
Also note that although this class provides the complete set of create/list/retrieve/update/destroy actions by default, you can restrict the available operations by using the standard permission classes.
diff --git a/docs/topics/3.0-announcement.md b/docs/topics/3.0-announcement.md
index 24e69c2d..59fe779c 100644
--- a/docs/topics/3.0-announcement.md
+++ b/docs/topics/3.0-announcement.md
@@ -826,7 +826,7 @@ The `style` keyword argument can be used to pass through additional information
For example, to use a `textarea` control instead of the default `input` control, you would use the following…
additional_notes = serializers.CharField(
- style={'base_template': 'text_area.html'}
+ style={'base_template': 'textarea.html'}
)
Similarly, to use a radio button control instead of the default `select` control, you would use the following…
diff --git a/docs/topics/third-party-resources.md b/docs/topics/third-party-resources.md
index 6f4df288..e26e3a2f 100644
--- a/docs/topics/third-party-resources.md
+++ b/docs/topics/third-party-resources.md
@@ -237,7 +237,7 @@ To submit new content, [open an issue][drf-create-issue] or [create a pull reque
* [django-rest-framework-proxy][django-rest-framework-proxy] - Proxy to redirect incoming request to another API server.
* [gaiarestframework][gaiarestframework] - Utils for django-rest-framewok
* [drf-extensions][drf-extensions] - A collection of custom extensions
-* [ember-data-django-rest-adapter][ember-data-django-rest-adapter] - An ember-data adapter
+* [ember-django-adapter][ember-django-adapter] - An adapter for working with Ember.js
## Other Resources
@@ -309,7 +309,7 @@ To submit new content, [open an issue][drf-create-issue] or [create a pull reque
[django-rest-framework-proxy]: https://github.com/eofs/django-rest-framework-proxy
[gaiarestframework]: https://github.com/AppsFuel/gaiarestframework
[drf-extensions]: https://github.com/chibisov/drf-extensions
-[ember-data-django-rest-adapter]: https://github.com/toranb/ember-data-django-rest-adapter
+[ember-django-adapter]: https://github.com/dustinfarris/ember-django-adapter
[beginners-guide-to-the-django-rest-framework]: http://code.tutsplus.com/tutorials/beginners-guide-to-the-django-rest-framework--cms-19786
[getting-started-with-django-rest-framework-and-angularjs]: http://blog.kevinastone.com/getting-started-with-django-rest-framework-and-angularjs.html
[end-to-end-web-app-with-django-rest-framework-angularjs]: http://blog.mourafiq.com/post/55034504632/end-to-end-web-app-with-django-rest-framework
diff --git a/docs/tutorial/1-serialization.md b/docs/tutorial/1-serialization.md
index 80e869ea..ceb23a02 100644
--- a/docs/tutorial/1-serialization.md
+++ b/docs/tutorial/1-serialization.md
@@ -97,7 +97,7 @@ The first thing we need to get started on our Web API is to provide a way of ser
class SnippetSerializer(serializers.Serializer):
pk = serializers.IntegerField(read_only=True)
title = serializers.CharField(required=False, allow_blank=True, max_length=100)
- code = serializers.CharField(style={'type': 'textarea'})
+ code = serializers.CharField(style={'base_template': 'textarea.html'})
linenos = serializers.BooleanField(required=False)
language = serializers.ChoiceField(choices=LANGUAGE_CHOICES, default='python')
style = serializers.ChoiceField(choices=STYLE_CHOICES, default='friendly')
diff --git a/docs/tutorial/2-requests-and-responses.md b/docs/tutorial/2-requests-and-responses.md
index 4ca4e264..e2c173d6 100644
--- a/docs/tutorial/2-requests-and-responses.md
+++ b/docs/tutorial/2-requests-and-responses.md
@@ -96,7 +96,7 @@ Notice that we're no longer explicitly tying our requests or responses to a give
## Adding optional format suffixes to our URLs
-To take advantage of the fact that our responses are no longer hardwired to a single content type let's add support for format suffixes to our API endpoints. Using format suffixes gives us URLs that explicitly refer to a given format, and means our API will be able to handle URLs such as [http://example.com/api/items/4.json][json-url].
+To take advantage of the fact that our responses are no longer hardwired to a single content type let's add support for format suffixes to our API endpoints. Using format suffixes gives us URLs that explicitly refer to a given format, and means our API will be able to handle URLs such as [http://example.com/api/items/4/.json][json-url].
Start by adding a `format` keyword argument to both of the views, like so.
diff --git a/docs/tutorial/5-relationships-and-hyperlinked-apis.md b/docs/tutorial/5-relationships-and-hyperlinked-apis.md
index 2841f03e..740a4ce2 100644
--- a/docs/tutorial/5-relationships-and-hyperlinked-apis.md
+++ b/docs/tutorial/5-relationships-and-hyperlinked-apis.md
@@ -138,7 +138,7 @@ After adding all those names into our URLconf, our final `snippets/urls.py` file
The list views for users and code snippets could end up returning quite a lot of instances, so really we'd like to make sure we paginate the results, and allow the API client to step through each of the individual pages.
-We can change the default list style to use pagination, by modifying our `settings.py` file slightly. Add the following setting:
+We can change the default list style to use pagination, by modifying our `tutorial/settings.py` file slightly. Add the following setting:
REST_FRAMEWORK = {
'PAGINATE_BY': 10
diff --git a/requirements.txt b/requirements.txt
index cb044958..bf461179 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -14,6 +14,8 @@ django-filter>=0.9.2
# wheel for PyPI installs
wheel==0.24.0
+# twine for secured PyPI uploads
+twine==1.4.0
# MkDocs for documentation previews/deploys
mkdocs==0.11.1
diff --git a/rest_framework/authentication.py b/rest_framework/authentication.py
index a75cd30c..f0702286 100644
--- a/rest_framework/authentication.py
+++ b/rest_framework/authentication.py
@@ -168,7 +168,7 @@ class TokenAuthentication(BaseAuthentication):
def authenticate_credentials(self, key):
try:
- token = self.model.objects.get(key=key)
+ token = self.model.objects.select_related('user').get(key=key)
except self.model.DoesNotExist:
raise exceptions.AuthenticationFailed(_('Invalid token.'))
diff --git a/rest_framework/decorators.py b/rest_framework/decorators.py
index 325435b3..21de1acf 100644
--- a/rest_framework/decorators.py
+++ b/rest_framework/decorators.py
@@ -18,8 +18,7 @@ def api_view(http_method_names=None):
Decorator that converts a function-based view into an APIView subclass.
Takes a list of allowed methods for the view as an argument.
"""
- if http_method_names is None:
- http_method_names = ['GET']
+ http_method_names = ['GET'] if (http_method_names is None) else http_method_names
def decorator(func):
@@ -109,10 +108,12 @@ def permission_classes(permission_classes):
return decorator
-def detail_route(methods=['get'], **kwargs):
+def detail_route(methods=None, **kwargs):
"""
Used to mark a method on a ViewSet that should be routed for detail requests.
"""
+ methods = ['get'] if (methods is None) else methods
+
def decorator(func):
func.bind_to_methods = methods
func.detail = True
@@ -121,10 +122,12 @@ def detail_route(methods=['get'], **kwargs):
return decorator
-def list_route(methods=['get'], **kwargs):
+def list_route(methods=None, **kwargs):
"""
Used to mark a method on a ViewSet that should be routed for list requests.
"""
+ methods = ['get'] if (methods is None) else methods
+
def decorator(func):
func.bind_to_methods = methods
func.detail = False
diff --git a/rest_framework/pagination.py b/rest_framework/pagination.py
index b3658aca..496500ba 100644
--- a/rest_framework/pagination.py
+++ b/rest_framework/pagination.py
@@ -168,7 +168,9 @@ def _reverse_ordering(ordering_tuple):
Given an order_by tuple such as `('-created', 'uuid')` reverse the
ordering and return a new tuple, eg. `('created', '-uuid')`.
"""
- invert = lambda x: x[1:] if (x.startswith('-')) else '-' + x
+ def invert(x):
+ return x[1:] if (x.startswith('-')) else '-' + x
+
return tuple([invert(item) for item in ordering_tuple])
diff --git a/rest_framework/request.py b/rest_framework/request.py
index 86fb1ef1..081ace23 100644
--- a/rest_framework/request.py
+++ b/rest_framework/request.py
@@ -12,12 +12,13 @@ from __future__ import unicode_literals
from django.conf import settings
from django.http import QueryDict
from django.http.multipartparser import parse_header
+from django.utils import six
from django.utils.datastructures import MultiValueDict
from django.utils.datastructures import MergeDict as DjangoMergeDict
-from django.utils.six import BytesIO
from rest_framework import HTTP_HEADER_ENCODING
from rest_framework import exceptions
from rest_framework.settings import api_settings
+import sys
import warnings
@@ -366,7 +367,7 @@ class Request(object):
elif hasattr(self._request, 'read'):
self._stream = self._request
else:
- self._stream = BytesIO(self.raw_post_data)
+ self._stream = six.BytesIO(self.raw_post_data)
def _perform_form_overloading(self):
"""
@@ -408,7 +409,7 @@ class Request(object):
self._CONTENTTYPE_PARAM in self._data
):
self._content_type = self._data[self._CONTENTTYPE_PARAM]
- self._stream = BytesIO(self._data[self._CONTENT_PARAM].encode(self.parser_context['encoding']))
+ self._stream = six.BytesIO(self._data[self._CONTENT_PARAM].encode(self.parser_context['encoding']))
self._data, self._files, self._full_data = (Empty, Empty, Empty)
def _parse(self):
@@ -489,8 +490,16 @@ class Request(object):
else:
self.auth = None
- def __getattr__(self, attr):
+ def __getattribute__(self, attr):
"""
- Proxy other attributes to the underlying HttpRequest object.
+ If an attribute does not exist on this instance, then we also attempt
+ to proxy it to the underlying HttpRequest object.
"""
- return getattr(self._request, attr)
+ try:
+ return super(Request, self).__getattribute__(attr)
+ except AttributeError:
+ info = sys.exc_info()
+ try:
+ return getattr(self._request, attr)
+ except AttributeError:
+ six.reraise(info[0], info[1], info[2].tb_next)
diff --git a/rest_framework/response.py b/rest_framework/response.py
index 7f90bae1..c21c60a2 100644
--- a/rest_framework/response.py
+++ b/rest_framework/response.py
@@ -86,8 +86,9 @@ class Response(SimpleTemplateResponse):
state = super(Response, self).__getstate__()
for key in (
'accepted_renderer', 'renderer_context', 'resolver_match',
- 'client', 'request', 'wsgi_request', '_closable_objects'
+ 'client', 'request', 'wsgi_request'
):
if key in state:
del state[key]
+ state['_closable_objects'] = []
return state
diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py
index 7235d8c5..c60574d4 100644
--- a/rest_framework/serializers.py
+++ b/rest_framework/serializers.py
@@ -177,7 +177,7 @@ class BaseSerializer(Field):
)
assert hasattr(self, 'initial_data'), (
- 'Cannot call `.is_valid()` as no `data=` keyword argument was'
+ 'Cannot call `.is_valid()` as no `data=` keyword argument was '
'passed when instantiating the serializer instance.'
)
@@ -635,11 +635,11 @@ def raise_errors_on_nested_writes(method_name, serializer, validated_data):
If we don't do this explicitly they'd get a less helpful error when
calling `.save()` on the serializer.
- We don't *automatically* support these sorts of nested writes brecause
+ We don't *automatically* support these sorts of nested writes because
there are too many ambiguities to define a default behavior.
Eg. Suppose we have a `UserSerializer` with a nested profile. How should
- we handle the case of an update, where the `profile` realtionship does
+ we handle the case of an update, where the `profile` relationship does
not exist? Any of the following might be valid:
* Raise an application error.
diff --git a/rest_framework/templatetags/rest_framework.py b/rest_framework/templatetags/rest_framework.py
index a969836f..699ea897 100644
--- a/rest_framework/templatetags/rest_framework.py
+++ b/rest_framework/templatetags/rest_framework.py
@@ -143,7 +143,9 @@ def urlize_quoted_links(text, trim_url_limit=None, nofollow=True, autoescape=Tru
If autoescape is True, the link text and URLs will get autoescaped.
"""
- trim_url = lambda x, limit=trim_url_limit: limit is not None and (len(x) > limit and ('%s...' % x[:max(0, limit - 3)])) or x
+ def trim_url(x, limit=trim_url_limit):
+ return limit is not None and (len(x) > limit and ('%s...' % x[:max(0, limit - 3)])) or x
+
safe_input = isinstance(text, SafeData)
words = word_split_re.split(force_text(text))
for i, word in enumerate(words):
diff --git a/rest_framework/throttling.py b/rest_framework/throttling.py
index 0f10136d..261fc246 100644
--- a/rest_framework/throttling.py
+++ b/rest_framework/throttling.py
@@ -191,7 +191,7 @@ class UserRateThrottle(SimpleRateThrottle):
def get_cache_key(self, request, view):
if request.user.is_authenticated():
- ident = request.user.id
+ ident = request.user.pk
else:
ident = self.get_ident(request)
@@ -239,7 +239,7 @@ class ScopedRateThrottle(SimpleRateThrottle):
with the '.throttle_scope` property of the view.
"""
if request.user.is_authenticated():
- ident = request.user.id
+ ident = request.user.pk
else:
ident = self.get_ident(request)
diff --git a/setup.py b/setup.py
index efe39d8d..4cdcfa86 100755
--- a/setup.py
+++ b/setup.py
@@ -48,8 +48,11 @@ if sys.argv[-1] == 'publish':
if os.system("pip freeze | grep wheel"):
print("wheel not installed.\nUse `pip install wheel`.\nExiting.")
sys.exit()
- os.system("python setup.py sdist upload")
- os.system("python setup.py bdist_wheel upload")
+ if os.system("pip freeze | grep twine"):
+ print("twine not installed.\nUse `pip install twine`.\nExiting.")
+ sys.exit()
+ os.system("python setup.py sdist bdist_wheel")
+ os.system("twine upload dist/*")
print("You probably want to also tag the version now:")
print(" git tag -a %s -m 'version %s'" % (version, version))
print(" git push --tags")
diff --git a/tests/test_authentication.py b/tests/test_authentication.py
index 04c5782e..91e49f9d 100644
--- a/tests/test_authentication.py
+++ b/tests/test_authentication.py
@@ -168,6 +168,15 @@ class TokenAuthTests(TestCase):
response = self.csrf_client.post('/token/', {'example': 'example'}, format='json', HTTP_AUTHORIZATION=auth)
self.assertEqual(response.status_code, status.HTTP_200_OK)
+ def test_post_json_makes_one_db_query(self):
+ """Ensure that authenticating a user using a token performs only one DB query"""
+ auth = "Token " + self.key
+
+ def func_to_test():
+ return self.csrf_client.post('/token/', {'example': 'example'}, format='json', HTTP_AUTHORIZATION=auth)
+
+ self.assertNumQueries(1, func_to_test)
+
def test_post_form_failing_token_auth(self):
"""Ensure POSTing form over token auth without correct credentials fails"""
response = self.csrf_client.post('/token/', {'example': 'example'})
diff --git a/tests/test_relations_hyperlink.py b/tests/test_relations_hyperlink.py
index aede61d2..33b09713 100644
--- a/tests/test_relations_hyperlink.py
+++ b/tests/test_relations_hyperlink.py
@@ -12,7 +12,9 @@ factory = APIRequestFactory()
request = factory.get('/') # Just to ensure we have a request in the serializer context
-dummy_view = lambda request, pk: None
+def dummy_view(request, pk):
+ pass
+
urlpatterns = [
url(r'^dummyurl/(?P<pk>[0-9]+)/$', dummy_view, name='dummy-url'),
diff --git a/tests/test_renderers.py b/tests/test_renderers.py
index f68405f0..60a08225 100644
--- a/tests/test_renderers.py
+++ b/tests/test_renderers.py
@@ -21,8 +21,13 @@ import re
DUMMYSTATUS = status.HTTP_200_OK
DUMMYCONTENT = 'dummycontent'
-RENDERER_A_SERIALIZER = lambda x: ('Renderer A: %s' % x).encode('ascii')
-RENDERER_B_SERIALIZER = lambda x: ('Renderer B: %s' % x).encode('ascii')
+
+def RENDERER_A_SERIALIZER(x):
+ return ('Renderer A: %s' % x).encode('ascii')
+
+
+def RENDERER_B_SERIALIZER(x):
+ return ('Renderer B: %s' % x).encode('ascii')
expected_results = [
diff --git a/tests/test_request.py b/tests/test_request.py
index 02a9b1e2..c274ab69 100644
--- a/tests/test_request.py
+++ b/tests/test_request.py
@@ -249,9 +249,29 @@ class TestUserSetter(TestCase):
login(self.request, self.user)
self.assertEqual(self.wrapped_request.user, self.user)
+ def test_calling_user_fails_when_attribute_error_is_raised(self):
+ """
+ This proves that when an AttributeError is raised inside of the request.user
+ property, that we can handle this and report the true, underlying error.
+ """
+ class AuthRaisesAttributeError(object):
+ def authenticate(self, request):
+ import rest_framework
+ rest_framework.MISSPELLED_NAME_THAT_DOESNT_EXIST
-class TestAuthSetter(TestCase):
+ self.request = Request(factory.get('/'), authenticators=(AuthRaisesAttributeError(),))
+ SessionMiddleware().process_request(self.request)
+ login(self.request, self.user)
+ try:
+ self.request.user
+ except AttributeError as error:
+ self.assertEqual(str(error), "'module' object has no attribute 'MISSPELLED_NAME_THAT_DOESNT_EXIST'")
+ else:
+ assert False, 'AttributeError not raised'
+
+
+class TestAuthSetter(TestCase):
def test_auth_can_be_set(self):
request = Request(factory.get('/'))
request.auth = 'DUMMY'
diff --git a/tests/test_response.py b/tests/test_response.py
index f233ae33..4a9deaa2 100644
--- a/tests/test_response.py
+++ b/tests/test_response.py
@@ -38,8 +38,13 @@ class MockTextMediaRenderer(BaseRenderer):
DUMMYSTATUS = status.HTTP_200_OK
DUMMYCONTENT = 'dummycontent'
-RENDERER_A_SERIALIZER = lambda x: ('Renderer A: %s' % x).encode('ascii')
-RENDERER_B_SERIALIZER = lambda x: ('Renderer B: %s' % x).encode('ascii')
+
+def RENDERER_A_SERIALIZER(x):
+ return ('Renderer A: %s' % x).encode('ascii')
+
+
+def RENDERER_B_SERIALIZER(x):
+ return ('Renderer B: %s' % x).encode('ascii')
class RendererA(BaseRenderer):
diff --git a/tests/test_throttling.py b/tests/test_throttling.py
index cc36a004..50a53b3e 100644
--- a/tests/test_throttling.py
+++ b/tests/test_throttling.py
@@ -188,7 +188,9 @@ class ScopedRateThrottleTests(TestCase):
class XYScopedRateThrottle(ScopedRateThrottle):
TIMER_SECONDS = 0
THROTTLE_RATES = {'x': '3/min', 'y': '1/min'}
- timer = lambda self: self.TIMER_SECONDS
+
+ def timer(self):
+ return self.TIMER_SECONDS
class XView(APIView):
throttle_classes = (XYScopedRateThrottle,)
@@ -290,7 +292,9 @@ class XffTestingBase(TestCase):
class Throttle(ScopedRateThrottle):
THROTTLE_RATES = {'test_limit': '1/day'}
TIMER_SECONDS = 0
- timer = lambda self: self.TIMER_SECONDS
+
+ def timer(self):
+ return self.TIMER_SECONDS
class View(APIView):
throttle_classes = (Throttle,)
diff --git a/tests/test_versioning.py b/tests/test_versioning.py
index 553463d1..90ad8afd 100644
--- a/tests/test_versioning.py
+++ b/tests/test_versioning.py
@@ -32,8 +32,13 @@ class RequestInvalidVersionView(APIView):
factory = APIRequestFactory()
-dummy_view = lambda request: None
-dummy_pk_view = lambda request, pk: None
+
+def dummy_view(request):
+ pass
+
+
+def dummy_pk_view(request, pk):
+ pass
class TestRequestVersion:
diff --git a/tox.ini b/tox.ini
index 129752f3..76f4f09b 100644
--- a/tox.ini
+++ b/tox.ini
@@ -3,7 +3,7 @@ envlist =
py27-{flake8,docs},
{py26,py27}-django14,
{py26,py27,py32,py33,py34}-django{15,16},
- {py27,py32,py33,py34}-django{17,18alpha,master}
+ {py27,py32,py33,py34}-django{17,18alpha}
[testenv]
commands = ./runtests.py --fast
@@ -15,7 +15,6 @@ deps =
django16: Django==1.6.3 # Should track minimum supported
django17: Django==1.7.2 # Should track maximum supported
django18alpha: https://www.djangoproject.com/download/1.8a1/tarball/
- djangomaster: https://github.com/django/django/zipball/master
django-guardian==1.2.4
pytest-django==2.8.0
django-filter==0.9.2