aboutsummaryrefslogtreecommitdiffstats
path: root/docs
diff options
context:
space:
mode:
authorBen Konrath2012-11-06 03:22:25 +0100
committerBen Konrath2012-11-06 03:22:25 +0100
commit09f39bd23b3c688c89551845d665395e1aabbfab (patch)
tree67de86ddb90c4e91e66ee276252e9086064231da /docs
parent01564fb1e5727134d2ceb4b3ab79e013af1b4807 (diff)
parent455a8cedcf5aa1f265ae95d4f3bff359d51910c0 (diff)
downloaddjango-rest-framework-09f39bd23b3c688c89551845d665395e1aabbfab.tar.bz2
Merge branch 'master' into restframework2-filter
Diffstat (limited to 'docs')
-rw-r--r--docs/api-guide/fields.md44
-rw-r--r--docs/api-guide/serializers.md29
-rw-r--r--docs/index.md2
-rw-r--r--docs/template.html2
-rw-r--r--docs/topics/credits.md10
-rw-r--r--docs/topics/release-notes.md33
-rw-r--r--docs/tutorial/1-serialization.md12
-rw-r--r--docs/tutorial/2-requests-and-responses.md8
-rw-r--r--docs/tutorial/3-class-based-views.md12
-rw-r--r--docs/tutorial/5-relationships-and-hyperlinked-apis.md2
-rw-r--r--docs/tutorial/quickstart.md9
11 files changed, 112 insertions, 51 deletions
diff --git a/docs/api-guide/fields.md b/docs/api-guide/fields.md
index 8c3df067..411f7944 100644
--- a/docs/api-guide/fields.md
+++ b/docs/api-guide/fields.md
@@ -235,44 +235,50 @@ 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'`.
+* `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.
+
[cite]: http://www.python.org/dev/peps/pep-0020/
diff --git a/docs/api-guide/serializers.md b/docs/api-guide/serializers.md
index c88b9b0c..ee7f72dd 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
# <Comment object at 0x10633b2d0>
>>> 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_<fieldname>()` methods to your `Serializer` subclass. These are analagous to `clean_<fieldname>` 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_<fieldname>` methods should either just return the attrs dictionary or raise a `ValidationError`. For example:
+You can specify custom field-level validation by adding `.validate_<fieldname>` methods to your `Serializer` subclass. These are analagous to `.clean_<fieldname>` 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_<fieldname>` 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_<fieldname>()`
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`.
+
+## 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.
-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`.
+The generic views provided by REST framework call the `.save()` method when updating or creating entities.
## Dealing with nested objects
diff --git a/docs/index.md b/docs/index.md
index 75a1cf6e..5e086872 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.
-<!--
## Quickstart
Can't wait to get started? The [quickstart guide][quickstart] is the fastest way to get up and running with REST framework.
--->
## Tutorial
diff --git a/docs/template.html b/docs/template.html
index 94fc269f..c428dff3 100644
--- a/docs/template.html
+++ b/docs/template.html
@@ -53,7 +53,7 @@
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">Tutorial <b class="caret"></b></a>
<ul class="dropdown-menu">
- <!--<li><a href="{{ base_url }}/tutorial/quickstart{{ suffix }}">Quickstart</a></li>-->
+ <li><a href="{{ base_url }}/tutorial/quickstart{{ suffix }}">Quickstart</a></li>
<li><a href="{{ base_url }}/tutorial/1-serialization{{ suffix }}">1 - Serialization</a></li>
<li><a href="{{ base_url }}/tutorial/2-requests-and-responses{{ suffix }}">2 - Requests and responses</a></li>
<li><a href="{{ base_url }}/tutorial/3-class-based-views{{ suffix }}">3 - Class based views</a></li>
diff --git a/docs/topics/credits.md b/docs/topics/credits.md
index a74f7983..3fbcabb9 100644
--- a/docs/topics/credits.md
+++ b/docs/topics/credits.md
@@ -52,6 +52,10 @@ The following people have helped make REST framework great.
* Madis Väin - [madisvain]
* Stephan Groß - [minddust]
* Pavel Savchenko - [asfaltboy]
+* Otto Yiu - [ottoyiu]
+* Jacob Magnusson - [jmagnusson]
+* Osiloke Harold Emoekpere - [osiloke]
+* Michael Shepanski - [mjs7231]
Many thanks to everyone who's contributed to the project.
@@ -80,7 +84,7 @@ To contact the author directly:
[twitter]: http://twitter.com/_tomchristie
[bootstrap]: http://twitter.github.com/bootstrap/
[markdown]: http://daringfireball.net/projects/markdown/
-[github]: github.com/tomchristie/django-rest-framework
+[github]: https://github.com/tomchristie/django-rest-framework
[travis-ci]: https://secure.travis-ci.org/tomchristie/django-rest-framework
[piston]: https://bitbucket.org/jespern/django-piston
[tastypie]: https://github.com/toastdriven/django-tastypie
@@ -139,3 +143,7 @@ To contact the author directly:
[madisvain]: https://github.com/madisvain
[minddust]: https://github.com/minddust
[asfaltboy]: https://github.com/asfaltboy
+[ottoyiu]: https://github.com/OttoYiu
+[jmagnusson]: https://github.com/jmagnusson
+[osiloke]: https://github.com/osiloke
+[mjs7231]: https://github.com/mjs7231 \ No newline at end of file
diff --git a/docs/topics/release-notes.md b/docs/topics/release-notes.md
index b336aeab..b5c81c2b 100644
--- a/docs/topics/release-notes.md
+++ b/docs/topics/release-notes.md
@@ -4,14 +4,40 @@
>
> &mdash; Eric S. Raymond, [The Cathedral and the Bazaar][cite].
-## Master
+## 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.)
+* Bugfix: Support choice field in Browseable API.
+* Bugfix: Related fields with `read_only=True` do not require a `queryset` argument.
+
+## 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
+
* **Fix all of the things.** (Well, almost.)
-* For more information please see the [2.0 migration guide][migration].
+* For more information please see the [2.0 announcement][announcement].
---
@@ -117,4 +143,5 @@
* Initial release.
[cite]: http://www.catb.org/~esr/writings/cathedral-bazaar/cathedral-bazaar/ar01s04.html
-[migration]: migration.md \ No newline at end of file
+[2.1.0-notes]: https://groups.google.com/d/topic/django-rest-framework/Vv2M0CMY9bg/discussion
+[announcement]: rest-framework-2-announcement.md \ No newline at end of file
diff --git a/docs/tutorial/1-serialization.md b/docs/tutorial/1-serialization.md
index 316a3c25..ba64f2aa 100644
--- a/docs/tutorial/1-serialization.md
+++ b/docs/tutorial/1-serialization.md
@@ -162,7 +162,7 @@ Okay, once we've got a few imports out of the way, let's create a code snippet t
We've now got a few snippet instances to play with. Let's take a look at serializing one of those instances.
- serializer = SnippetSerializer(instance=snippet)
+ serializer = SnippetSerializer(snippet)
serializer.data
# {'pk': 1, 'title': u'', 'code': u'print "hello, world"\n', 'linenos': False, 'language': u'python', 'style': u'friendly'}
@@ -181,7 +181,7 @@ Deserialization is similar. First we parse a stream into python native datatype
...then we restore those native datatypes into to a fully populated object instance.
- serializer = SnippetSerializer(data)
+ serializer = SnippetSerializer(data=data)
serializer.is_valid()
# True
serializer.object
@@ -240,12 +240,12 @@ The root of our API is going to be a view that supports listing all the existing
"""
if request.method == 'GET':
snippets = Snippet.objects.all()
- serializer = SnippetSerializer(instance=snippets)
+ serializer = SnippetSerializer(snippets)
return JSONResponse(serializer.data)
elif request.method == 'POST':
data = JSONParser().parse(request)
- serializer = SnippetSerializer(data)
+ serializer = SnippetSerializer(data=data)
if serializer.is_valid():
serializer.save()
return JSONResponse(serializer.data, status=201)
@@ -267,12 +267,12 @@ We'll also need a view which corresponds to an individual snippet, and can be us
return HttpResponse(status=404)
if request.method == 'GET':
- serializer = SnippetSerializer(instance=snippet)
+ serializer = SnippetSerializer(snippet)
return JSONResponse(serializer.data)
elif request.method == 'PUT':
data = JSONParser().parse(request)
- serializer = SnippetSerializer(data, instance=snippet)
+ serializer = SnippetSerializer(snippet, data=data)
if serializer.is_valid():
serializer.save()
return JSONResponse(serializer.data)
diff --git a/docs/tutorial/2-requests-and-responses.md b/docs/tutorial/2-requests-and-responses.md
index a7c23cba..b29daf05 100644
--- a/docs/tutorial/2-requests-and-responses.md
+++ b/docs/tutorial/2-requests-and-responses.md
@@ -52,11 +52,11 @@ We don't need our `JSONResponse` class anymore, so go ahead and delete that. On
"""
if request.method == 'GET':
snippets = Snippet.objects.all()
- serializer = SnippetSerializer(instance=snippets)
+ serializer = SnippetSerializer(snippets)
return Response(serializer.data)
elif request.method == 'POST':
- serializer = SnippetSerializer(request.DATA)
+ serializer = SnippetSerializer(data=request.DATA)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
@@ -77,11 +77,11 @@ Our instance view is an improvement over the previous example. It's a little mo
return Response(status=status.HTTP_404_NOT_FOUND)
if request.method == 'GET':
- serializer = SnippetSerializer(instance=snippet)
+ serializer = SnippetSerializer(snippet)
return Response(serializer.data)
elif request.method == 'PUT':
- serializer = SnippetSerializer(request.DATA, instance=snippet)
+ serializer = SnippetSerializer(snippet, data=request.DATA)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
diff --git a/docs/tutorial/3-class-based-views.md b/docs/tutorial/3-class-based-views.md
index a31dccb2..eddf6311 100644
--- a/docs/tutorial/3-class-based-views.md
+++ b/docs/tutorial/3-class-based-views.md
@@ -20,11 +20,11 @@ We'll start by rewriting the root view as a class based view. All this involves
"""
def get(self, request, format=None):
snippets = Snippet.objects.all()
- serializer = SnippetSerializer(instance=snippets)
+ serializer = SnippetSerializer(snippets)
return Response(serializer.data)
def post(self, request, format=None):
- serializer = SnippetSerializer(request.DATA)
+ serializer = SnippetSerializer(data=request.DATA)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
@@ -44,12 +44,12 @@ So far, so good. It looks pretty similar to the previous case, but we've got be
def get(self, request, pk, format=None):
snippet = self.get_object(pk)
- serializer = SnippetSerializer(instance=snippet)
+ serializer = SnippetSerializer(snippet)
return Response(serializer.data)
def put(self, request, pk, format=None):
snippet = self.get_object(pk)
- serializer = SnippetSerializer(request.DATA, instance=snippet)
+ serializer = SnippetSerializer(snippet, data=request.DATA)
if serializer.is_valid():
serializer.save()
return Response(serializer.data)
@@ -92,7 +92,7 @@ Let's take a look at how we can compose our views by using the mixin classes.
class SnippetList(mixins.ListModelMixin,
mixins.CreateModelMixin,
- generics.MultipleObjectBaseView):
+ generics.MultipleObjectAPIView):
model = Snippet
serializer_class = SnippetSerializer
@@ -102,7 +102,7 @@ Let's take a look at how we can compose our views by using the mixin classes.
def post(self, request, *args, **kwargs):
return self.create(request, *args, **kwargs)
-We'll take a moment to examine exactly what's happening here - We're building our view using `MultipleObjectBaseView`, and adding in `ListModelMixin` and `CreateModelMixin`.
+We'll take a moment to examine exactly what's happening here - We're building our view using `MultipleObjectAPIView`, and adding in `ListModelMixin` and `CreateModelMixin`.
The base class provides the core functionality, and the mixin classes provide the `.list()` and `.create()` actions. We're then explicitly binding the `get` and `post` methods to the appropriate actions. Simple enough stuff so far.
diff --git a/docs/tutorial/5-relationships-and-hyperlinked-apis.md b/docs/tutorial/5-relationships-and-hyperlinked-apis.md
index 3113249b..98c45b82 100644
--- a/docs/tutorial/5-relationships-and-hyperlinked-apis.md
+++ b/docs/tutorial/5-relationships-and-hyperlinked-apis.md
@@ -167,7 +167,7 @@ We've reached the end of our tutorial. If you want to get more involved in the
* Join the [REST framework discussion group][group], and help build the community.
* Follow the author [on Twitter][twitter] and say hi.
-**Now go build some awesome things.**
+**Now go build awesome things.**
[repo]: https://github.com/tomchristie/rest-framework-tutorial
[sandbox]: http://restframework.herokuapp.com/
diff --git a/docs/tutorial/quickstart.md b/docs/tutorial/quickstart.md
index 6bde725b..93da1a59 100644
--- a/docs/tutorial/quickstart.md
+++ b/docs/tutorial/quickstart.md
@@ -19,12 +19,19 @@ First up we're going to define some serializers in `quickstart/serializers.py` t
class GroupSerializer(serializers.HyperlinkedModelSerializer):
+ permissions = serializers.ManySlugRelatedField(
+ slug_field='codename',
+ queryset=Permission.objects.all()
+ )
+
class Meta:
model = Group
fields = ('url', 'name', 'permissions')
Notice that we're using hyperlinked relations in this case, with `HyperlinkedModelSerializer`. You can also use primary key and various other relationships, but hyperlinking is good RESTful design.
+We've also overridden the `permission` field on the `GroupSerializer`. In this case we don't want to use a hyperlinked representation, but instead use the list of permission codenames associated with the group, so we've used a `ManySlugRelatedField`, using the `codename` field for the representation.
+
## Views
Right, we'd better write some views then. Open `quickstart/views.py` and get typing.
@@ -152,7 +159,7 @@ We can now access our API, both from the command-line, using tools like `curl`..
},
{
"email": "tom@example.com",
- "groups": [],
+ "groups": [ ],
"url": "http://127.0.0.1:8000/users/2/",
"username": "tom"
}