diff --git a/.gitignore b/.gitignore index 96186b465..25113d09d 100644 --- a/.gitignore +++ b/.gitignore @@ -7,7 +7,7 @@ html/ coverage/ build/ dist/ -rest_framework.egg-info/ +*.egg-info/ MANIFEST !.gitignore diff --git a/README.md b/README.md index 4b2245cca..7275761c5 100644 --- a/README.md +++ b/README.md @@ -57,8 +57,37 @@ To run the tests. # Changelog +## 2.1.0 + +**Date**: 5th Nov 2012 + +**Warning**: Please read [this thread][2.1.0-notes] regarding the `instance` and `data` keyword args before updating to 2.1.0. + +* **Serializer `instance` and `data` keyword args have their position swapped.** +* `queryset` argument is now optional on writable model fields. +* Hyperlinked related fields optionally take `slug_field` and `slug_field_kwarg` arguments. +* Support Django's cache framework. +* Minor field improvements. (Don't stringify dicts, more robust many-pk fields.) +* Bugfixes (Support choice field in Browseable API) + +## 2.0.2 + +**Date**: 2nd Nov 2012 + +* Fix issues with pk related fields in the browsable API. + +## 2.0.1 + +**Date**: 1st Nov 2012 + +* Add support for relational fields in the browsable API. +* Added SlugRelatedField and ManySlugRelatedField. +* If PUT creates an instance return '201 Created', instead of '200 OK'. + ## 2.0.0 +**Date**: 30th Oct 2012 + * Redesign of core components. * Fix **all of the things**. @@ -93,6 +122,7 @@ OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. [0.4]: https://github.com/tomchristie/django-rest-framework/tree/0.4.X [sandbox]: http://restframework.herokuapp.com/ [rest-framework-2-announcement]: topics/rest-framework-2-announcement.md +[2.1.0-notes]: https://groups.google.com/d/topic/django-rest-framework/Vv2M0CMY9bg/discussion [docs]: http://django-rest-framework.org/ [urlobject]: https://github.com/zacharyvoase/urlobject diff --git a/docs/api-guide/authentication.md b/docs/api-guide/authentication.md index 889d16c03..3137b9d4c 100644 --- a/docs/api-guide/authentication.md +++ b/docs/api-guide/authentication.md @@ -30,7 +30,7 @@ The default authentication policy may be set globally, using the `DEFAULT_AUTHEN REST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES': ( - 'rest_framework.authentication.UserBasicAuthentication', + 'rest_framework.authentication.BasicAuthentication', 'rest_framework.authentication.SessionAuthentication', ) } @@ -38,7 +38,7 @@ The default authentication policy may be set globally, using the `DEFAULT_AUTHEN You can also set the authentication policy on a per-view basis, using the `APIView` class based views. class ExampleView(APIView): - authentication_classes = (SessionAuthentication, UserBasicAuthentication) + authentication_classes = (SessionAuthentication, BasicAuthentication) permission_classes = (IsAuthenticated,) def get(self, request, format=None): @@ -51,7 +51,7 @@ You can also set the authentication policy on a per-view basis, using the `APIVi Or, if you're using the `@api_view` decorator with function based views. @api_view(['GET']) - @authentication_classes((SessionAuthentication, UserBasicAuthentication)) + @authentication_classes((SessionAuthentication, BasicAuthentication)) @permissions_classes((IsAuthenticated,)) def example_view(request, format=None): content = { diff --git a/docs/api-guide/fields.md b/docs/api-guide/fields.md index 8c3df0678..0485b158f 100644 --- a/docs/api-guide/fields.md +++ b/docs/api-guide/fields.md @@ -235,44 +235,54 @@ Then an example output format for a Bookmark instance would be: 'url': u'https://www.djangoproject.com/' } -## PrimaryKeyRelatedField +## PrimaryKeyRelatedField / ManyPrimaryKeyRelatedField -This field can be applied to any "to-one" relationship, such as a `ForeignKey` field. +`PrimaryKeyRelatedField` and `ManyPrimaryKeyRelatedField` will represent the target of the relationship using it's primary key. -`PrimaryKeyRelatedField` will represent the target of the field using it's primary key. +By default these fields are read-write, although you can change this behaviour using the `read_only` flag. -Be default, `PrimaryKeyRelatedField` is read-write, although you can change this behaviour using the `read_only` flag. +**Arguments**: -## ManyPrimaryKeyRelatedField +* `queryset` - By default `ModelSerializer` classes will use the default queryset for the relationship. `Serializer` classes must either set a queryset explicitly, or set `read_only=True`. -This field can be applied to any "to-many" relationship, such as a `ManyToManyField` field, or a reverse `ForeignKey` relationship. +## SlugRelatedField / ManySlugRelatedField -`PrimaryKeyRelatedField` will represent the targets of the field using their primary key. +`SlugRelatedField` and `ManySlugRelatedField` will represent the target of the relationship using a unique slug. -Be default, `ManyPrimaryKeyRelatedField` is read-write, although you can change this behaviour using the `read_only` flag. +By default these fields read-write, although you can change this behaviour using the `read_only` flag. -## HyperlinkedRelatedField +**Arguments**: -This field can be applied to any "to-one" relationship, such as a `ForeignKey` field. +* `slug_field` - The field on the target that should be used to represent it. This should be a field that uniquely identifies any given instance. For example, `username`. +* `queryset` - By default `ModelSerializer` classes will use the default queryset for the relationship. `Serializer` classes must either set a queryset explicitly, or set `read_only=True`. -`HyperlinkedRelatedField` will represent the target of the field using a hyperlink. You must include a named URL pattern in your URL conf, with a name like `'{model-name}-detail'` that corresponds to the target of the hyperlink. +## HyperlinkedRelatedField / ManyHyperlinkedRelatedField -Be default, `HyperlinkedRelatedField` is read-write, although you can change this behaviour using the `read_only` flag. +`HyperlinkedRelatedField` and `ManyHyperlinkedRelatedField` will represent the target of the relationship using a hyperlink. -## ManyHyperlinkedRelatedField +By default, `HyperlinkedRelatedField` is read-write, although you can change this behaviour using the `read_only` flag. -This field can be applied to any "to-many" relationship, such as a `ManyToManyField` field, or a reverse `ForeignKey` relationship. +**Arguments**: -`ManyHyperlinkedRelatedField` will represent the targets of the field using hyperlinks. You must include a named URL pattern in your URL conf, with a name like `'{model-name}-detail'` that corresponds to the target of the hyperlink. - -Be default, `ManyHyperlinkedRelatedField` is read-write, although you can change this behaviour using the `read_only` flag. +* `view_name` - The view name that should be used as the target of the relationship. **required**. +* `format` - If using format suffixes, hyperlinked fields will use the same format suffix for the target unless overridden by using the `format` argument. +* `queryset` - By default `ModelSerializer` classes will use the default queryset for the relationship. `Serializer` classes must either set a queryset explicitly, or set `read_only=True`. +* `slug_field` - The field on the target that should be used for the lookup. Default is `'slug'`. +* `pk_url_kwarg` - The named url parameter for the pk field lookup. Default is `pk`. +* `slug_url_kwarg` - The named url parameter for the slug field lookup. Default is to use the same value as given for `slug_field`. ## HyperLinkedIdentityField This field can be applied as an identity relationship, such as the `'url'` field on a HyperlinkedModelSerializer. -You must include a named URL pattern in your URL conf, with a name like `'{model-name}-detail'` that corresponds to the model. - This field is always read-only. +**Arguments**: + +* `view_name` - The view name that should be used as the target of the relationship. **required**. +* `format` - If using format suffixes, hyperlinked fields will use the same format suffix for the target unless overridden by using the `format` argument. +* `slug_field` - The field on the target that should be used for the lookup. Default is `'slug'`. +* `pk_url_kwarg` - The named url parameter for the pk field lookup. Default is `pk`. +* `slug_url_kwarg` - The named url parameter for the slug field lookup. Default is to use the same value as given for `slug_field`. + [cite]: http://www.python.org/dev/peps/pep-0020/ diff --git a/docs/api-guide/renderers.md b/docs/api-guide/renderers.md index c3d12ddbb..374ff0ab2 100644 --- a/docs/api-guide/renderers.md +++ b/docs/api-guide/renderers.md @@ -257,6 +257,21 @@ In [the words of Roy Fielding][quote], "A REST API should spend almost all of it For good examples of custom media types, see GitHub's use of a custom [application/vnd.github+json] media type, and Mike Amundsen's IANA approved [application/vnd.collection+json] JSON-based hypermedia. +## HTML error views + +Typically a renderer will behave the same regardless of if it's dealing with a regular response, or with a response caused by an exception being raised, such as an `Http404` or `PermissionDenied` exception, or a subclass of `APIException`. + +If you're using either the `TemplateHTMLRenderer` or the `StaticHTMLRenderer` and an exception is raised, the behavior is slightly different, and mirrors [Django's default handling of error views][django-error-views]. + +Exceptions raised and handled by an HTML renderer will attempt to render using one of the following methods, by order of precedence. + +* Load and render a template named `{status_code}.html`. +* Load and render a template named `api_exception.html`. +* Render the HTTP status code and text, for example "404 Not Found". + +Templates will render with a `RequestContext` which includes the `status_code` and `details` keys. + + [cite]: https://docs.djangoproject.com/en/dev/ref/template-response/#the-rendering-process [conneg]: content-negotiation.md [browser-accept-headers]: http://www.gethifi.com/blog/browser-rest-http-accept-headers @@ -265,3 +280,4 @@ For good examples of custom media types, see GitHub's use of a custom [applicati [quote]: http://roy.gbiv.com/untangled/2008/rest-apis-must-be-hypertext-driven [application/vnd.github+json]: http://developer.github.com/v3/media/ [application/vnd.collection+json]: http://www.amundsen.com/media-types/collection/ +[django-error-views]: https://docs.djangoproject.com/en/dev/topics/http/views/#customizing-error-views \ No newline at end of file diff --git a/docs/api-guide/serializers.md b/docs/api-guide/serializers.md index c88b9b0c6..ee7f72dd7 100644 --- a/docs/api-guide/serializers.md +++ b/docs/api-guide/serializers.md @@ -47,7 +47,7 @@ The first part of serializer class defines the fields that get serialized/deseri We can now use `CommentSerializer` to serialize a comment, or list of comments. Again, using the `Serializer` class looks a lot like using a `Form` class. - serializer = CommentSerializer(instance=comment) + serializer = CommentSerializer(comment) serializer.data # {'email': u'leila@example.com', 'content': u'foo bar', 'created': datetime.datetime(2012, 8, 22, 16, 20, 9, 822774)} @@ -65,20 +65,29 @@ Deserialization is similar. First we parse a stream into python native datatype ...then we restore those native datatypes into a fully populated object instance. - serializer = CommentSerializer(data) + serializer = CommentSerializer(data=data) serializer.is_valid() # True serializer.object # >>> serializer.deserialize('json', stream) +When deserializing data, we can either create a new instance, or update an existing instance. + + serializer = CommentSerializer(data=data) # Create new instance + serializer = CommentSerializer(comment, data=data) # Update `instance` + ## Validation When deserializing data, you always need to call `is_valid()` before attempting to access the deserialized object. If any validation errors occur, the `.errors` and `.non_field_errors` properties will contain the resulting error messages. ### Field-level validation -You can specify custom field-level validation by adding `validate_()` methods to your `Serializer` subclass. These are analagous to `clean_` methods on Django forms, but accept slightly different arguments. They take a dictionary of deserialized attributes as a first argument, and the field name in that dictionary as a second argument (which will be either the name of the field or the value of the `source` argument to the field, if one was provided). Your `validate_` methods should either just return the attrs dictionary or raise a `ValidationError`. For example: +You can specify custom field-level validation by adding `.validate_` methods to your `Serializer` subclass. These are analagous to `.clean_` methods on Django forms, but accept slightly different arguments. + +They take a dictionary of deserialized attributes as a first argument, and the field name in that dictionary as a second argument (which will be either the name of the field or the value of the `source` argument to the field, if one was provided). + +Your `validate_` methods should either just return the `attrs` dictionary or raise a `ValidationError`. For example: from rest_framework import serializers @@ -88,16 +97,22 @@ You can specify custom field-level validation by adding `validate_()` def validate_title(self, attrs, source): """ - Check that the blog post is about Django + Check that the blog post is about Django. """ value = attrs[source] - if "Django" not in value: + if "django" not in value.lower(): raise serializers.ValidationError("Blog post is not about Django") return attrs -### Final cross-field validation +### Object-level validation -To do any other validation that requires access to multiple fields, add a method called `validate` to your `Serializer` subclass. This method takes a single argument, which is the `attrs` dictionary. It should raise a `ValidationError` if necessary, or just return `attrs`. +To do any other validation that requires access to multiple fields, add a method called `.validate()` to your `Serializer` subclass. This method takes a single argument, which is the `attrs` dictionary. It should raise a `ValidationError` if necessary, or just return `attrs`. + +## Saving object state + +Serializers also include a `.save()` method that you can override if you want to provide a method of persisting the state of a deserialized object. The default behavior of the method is to simply call `.save()` on the deserialized object instance. + +The generic views provided by REST framework call the `.save()` method when updating or creating entities. ## Dealing with nested objects diff --git a/docs/api-guide/throttling.md b/docs/api-guide/throttling.md index bfda7079b..b03bc9e04 100644 --- a/docs/api-guide/throttling.md +++ b/docs/api-guide/throttling.md @@ -31,8 +31,8 @@ The default throttling policy may be set globally, using the `DEFAULT_THROTTLE_C REST_FRAMEWORK = { 'DEFAULT_THROTTLE_CLASSES': ( - 'rest_framework.throttles.AnonThrottle', - 'rest_framework.throttles.UserThrottle' + 'rest_framework.throttling.AnonRateThrottle', + 'rest_framework.throttling.UserRateThrottle' ), 'DEFAULT_THROTTLE_RATES': { 'anon': '100/day', @@ -136,7 +136,7 @@ For example, given the following views... REST_FRAMEWORK = { 'DEFAULT_THROTTLE_CLASSES': ( - 'rest_framework.throttles.ScopedRateThrottle' + 'rest_framework.throttling.ScopedRateThrottle' ), 'DEFAULT_THROTTLE_RATES': { 'contacts': '1000/day', diff --git a/docs/index.md b/docs/index.md index 75a1cf6e6..5e0868724 100644 --- a/docs/index.md +++ b/docs/index.md @@ -66,11 +66,9 @@ If you're intending to use the browseable API you'll want to add REST framework' Note that the URL path can be whatever you want, but you must include `rest_framework.urls` with the `rest_framework` namespace. - ## Tutorial diff --git a/docs/template.html b/docs/template.html index 94fc269f4..c428dff3a 100644 --- a/docs/template.html +++ b/docs/template.html @@ -53,7 +53,7 @@