mirror of
https://github.com/encode/django-rest-framework.git
synced 2024-11-22 17:47:04 +03:00
Merge branch 'master' into version-3.1
This commit is contained in:
commit
c8d88c8c8a
|
@ -112,6 +112,8 @@ Two options are currently used in HTML form generation, `'input_type'` and `'bas
|
|||
|
||||
A boolean representation.
|
||||
|
||||
When using HTML encoded form input be aware that omitting a value will always be treated as setting a field to `False`, even if it has a `default=True` option specified. This is because HTML checkbox inputs represent the unchecked state by omitting the value, so REST framework treats omission as if it is an empty checkbox input.
|
||||
|
||||
Corresponds to `django.db.models.fields.BooleanField`.
|
||||
|
||||
**Signature:** `BooleanField()`
|
||||
|
|
|
@ -10,12 +10,24 @@ Together with [authentication] and [throttling], permissions determine whether a
|
|||
|
||||
Permission checks are always run at the very start of the view, before any other code is allowed to proceed. Permission checks will typically use the authentication information in the `request.user` and `request.auth` properties to determine if the incoming request should be permitted.
|
||||
|
||||
Permissions are used to grant or deny access different classes of users to different parts of the API.
|
||||
|
||||
The simplest style of permission would be to allow access to any authenticated user, and deny access to any unauthenticated user. This corresponds the `IsAuthenticated` class in REST framework.
|
||||
|
||||
A slightly less strict style of permission would be to allow full access to authenticated users, but allow read-only access to unauthenticated users. This corresponds to the `IsAuthenticatedOrReadOnly` class in REST framework.
|
||||
|
||||
## How permissions are determined
|
||||
|
||||
Permissions in REST framework are always defined as a list of permission classes.
|
||||
|
||||
Before running the main body of the view each permission in the list is checked.
|
||||
If any permission check fails an `exceptions.PermissionDenied` exception will be raised, and the main body of the view will not run.
|
||||
If any permission check fails an `exceptions.PermissionDenied` or `exceptions.NotAuthenticated` exception will be raised, and the main body of the view will not run.
|
||||
|
||||
When the permissions checks fail either a "403 Forbidden" or a "401 Unauthorized" response will be returned, according to the following rules:
|
||||
|
||||
* The request was successfully authenticated, but permission was denied. *— An HTTP 403 Forbidden response will be returned.*
|
||||
* The request was not successfully authenticated, and the highest priority authentication class *does not* use `WWW-Authenticate` headers. *— An HTTP 403 Forbidden response will be returned.*
|
||||
* The request was not successfully authenticated, and the highest priority authentication class *does* use `WWW-Authenticate` headers. *— An HTTP 401 Unauthorized response, with an appropriate `WWW-Authenticate` header will be returned.*
|
||||
|
||||
## Object level permissions
|
||||
|
||||
|
|
|
@ -397,7 +397,7 @@ We could define a custom field that could be used to serialize tagged instances,
|
|||
return 'Note: ' + value.text
|
||||
raise Exception('Unexpected type of tagged object')
|
||||
|
||||
If you need the target of the relationship to have a nested representation, you can use the required serializers inside the `.to_native()` method:
|
||||
If you need the target of the relationship to have a nested representation, you can use the required serializers inside the `.to_representation()` method:
|
||||
|
||||
def to_representation(self, value):
|
||||
"""
|
||||
|
|
|
@ -22,11 +22,13 @@ The serializers in REST framework work very similarly to Django's `Form` and `Mo
|
|||
|
||||
Let's start by creating a simple object we can use for example purposes:
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
class Comment(object):
|
||||
def __init__(self, email, content, created=None):
|
||||
self.email = email
|
||||
self.content = content
|
||||
self.created = created or datetime.datetime.now()
|
||||
self.created = created or datetime.now()
|
||||
|
||||
comment = Comment(email='leila@example.com', content='foo bar')
|
||||
|
||||
|
@ -61,10 +63,10 @@ At this point we've translated the model instance into Python native datatypes.
|
|||
|
||||
Deserialization is similar. First we parse a stream into Python native datatypes...
|
||||
|
||||
from StringIO import StringIO
|
||||
from django.utils.six import BytesIO
|
||||
from rest_framework.parsers import JSONParser
|
||||
|
||||
stream = StringIO(json)
|
||||
stream = BytesIO(json)
|
||||
data = JSONParser().parse(stream)
|
||||
|
||||
...then we restore those native datatypes into a dictionary of validated data.
|
||||
|
@ -240,6 +242,12 @@ Serializer classes can also include reusable validators that are applied to the
|
|||
|
||||
For more information see the [validators documentation](validators.md).
|
||||
|
||||
## Accessing the initial data and instance
|
||||
|
||||
When passing an initial object or queryset to a serializer instance, the object will be made available as `.instance`. If no initial object is passed then the `.instance` attribute will be `None`.
|
||||
|
||||
When passing data to a serializer instance, the unmodified data will be made available as `.initial_data`. If the data keyword argument is not passed then the `.initial_data` attribute will not exist.
|
||||
|
||||
## Partial updates
|
||||
|
||||
By default, serializers must be passed values for all required fields or they will raise validation errors. You can use the `partial` argument in order to allow partial updates.
|
||||
|
|
|
@ -40,9 +40,21 @@ You can determine your currently installed version using `pip freeze`:
|
|||
|
||||
## 3.0.x series
|
||||
|
||||
### 3.0.2
|
||||
|
||||
**Date**: [17th December 2014][3.0.2-milestone].
|
||||
|
||||
* Ensure `request.user` is made available to response middleware. ([#2155][gh2155])
|
||||
* `Client.logout()` also cancels any existing `force_authenticate`. ([#2218][gh2218], [#2259][gh2259])
|
||||
* Extra assertions and better checks to preventing incorrect serializer API use. ([#2228][gh2228], [#2234][gh2234], [#2262][gh2262], [#2263][gh2263], [#2266][gh2266], [#2267][gh2267], [#2289][gh2289], [#2291][gh2291])
|
||||
* Fixed `min_length` message for `CharField`. ([#2255][gh2255])
|
||||
* Fix `UnicodeDecodeError`, which can occur on serializer `repr`. ([#2270][gh2270], [#2279][gh2279])
|
||||
* Fix empty HTML values when a default is provided. ([#2280][gh2280], [#2294][gh2294])
|
||||
* Fix `SlugRelatedField` raising `UnicodeEncodeError` when used as a multiple choice input. ([#2290][gh2290])
|
||||
|
||||
### 3.0.1
|
||||
|
||||
**Date**: [December 2014][3.0.1-milestone].
|
||||
**Date**: [11th December 2014][3.0.1-milestone].
|
||||
|
||||
* More helpful error message when the default Serializer `create()` fails. ([#2013][gh2013])
|
||||
* Raise error when attempting to save serializer if data is not valid. ([#2098][gh2098])
|
||||
|
@ -665,9 +677,11 @@ For older release notes, [please see the GitHub repo](old-release-notes).
|
|||
[ticket-582]: https://github.com/tomchristie/django-rest-framework/issues/582
|
||||
[rfc-6266]: http://tools.ietf.org/html/rfc6266#section-4.3
|
||||
[old-release-notes]: https://github.com/tomchristie/django-rest-framework/blob/2.4.4/docs/topics/release-notes.md#04x-series
|
||||
|
||||
[3.0.1-milestone]: https://github.com/tomchristie/django-rest-framework/issues?q=milestone%3A%223.0.1+Release%22
|
||||
[3.0.2-milestone]: https://github.com/tomchristie/django-rest-framework/issues?q=milestone%3A%223.0.2+Release%22
|
||||
|
||||
|
||||
<!-- 3.0.1 -->
|
||||
[gh2013]: https://github.com/tomchristie/django-rest-framework/issues/2013
|
||||
[gh2098]: https://github.com/tomchristie/django-rest-framework/issues/2098
|
||||
[gh2109]: https://github.com/tomchristie/django-rest-framework/issues/2109
|
||||
|
@ -697,3 +711,21 @@ For older release notes, [please see the GitHub repo](old-release-notes).
|
|||
[gh2242]: https://github.com/tomchristie/django-rest-framework/issues/2242
|
||||
[gh2243]: https://github.com/tomchristie/django-rest-framework/issues/2243
|
||||
[gh2244]: https://github.com/tomchristie/django-rest-framework/issues/2244
|
||||
<!-- 3.0.2 -->
|
||||
[gh2155]: https://github.com/tomchristie/django-rest-framework/issues/2155
|
||||
[gh2218]: https://github.com/tomchristie/django-rest-framework/issues/2218
|
||||
[gh2228]: https://github.com/tomchristie/django-rest-framework/issues/2228
|
||||
[gh2234]: https://github.com/tomchristie/django-rest-framework/issues/2234
|
||||
[gh2255]: https://github.com/tomchristie/django-rest-framework/issues/2255
|
||||
[gh2259]: https://github.com/tomchristie/django-rest-framework/issues/2259
|
||||
[gh2262]: https://github.com/tomchristie/django-rest-framework/issues/2262
|
||||
[gh2263]: https://github.com/tomchristie/django-rest-framework/issues/2263
|
||||
[gh2266]: https://github.com/tomchristie/django-rest-framework/issues/2266
|
||||
[gh2267]: https://github.com/tomchristie/django-rest-framework/issues/2267
|
||||
[gh2270]: https://github.com/tomchristie/django-rest-framework/issues/2270
|
||||
[gh2279]: https://github.com/tomchristie/django-rest-framework/issues/2279
|
||||
[gh2280]: https://github.com/tomchristie/django-rest-framework/issues/2280
|
||||
[gh2289]: https://github.com/tomchristie/django-rest-framework/issues/2289
|
||||
[gh2290]: https://github.com/tomchristie/django-rest-framework/issues/2290
|
||||
[gh2291]: https://github.com/tomchristie/django-rest-framework/issues/2291
|
||||
[gh2294]: https://github.com/tomchristie/django-rest-framework/issues/2294
|
||||
|
|
|
@ -161,9 +161,7 @@ At this point we've translated the model instance into Python native datatypes.
|
|||
|
||||
Deserialization is similar. First we parse a stream into Python native datatypes...
|
||||
|
||||
# This import will use either `StringIO.StringIO` or `io.BytesIO`
|
||||
# as appropriate, depending on if we're running Python 2 or Python 3.
|
||||
from rest_framework.compat import BytesIO
|
||||
from django.utils.six import BytesIO
|
||||
|
||||
stream = BytesIO(content)
|
||||
data = JSONParser().parse(stream)
|
||||
|
@ -200,7 +198,7 @@ Open the file `snippets/serializers.py` again, and edit the `SnippetSerializer`
|
|||
model = Snippet
|
||||
fields = ('id', 'title', 'code', 'linenos', 'language', 'style')
|
||||
|
||||
One nice property that serializers have is that you can inspect all the fields in a serializer instance, by printing it's representation. Open the Django shell with `python manange.py shell`, then try the following:
|
||||
One nice property that serializers have is that you can inspect all the fields in a serializer instance, by printing it's representation. Open the Django shell with `python manage.py shell`, then try the following:
|
||||
|
||||
>>> from snippets.serializers import SnippetSerializer
|
||||
>>> serializer = SnippetSerializer()
|
||||
|
|
|
@ -206,7 +206,7 @@ If we try to create a snippet without authenticating, we'll get an error:
|
|||
|
||||
We can make a successful request by including the username and password of one of the users we created earlier.
|
||||
|
||||
http POST -a tom:password http://127.0.0.1:8000/snippets/ code="print 789"
|
||||
http -a tom:password POST http://127.0.0.1:8000/snippets/ code="print 789"
|
||||
|
||||
{
|
||||
"id": 5,
|
||||
|
|
|
@ -44,8 +44,8 @@ Next we're going to replace the `SnippetList`, `SnippetDetail` and `SnippetHighl
|
|||
snippet = self.get_object()
|
||||
return Response(snippet.highlighted)
|
||||
|
||||
def pre_save(self, obj):
|
||||
obj.owner = self.request.user
|
||||
def perform_create(self, serializer):
|
||||
serializer.save(owner=self.request.user)
|
||||
|
||||
This time we've used the `ModelViewSet` class in order to get the complete set of default read and write operations.
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@ Create a new Django project named `tutorial`, then start a new app called `quick
|
|||
pip install djangorestframework
|
||||
|
||||
# Set up a new project with a single application
|
||||
django-admin.py startproject tutorial .
|
||||
django-admin.py startproject tutorial . # Note the trailing '.' character
|
||||
cd tutorial
|
||||
django-admin.py startapp quickstart
|
||||
cd ..
|
||||
|
|
|
@ -8,7 +8,7 @@ ______ _____ _____ _____ __
|
|||
"""
|
||||
|
||||
__title__ = 'Django REST framework'
|
||||
__version__ = '3.0.1'
|
||||
__version__ = '3.0.2'
|
||||
__author__ = 'Tom Christie'
|
||||
__license__ = 'BSD 2-Clause'
|
||||
__copyright__ = 'Copyright 2011-2014 Tom Christie'
|
||||
|
|
|
@ -5,8 +5,8 @@ In addition Django's built in 403 and 404 exceptions are handled.
|
|||
(`django.http.Http404` and `django.core.exceptions.PermissionDenied`)
|
||||
"""
|
||||
from __future__ import unicode_literals
|
||||
from django.utils import six
|
||||
from django.utils.encoding import force_text
|
||||
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.utils.translation import ungettext_lazy
|
||||
from rest_framework import status
|
||||
|
@ -66,7 +66,7 @@ class ValidationError(APIException):
|
|||
self.detail = _force_text_recursive(detail)
|
||||
|
||||
def __str__(self):
|
||||
return str(self.detail)
|
||||
return six.text_type(self.detail)
|
||||
|
||||
|
||||
class ParseError(APIException):
|
||||
|
|
|
@ -184,8 +184,11 @@ class Field(object):
|
|||
self.style = {} if style is None else style
|
||||
self.allow_null = allow_null
|
||||
|
||||
if allow_null and self.default_empty_html is empty:
|
||||
self.default_empty_html = None
|
||||
if self.default_empty_html is not empty:
|
||||
if not required:
|
||||
self.default_empty_html = empty
|
||||
elif default is not empty:
|
||||
self.default_empty_html = default
|
||||
|
||||
if validators is not None:
|
||||
self.validators = validators[:]
|
||||
|
@ -557,6 +560,11 @@ class CharField(Field):
|
|||
message = self.error_messages['min_length'].format(min_length=min_length)
|
||||
self.validators.append(MinLengthValidator(min_length, message=message))
|
||||
|
||||
if self.allow_null and (not self.allow_blank) and (self.default is empty):
|
||||
# HTML input cannot represent `None` values, so we need to
|
||||
# forcibly coerce empty HTML values to `None` if `allow_null=True`.
|
||||
self.default_empty_html = None
|
||||
|
||||
def run_validation(self, data=empty):
|
||||
# Test for the empty string here so that it does not get validated,
|
||||
# and so that subclasses do not need to handle it explicitly
|
||||
|
|
|
@ -79,16 +79,14 @@ class GenericAPIView(views.APIView):
|
|||
'view': self
|
||||
}
|
||||
|
||||
def get_serializer(self, instance=None, data=None, many=False, partial=False):
|
||||
def get_serializer(self, *args, **kwargs):
|
||||
"""
|
||||
Return the serializer instance that should be used for validating and
|
||||
deserializing input, and for serializing output.
|
||||
"""
|
||||
serializer_class = self.get_serializer_class()
|
||||
context = self.get_serializer_context()
|
||||
return serializer_class(
|
||||
instance, data=data, many=many, partial=partial, context=context
|
||||
)
|
||||
kwargs['context'] = self.get_serializer_context()
|
||||
return serializer_class(*args, **kwargs)
|
||||
|
||||
def get_pagination_serializer(self, page):
|
||||
"""
|
||||
|
|
|
@ -1,13 +1,15 @@
|
|||
from django.utils.encoding import smart_text
|
||||
from rest_framework.fields import get_attribute, empty, Field
|
||||
from rest_framework.reverse import reverse
|
||||
from rest_framework.utils import html
|
||||
# coding: utf-8
|
||||
from __future__ import unicode_literals
|
||||
from django.core.exceptions import ObjectDoesNotExist, ImproperlyConfigured
|
||||
from django.core.urlresolvers import resolve, get_script_prefix, NoReverseMatch, Resolver404
|
||||
from django.db.models.query import QuerySet
|
||||
from django.utils import six
|
||||
from django.utils.encoding import smart_text
|
||||
from django.utils.six.moves.urllib import parse as urlparse
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from rest_framework.fields import get_attribute, empty, Field
|
||||
from rest_framework.reverse import reverse
|
||||
from rest_framework.utils import html
|
||||
|
||||
|
||||
class PKOnlyObject(object):
|
||||
|
@ -103,8 +105,8 @@ class RelatedField(Field):
|
|||
def choices(self):
|
||||
return dict([
|
||||
(
|
||||
str(self.to_representation(item)),
|
||||
str(item)
|
||||
six.text_type(self.to_representation(item)),
|
||||
six.text_type(item)
|
||||
)
|
||||
for item in self.queryset.all()
|
||||
])
|
||||
|
@ -364,8 +366,8 @@ class ManyRelatedField(Field):
|
|||
]
|
||||
return dict([
|
||||
(
|
||||
str(item_representation),
|
||||
str(item) + ' - ' + str(item_representation)
|
||||
six.text_type(item_representation),
|
||||
six.text_type(item) + ' - ' + six.text_type(item_representation)
|
||||
)
|
||||
for item, item_representation in items_and_representations
|
||||
])
|
||||
|
|
|
@ -435,12 +435,12 @@ class BrowsableAPIRenderer(BaseRenderer):
|
|||
# serializer instance, rather than dynamically creating a new one.
|
||||
if request.method == method and serializer is not None:
|
||||
try:
|
||||
data = request.data
|
||||
kwargs = {'data': request.data}
|
||||
except ParseError:
|
||||
data = None
|
||||
kwargs = {}
|
||||
existing_serializer = serializer
|
||||
else:
|
||||
data = None
|
||||
kwargs = {}
|
||||
existing_serializer = None
|
||||
|
||||
with override_method(view, request, method) as request:
|
||||
|
@ -460,11 +460,13 @@ class BrowsableAPIRenderer(BaseRenderer):
|
|||
serializer = existing_serializer
|
||||
else:
|
||||
if method in ('PUT', 'PATCH'):
|
||||
serializer = view.get_serializer(instance=instance, data=data)
|
||||
serializer = view.get_serializer(instance=instance, **kwargs)
|
||||
else:
|
||||
serializer = view.get_serializer(data=data)
|
||||
if data is not None:
|
||||
serializer.is_valid()
|
||||
serializer = view.get_serializer(**kwargs)
|
||||
|
||||
if hasattr(serializer, 'initial_data'):
|
||||
serializer.is_valid()
|
||||
|
||||
form_renderer = self.form_renderer_class()
|
||||
return form_renderer.render(
|
||||
serializer.data,
|
||||
|
|
|
@ -277,8 +277,12 @@ class Request(object):
|
|||
Sets the user on the current request. This is necessary to maintain
|
||||
compatibility with django.contrib.auth where the user property is
|
||||
set in the login and logout functions.
|
||||
|
||||
Note that we also set the user on Django's underlying `HttpRequest`
|
||||
instance, ensuring that it is available to any middleware in the stack.
|
||||
"""
|
||||
self._user = value
|
||||
self._request.user = value
|
||||
|
||||
@property
|
||||
def auth(self):
|
||||
|
@ -297,6 +301,7 @@ class Request(object):
|
|||
request, such as an authentication token.
|
||||
"""
|
||||
self._auth = value
|
||||
self._request.auth = value
|
||||
|
||||
@property
|
||||
def successful_authenticator(self):
|
||||
|
@ -456,7 +461,7 @@ class Request(object):
|
|||
|
||||
if user_auth_tuple is not None:
|
||||
self._authenticator = authenticator
|
||||
self._user, self._auth = user_auth_tuple
|
||||
self.user, self.auth = user_auth_tuple
|
||||
return
|
||||
|
||||
self._not_authenticated()
|
||||
|
@ -471,14 +476,14 @@ class Request(object):
|
|||
self._authenticator = None
|
||||
|
||||
if api_settings.UNAUTHENTICATED_USER:
|
||||
self._user = api_settings.UNAUTHENTICATED_USER()
|
||||
self.user = api_settings.UNAUTHENTICATED_USER()
|
||||
else:
|
||||
self._user = None
|
||||
self.user = None
|
||||
|
||||
if api_settings.UNAUTHENTICATED_TOKEN:
|
||||
self._auth = api_settings.UNAUTHENTICATED_TOKEN()
|
||||
self.auth = api_settings.UNAUTHENTICATED_TOKEN()
|
||||
else:
|
||||
self._auth = None
|
||||
self.auth = None
|
||||
|
||||
def __getattr__(self, attr):
|
||||
"""
|
||||
|
|
|
@ -58,11 +58,31 @@ class BaseSerializer(Field):
|
|||
"""
|
||||
The BaseSerializer class provides a minimal class which may be used
|
||||
for writing custom serializer implementations.
|
||||
|
||||
Note that we strongly restrict the ordering of operations/properties
|
||||
that may be used on the serializer in order to enforce correct usage.
|
||||
|
||||
In particular, if a `data=` argument is passed then:
|
||||
|
||||
.is_valid() - Available.
|
||||
.initial_data - Available.
|
||||
.validated_data - Only available after calling `is_valid()`
|
||||
.errors - Only available after calling `is_valid()`
|
||||
.data - Only available after calling `is_valid()`
|
||||
|
||||
If a `data=` argument is not passed then:
|
||||
|
||||
.is_valid() - Not available.
|
||||
.initial_data - Not available.
|
||||
.validated_data - Not available.
|
||||
.errors - Not available.
|
||||
.data - Available.
|
||||
"""
|
||||
|
||||
def __init__(self, instance=None, data=None, **kwargs):
|
||||
def __init__(self, instance=None, data=empty, **kwargs):
|
||||
self.instance = instance
|
||||
self._initial_data = data
|
||||
if data is not empty:
|
||||
self.initial_data = data
|
||||
self.partial = kwargs.pop('partial', False)
|
||||
self._context = kwargs.pop('context', {})
|
||||
kwargs.pop('many', None)
|
||||
|
@ -156,9 +176,14 @@ class BaseSerializer(Field):
|
|||
(self.__class__.__module__, self.__class__.__name__)
|
||||
)
|
||||
|
||||
assert hasattr(self, 'initial_data'), (
|
||||
'Cannot call `.is_valid()` as no `data=` keyword argument was'
|
||||
'passed when instantiating the serializer instance.'
|
||||
)
|
||||
|
||||
if not hasattr(self, '_validated_data'):
|
||||
try:
|
||||
self._validated_data = self.run_validation(self._initial_data)
|
||||
self._validated_data = self.run_validation(self.initial_data)
|
||||
except ValidationError as exc:
|
||||
self._validated_data = {}
|
||||
self._errors = exc.detail
|
||||
|
@ -172,6 +197,16 @@ class BaseSerializer(Field):
|
|||
|
||||
@property
|
||||
def data(self):
|
||||
if hasattr(self, 'initial_data') and not hasattr(self, '_validated_data'):
|
||||
msg = (
|
||||
'When a serializer is passed a `data` keyword argument you '
|
||||
'must call `.is_valid()` before attempting to access the '
|
||||
'serialized `.data` representation.\n'
|
||||
'You should either call `.is_valid()` first, '
|
||||
'or access `.initial_data` instead.'
|
||||
)
|
||||
raise AssertionError(msg)
|
||||
|
||||
if not hasattr(self, '_data'):
|
||||
if self.instance is not None and not getattr(self, '_errors', None):
|
||||
self._data = self.to_representation(self.instance)
|
||||
|
@ -295,11 +330,11 @@ class Serializer(BaseSerializer):
|
|||
return getattr(getattr(self, 'Meta', None), 'validators', [])
|
||||
|
||||
def get_initial(self):
|
||||
if self._initial_data is not None:
|
||||
if hasattr(self, 'initial_data'):
|
||||
return OrderedDict([
|
||||
(field_name, field.get_value(self._initial_data))
|
||||
(field_name, field.get_value(self.initial_data))
|
||||
for field_name, field in self.fields.items()
|
||||
if field.get_value(self._initial_data) is not empty
|
||||
if field.get_value(self.initial_data) is not empty
|
||||
and not field.read_only
|
||||
])
|
||||
|
||||
|
@ -447,8 +482,8 @@ class ListSerializer(BaseSerializer):
|
|||
self.child.bind(field_name='', parent=self)
|
||||
|
||||
def get_initial(self):
|
||||
if self._initial_data is not None:
|
||||
return self.to_representation(self._initial_data)
|
||||
if hasattr(self, 'initial_data'):
|
||||
return self.to_representation(self.initial_data)
|
||||
return []
|
||||
|
||||
def get_value(self, dictionary):
|
||||
|
|
3
setup.py
3
setup.py
|
@ -59,6 +59,9 @@ version = get_version('rest_framework')
|
|||
|
||||
|
||||
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")
|
||||
print("You probably want to also tag the version now:")
|
||||
|
|
|
@ -22,7 +22,7 @@ class TestSimpleBoundField:
|
|||
amount = serializers.IntegerField()
|
||||
|
||||
serializer = ExampleSerializer(data={'text': 'abc', 'amount': 123})
|
||||
|
||||
assert serializer.is_valid()
|
||||
assert serializer['text'].value == 'abc'
|
||||
assert serializer['text'].errors is None
|
||||
assert serializer['text'].name == 'text'
|
||||
|
|
|
@ -215,6 +215,48 @@ class TestBooleanHTMLInput:
|
|||
assert serializer.validated_data == {'archived': False}
|
||||
|
||||
|
||||
class MockHTMLDict(dict):
|
||||
"""
|
||||
This class mocks up a dictionary like object, that behaves
|
||||
as if it was returned for multipart or urlencoded data.
|
||||
"""
|
||||
getlist = None
|
||||
|
||||
|
||||
class TestCharHTMLInput:
|
||||
def test_empty_html_checkbox(self):
|
||||
class TestSerializer(serializers.Serializer):
|
||||
message = serializers.CharField(default='happy')
|
||||
|
||||
serializer = TestSerializer(data=MockHTMLDict())
|
||||
assert serializer.is_valid()
|
||||
assert serializer.validated_data == {'message': 'happy'}
|
||||
|
||||
def test_empty_html_checkbox_allow_null(self):
|
||||
class TestSerializer(serializers.Serializer):
|
||||
message = serializers.CharField(allow_null=True)
|
||||
|
||||
serializer = TestSerializer(data=MockHTMLDict())
|
||||
assert serializer.is_valid()
|
||||
assert serializer.validated_data == {'message': None}
|
||||
|
||||
def test_empty_html_checkbox_allow_null_allow_blank(self):
|
||||
class TestSerializer(serializers.Serializer):
|
||||
message = serializers.CharField(allow_null=True, allow_blank=True)
|
||||
|
||||
serializer = TestSerializer(data=MockHTMLDict({}))
|
||||
assert serializer.is_valid()
|
||||
assert serializer.validated_data == {'message': ''}
|
||||
|
||||
def test_empty_html_required_false(self):
|
||||
class TestSerializer(serializers.Serializer):
|
||||
message = serializers.CharField(required=False)
|
||||
|
||||
serializer = TestSerializer(data=MockHTMLDict())
|
||||
assert serializer.is_valid()
|
||||
assert serializer.validated_data == {}
|
||||
|
||||
|
||||
class TestCreateOnlyDefault:
|
||||
def setup(self):
|
||||
default = serializers.CreateOnlyDefault('2001-01-01')
|
||||
|
|
37
tests/test_middleware.py
Normal file
37
tests/test_middleware.py
Normal file
|
@ -0,0 +1,37 @@
|
|||
|
||||
from django.conf.urls import patterns, url
|
||||
from django.contrib.auth.models import User
|
||||
from rest_framework.authentication import TokenAuthentication
|
||||
from rest_framework.authtoken.models import Token
|
||||
from rest_framework.test import APITestCase
|
||||
from rest_framework.views import APIView
|
||||
|
||||
|
||||
urlpatterns = patterns(
|
||||
'',
|
||||
url(r'^$', APIView.as_view(authentication_classes=(TokenAuthentication,))),
|
||||
)
|
||||
|
||||
|
||||
class MyMiddleware(object):
|
||||
|
||||
def process_response(self, request, response):
|
||||
assert hasattr(request, 'user'), '`user` is not set on request'
|
||||
assert request.user.is_authenticated(), '`user` is not authenticated'
|
||||
return response
|
||||
|
||||
|
||||
class TestMiddleware(APITestCase):
|
||||
|
||||
urls = 'tests.test_middleware'
|
||||
|
||||
def test_middleware_can_access_user_when_processing_response(self):
|
||||
user = User.objects.create_user('john', 'john@example.com', 'password')
|
||||
key = 'abcd1234'
|
||||
Token.objects.create(key=key, user=user)
|
||||
|
||||
with self.settings(
|
||||
MIDDLEWARE_CLASSES=('tests.test_middleware.MyMiddleware',)
|
||||
):
|
||||
auth = 'Token ' + key
|
||||
self.client.get('/', HTTP_AUTHORIZATION=auth)
|
|
@ -224,7 +224,8 @@ class TestUserSetter(TestCase):
|
|||
def setUp(self):
|
||||
# Pass request object through session middleware so session is
|
||||
# available to login and logout functions
|
||||
self.request = Request(factory.get('/'))
|
||||
self.wrapped_request = factory.get('/')
|
||||
self.request = Request(self.wrapped_request)
|
||||
SessionMiddleware().process_request(self.request)
|
||||
|
||||
User.objects.create_user('ringo', 'starr@thebeatles.com', 'yellow')
|
||||
|
@ -244,6 +245,10 @@ class TestUserSetter(TestCase):
|
|||
logout(self.request)
|
||||
self.assertTrue(self.request.user.is_anonymous())
|
||||
|
||||
def test_logged_in_user_is_set_on_wrapped_request(self):
|
||||
login(self.request, self.user)
|
||||
self.assertEqual(self.wrapped_request.user, self.user)
|
||||
|
||||
|
||||
class TestAuthSetter(TestCase):
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user