aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--README.md2
-rw-r--r--docs/api-guide/fields.md8
-rw-r--r--docs/api-guide/relations.md24
-rw-r--r--docs/api-guide/throttling.md9
-rw-r--r--docs/index.md2
-rw-r--r--docs/topics/2.2-announcement.md9
-rw-r--r--docs/topics/credits.md6
-rw-r--r--docs/topics/release-notes.md2
-rw-r--r--rest_framework/compat.py2
-rw-r--r--rest_framework/fields.py40
-rw-r--r--rest_framework/serializers.py1
-rw-r--r--rest_framework/tests/fields.py48
-rw-r--r--rest_framework/views.py5
13 files changed, 128 insertions, 30 deletions
diff --git a/README.md b/README.md
index 861c677c..7b7d1d47 100644
--- a/README.md
+++ b/README.md
@@ -26,7 +26,7 @@ There is also a sandbox API you can use for testing purposes, [available here][s
# Requirements
-* Python (2.6, 2.7, 3.2, 3.3)
+* Python (2.6.5+, 2.7, 3.2, 3.3)
* Django (1.3, 1.4, 1.5)
**Optional:**
diff --git a/docs/api-guide/fields.md b/docs/api-guide/fields.md
index 3f8a36e2..8c28273b 100644
--- a/docs/api-guide/fields.md
+++ b/docs/api-guide/fields.md
@@ -199,10 +199,16 @@ If you want to override this behavior, you'll need to declare the `DateTimeField
class CommentSerializer(serializers.ModelSerializer):
created = serializers.DateTimeField()
-
+
class Meta:
model = Comment
+## TimeField
+
+A time representation.
+
+Corresponds to `django.db.models.fields.TimeField`
+
## IntegerField
An integer representation.
diff --git a/docs/api-guide/relations.md b/docs/api-guide/relations.md
index 25fca475..623fe1a9 100644
--- a/docs/api-guide/relations.md
+++ b/docs/api-guide/relations.md
@@ -43,7 +43,7 @@ In order to explain the various types of relational fields, we'll use a couple o
For example, the following serializer.
- class AlbumSerializer(serializer.ModelSerializer):
+ class AlbumSerializer(serializers.ModelSerializer):
tracks = RelatedField(many=True)
class Meta:
@@ -75,7 +75,7 @@ This field is read only.
For example, the following serializer:
- class AlbumSerializer(serializer.ModelSerializer):
+ class AlbumSerializer(serializers.ModelSerializer):
tracks = PrimaryKeyRelatedField(many=True, read_only=True)
class Meta:
@@ -109,7 +109,7 @@ By default this field is read-write, although you can change this behavior using
For example, the following serializer:
- class AlbumSerializer(serializer.ModelSerializer):
+ class AlbumSerializer(serializers.ModelSerializer):
tracks = HyperlinkedRelatedField(many=True, read_only=True,
view_name='track-detail')
@@ -149,7 +149,7 @@ By default this field is read-write, although you can change this behavior using
For example, the following serializer:
- class AlbumSerializer(serializer.ModelSerializer):
+ class AlbumSerializer(serializers.ModelSerializer):
tracks = SlugRelatedField(many=True, read_only=True, slug_field='title')
class Meta:
@@ -223,12 +223,12 @@ Note that nested relationships are currently read-only. For read-write relation
For example, the following serializer:
- class TrackSerializer(serializer.ModelSerializer):
+ class TrackSerializer(serializers.ModelSerializer):
class Meta:
model = Track
fields = ('order', 'title')
- class AlbumSerializer(serializer.ModelSerializer):
+ class AlbumSerializer(serializers.ModelSerializer):
tracks = TrackSerializer(many=True)
class Meta:
@@ -265,7 +265,7 @@ For, example, we could define a relational field, to serialize a track to a cust
duration = time.strftime('%M:%S', time.gmtime(value.duration))
return 'Track %d: %s (%s)' % (value.order, value.name, duration)
- class AlbumSerializer(serializer.ModelSerializer):
+ class AlbumSerializer(serializers.ModelSerializer):
tracks = TrackListingField(many=True)
class Meta:
@@ -295,14 +295,14 @@ Note that reverse relationships are not automatically generated by the `ModelSer
**The following will not work:**
- class AlbumSerializer(serializer.ModelSerializer):
+ class AlbumSerializer(serializers.ModelSerializer):
class Meta:
fields = ('tracks', ...)
Instead, you must explicitly add it to the serializer. For example:
- class AlbumSerializer(serializer.ModelSerializer):
- tracks = serializers.PrimaryKeyRelationship(many=True)
+ class AlbumSerializer(serializers.ModelSerializer):
+ tracks = serializers.PrimaryKeyRelatedField(many=True)
...
By default, the field will uses the same accessor as it's field name to retrieve the relationship, so in this example, `Album` instances would need to have the `tracks` attribute for this relationship to work.
@@ -315,8 +315,8 @@ The best way to ensure this is typically to make sure that the relationship on t
Alternatively, you can use the `source` argument on the serializer field, to use a different accessor attribute than the field name. For example.
- class AlbumSerializer(serializer.ModelSerializer):
- tracks = serializers.PrimaryKeyRelationship(many=True, source='track_set')
+ class AlbumSerializer(serializers.ModelSerializer):
+ tracks = serializers.PrimaryKeyRelatedField(many=True, source='track_set')
See the Django documentation on [reverse relationships][reverse-relationships] for more details.
diff --git a/docs/api-guide/throttling.md b/docs/api-guide/throttling.md
index 923593bc..1abd49f4 100644
--- a/docs/api-guide/throttling.md
+++ b/docs/api-guide/throttling.md
@@ -6,8 +6,6 @@
>
> [Twitter API rate limiting response][cite]
-[cite]: https://dev.twitter.com/docs/error-codes-responses
-
Throttling is similar to [permissions], in that it determines if a request should be authorized. Throttles indicate a temporary state, and are used to control the rate of requests that clients can make to an API.
As with permissions, multiple throttles may be used. Your API might have a restrictive throttle for unauthenticated requests, and a less restrictive throttle for authenticated requests.
@@ -63,6 +61,10 @@ Or, if you're using the `@api_view` decorator with function based views.
}
return Response(content)
+## Setting up the cache
+
+The throttle classes provided by REST framework use Django's cache backend. You should make sure that you've set appropriate [cache settings][cache-setting]. The default value of `LocMemCache` backend should be okay for simple setups. See Django's [cache documentation][cache-docs] for more details.
+
---
# API Reference
@@ -162,4 +164,7 @@ The following is an example of a rate throttle, that will randomly throttle 1 in
def allow_request(self, request, view):
return random.randint(1, 10) == 1
+[cite]: https://dev.twitter.com/docs/error-codes-responses
[permissions]: permissions.md
+[cache-setting]: https://docs.djangoproject.com/en/dev/ref/settings/#caches
+[cache-docs]: https://docs.djangoproject.com/en/dev/topics/cache/#setting-up-the-cache \ No newline at end of file
diff --git a/docs/index.md b/docs/index.md
index 32b42419..0188accf 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -27,7 +27,7 @@ There is also a sandbox API you can use for testing purposes, [available here][s
REST framework requires the following:
-* Python (2.6, 2.7, 3.2, 3.3)
+* Python (2.6.5+, 2.7, 3.2, 3.3)
* Django (1.3, 1.4, 1.5)
The following packages are optional:
diff --git a/docs/topics/2.2-announcement.md b/docs/topics/2.2-announcement.md
index e24fc615..d7164ce4 100644
--- a/docs/topics/2.2-announcement.md
+++ b/docs/topics/2.2-announcement.md
@@ -10,6 +10,8 @@ Django 1.6's Python 3 support is expected to be officially labeled as 'productio
If you want to start ensuring that your own projects are Python 3 ready, we can highly recommend Django's [Porting to Python 3][porting-python-3] documentation.
+Django REST framework's Python 2.6 support now requires 2.6.5 or above, in line with [Django 1.5's Python compatibility][python-compat].
+
## Deprecation policy
We've now introduced an official deprecation policy, which is in line with [Django's deprecation policy][django-deprecation-policy]. This policy will make it easy for you to continue to track the latest, greatest version of REST framework.
@@ -30,9 +32,11 @@ As of the 2.2 merge, we've also hit an impressive milestone. The number of comm
Our [mailing list][mailing-list] and #restframework IRC channel are also very active, and we've got a really impressive rate of development both on REST framework itself, and on third party packages such as the great [django-rest-framework-docs][django-rest-framework-docs] package from [Marc Gibbons][marcgibbons].
+---
+
## API changes
-The 2.2 release makes a few changes to the serializer fields API, in order to make it more consistent, simple, and easier to use.
+The 2.2 release makes a few changes to the API, in order to make it more consistent, simple, and easier to use.
### Cleaner to-many related fields
@@ -136,7 +140,7 @@ If you're overriding the `BasePermission` class, the old-style signature will co
Note also that the usage of the internal APIs for permission checking on the `View` class has been cleaned up slightly, and is now documented and subject to the deprecation policy in all future versions.
-## More explicit hyperlink relations behavior
+### More explicit hyperlink relations behavior
When using a serializer with a `HyperlinkedRelatedField` or `HyperlinkedIdentityField`, the hyperlinks would previously use absolute URLs if the serializer context included a `'request'` key, and fallback to using relative URLs otherwise. This could lead to non-obvious behavior, as it might not be clear why some serializers generated absolute URLs, and others do not.
@@ -145,6 +149,7 @@ From version 2.2 onwards, serializers with hyperlinked relationships *always* re
[xordoquy]: https://github.com/xordoquy
[django-python-3]: https://docs.djangoproject.com/en/dev/faq/install/#can-i-use-django-with-python-3
[porting-python-3]: https://docs.djangoproject.com/en/dev/topics/python3/
+[python-compat]: https://docs.djangoproject.com/en/dev/releases/1.5/#python-compatibility
[django-deprecation-policy]: https://docs.djangoproject.com/en/dev/internals/release-process/#internal-release-deprecation-policy
[credits]: http://django-rest-framework.org/topics/credits.html
[mailing-list]: https://groups.google.com/forum/?fromgroups#!forum/django-rest-framework
diff --git a/docs/topics/credits.md b/docs/topics/credits.md
index 1320d4d4..e546548e 100644
--- a/docs/topics/credits.md
+++ b/docs/topics/credits.md
@@ -4,7 +4,7 @@ The following people have helped make REST framework great.
* Tom Christie - [tomchristie]
* Marko Tibold - [markotibold]
-* Paul Bagwell - [pbgwl]
+* Paul Miller - [paulmillr]
* Sébastien Piquemal - [sebpiq]
* Carmen Wick - [cwick]
* Alex Ehlke - [aehlke]
@@ -103,6 +103,7 @@ The following people have helped make REST framework great.
* Fernando Rocha - [fernandogrd]
* Xavier Ordoquy - [xordoquy]
* Adam Wentz - [floppya]
+* Andreas Pelme - [pelme]
Many thanks to everyone who's contributed to the project.
@@ -141,7 +142,7 @@ You can also contact [@_tomchristie][twitter] directly on twitter.
[tomchristie]: https://github.com/tomchristie
[markotibold]: https://github.com/markotibold
-[pbgwl]: https://github.com/pbgwl
+[paulmillr]: https://github.com/paulmillr
[sebpiq]: https://github.com/sebpiq
[cwick]: https://github.com/cwick
[aehlke]: https://github.com/aehlke
@@ -240,3 +241,4 @@ You can also contact [@_tomchristie][twitter] directly on twitter.
[fernandogrd]: https://github.com/fernandogrd
[xordoquy]: https://github.com/xordoquy
[floppya]: https://github.com/floppya
+[pelme]: https://github.com/pelme
diff --git a/docs/topics/release-notes.md b/docs/topics/release-notes.md
index 3f3f8786..d5444f72 100644
--- a/docs/topics/release-notes.md
+++ b/docs/topics/release-notes.md
@@ -42,6 +42,8 @@ You can determine your currently installed version using `pip freeze`:
### Master
+* Added TimeField.
+* Serializer fields can be mapped to any method that takes no args, or only takes kwargs which have defaults.
* Bugfix: request.DATA should return an empty `QueryDict` with no data, not `None`.
* Bugfix: Remove unneeded field validation, which caused extra queries.
diff --git a/rest_framework/compat.py b/rest_framework/compat.py
index 9636b9c1..3fd865f8 100644
--- a/rest_framework/compat.py
+++ b/rest_framework/compat.py
@@ -349,7 +349,7 @@ except ImportError:
# dateparse is ALSO new in Django 1.4
try:
- from django.utils.dateparse import parse_date, parse_datetime
+ from django.utils.dateparse import parse_date, parse_datetime, parse_time
except ImportError:
import datetime
import re
diff --git a/rest_framework/fields.py b/rest_framework/fields.py
index 327008fb..e1fd1b64 100644
--- a/rest_framework/fields.py
+++ b/rest_framework/fields.py
@@ -18,16 +18,21 @@ from rest_framework.compat import timezone
from rest_framework.compat import BytesIO
from rest_framework.compat import six
from rest_framework.compat import smart_text
+from rest_framework.compat import parse_time
def is_simple_callable(obj):
"""
True if the object is a callable that takes no arguments.
"""
- return (
- (inspect.isfunction(obj) and not inspect.getargspec(obj)[0]) or
- (inspect.ismethod(obj) and len(inspect.getargspec(obj)[0]) <= 1)
- )
+ try:
+ args, _, _, defaults = inspect.getargspec(obj)
+ except TypeError:
+ return False
+ else:
+ len_args = len(args) if inspect.isfunction(obj) else len(args) - 1
+ len_defaults = len(defaults) if defaults else 0
+ return len_args <= len_defaults
def get_component(obj, attr_name):
@@ -531,6 +536,33 @@ class DateTimeField(WritableField):
raise ValidationError(msg)
+class TimeField(WritableField):
+ type_name = 'TimeField'
+ widget = widgets.TimeInput
+ form_field_class = forms.TimeField
+
+ default_error_messages = {
+ 'invalid': _("'%s' value has an invalid format. It must be a valid "
+ "time in the HH:MM[:ss[.uuuuuu]] format."),
+ }
+ empty = None
+
+ def from_native(self, value):
+ if value in validators.EMPTY_VALUES:
+ return None
+
+ if isinstance(value, datetime.time):
+ return value
+
+ try:
+ parsed = parse_time(value)
+ assert parsed is not None
+ return parsed
+ except ValueError:
+ msg = self.error_messages['invalid'] % value
+ raise ValidationError(msg)
+
+
class IntegerField(WritableField):
type_name = 'IntegerField'
form_field_class = forms.IntegerField
diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py
index 5d3475d4..b0372ab8 100644
--- a/rest_framework/serializers.py
+++ b/rest_framework/serializers.py
@@ -517,6 +517,7 @@ class ModelSerializer(Serializer):
models.PositiveSmallIntegerField: IntegerField,
models.DateTimeField: DateTimeField,
models.DateField: DateField,
+ models.TimeField: TimeField,
models.EmailField: EmailField,
models.CharField: CharField,
models.URLField: URLField,
diff --git a/rest_framework/tests/fields.py b/rest_framework/tests/fields.py
index b7587bf1..34f61678 100644
--- a/rest_framework/tests/fields.py
+++ b/rest_framework/tests/fields.py
@@ -2,8 +2,10 @@
General serializer field tests.
"""
from __future__ import unicode_literals
+import datetime
from django.db import models
from django.test import TestCase
+from django.core import validators
from rest_framework import serializers
@@ -26,7 +28,16 @@ class CharPrimaryKeyModelSerializer(serializers.ModelSerializer):
model = CharPrimaryKeyModel
-class ReadOnlyFieldTests(TestCase):
+class TimeFieldModel(models.Model):
+ clock = models.TimeField()
+
+
+class TimeFieldModelSerializer(serializers.ModelSerializer):
+ class Meta:
+ model = TimeFieldModel
+
+
+class BasicFieldTests(TestCase):
def test_auto_now_fields_read_only(self):
"""
auto_now and auto_now_add fields should be read_only by default.
@@ -47,3 +58,38 @@ class ReadOnlyFieldTests(TestCase):
"""
serializer = CharPrimaryKeyModelSerializer()
self.assertEquals(serializer.fields['id'].read_only, False)
+
+ def test_TimeField_from_native(self):
+ f = serializers.TimeField()
+ result = f.from_native('12:34:56.987654')
+
+ self.assertEqual(datetime.time(12, 34, 56, 987654), result)
+
+ def test_TimeField_from_native_datetime_time(self):
+ """
+ Make sure from_native() accepts a datetime.time instance.
+ """
+ f = serializers.TimeField()
+ result = f.from_native(datetime.time(12, 34, 56))
+ self.assertEqual(result, datetime.time(12, 34, 56))
+
+ def test_TimeField_from_native_empty(self):
+ f = serializers.TimeField()
+ result = f.from_native('')
+ self.assertEqual(result, None)
+
+ def test_TimeField_from_native_invalid_time(self):
+ f = serializers.TimeField()
+
+ try:
+ f.from_native('12:69:12')
+ except validators.ValidationError as e:
+ self.assertEqual(e.messages, ["'12:69:12' value has an invalid "
+ "format. It must be a valid time "
+ "in the HH:MM[:ss[.uuuuuu]] format."])
+ else:
+ self.fail("ValidationError was not properly raised")
+
+ def test_TimeFieldModelSerializer(self):
+ serializer = TimeFieldModelSerializer()
+ self.assertTrue(isinstance(serializer.fields['clock'], serializers.TimeField))
diff --git a/rest_framework/views.py b/rest_framework/views.py
index 55ad8cf3..fa742582 100644
--- a/rest_framework/views.py
+++ b/rest_framework/views.py
@@ -13,7 +13,6 @@ from rest_framework.response import Response
from rest_framework.request import Request
from rest_framework.settings import api_settings
import re
-import warnings
def _remove_trailing_string(content, trailing):
@@ -212,13 +211,13 @@ class APIView(View):
def get_parsers(self):
"""
- Instantiates and returns the list of renderers that this view can use.
+ Instantiates and returns the list of parsers that this view can use.
"""
return [parser() for parser in self.parser_classes]
def get_authenticators(self):
"""
- Instantiates and returns the list of renderers that this view can use.
+ Instantiates and returns the list of authenticators that this view can use.
"""
return [auth() for auth in self.authentication_classes]