mirror of
https://github.com/encode/django-rest-framework.git
synced 2025-01-30 11:14:25 +03:00
Update documentation
This commit is contained in:
parent
442f49eb5b
commit
6cce0681a9
|
@ -851,7 +851,7 @@ class UserSerializer(serializers.ModelSerializer):
|
|||
<h1 id="custom-fields">Custom fields</h1>
|
||||
<p>If you want to create a custom field, you'll need to subclass <code>Field</code> and then override either one or both of the <code>.to_representation()</code> and <code>.to_internal_value()</code> methods. These two methods are used to convert between the initial datatype, and a primitive, serializable datatype. Primitive datatypes will typically be any of a number, string, boolean, <code>date</code>/<code>time</code>/<code>datetime</code> or <code>None</code>. They may also be any list or dictionary like object that only contains other primitive objects. Other types might be supported, depending on the renderer that you are using.</p>
|
||||
<p>The <code>.to_representation()</code> method is called to convert the initial datatype into a primitive, serializable datatype.</p>
|
||||
<p>The <code>to_internal_value()</code> method is called to restore a primitive datatype into its internal python representation.</p>
|
||||
<p>The <code>to_internal_value()</code> method is called to restore a primitive datatype into its internal python representation. This method should raise a <code>serializer.ValidationError</code> if the data is invalid.</p>
|
||||
<p>Note that the <code>WritableField</code> class that was present in version 2.x no longer exists. You should subclass <code>Field</code> and override <code>to_internal_value()</code> if the field supports data input.</p>
|
||||
<h2 id="examples">Examples</h2>
|
||||
<p>Let's look at an example of serializing a class that represents an RGB color value:</p>
|
||||
|
@ -890,6 +890,49 @@ class ColorField(serializers.Field):
|
|||
"""
|
||||
return obj.__class__.__name__
|
||||
</code></pre>
|
||||
<h4 id="raising-validation-errors">Raising validation errors</h4>
|
||||
<p>Our <code>ColorField</code> class above currently does not perform any data validation.
|
||||
To indicate invalid data, we should raise a <code>serializers.ValidationError</code>, like so:</p>
|
||||
<pre><code>def to_internal_value(self, data):
|
||||
if not isinstance(data, six.text_type):
|
||||
msg = 'Incorrect type. Expected a string, but got %s'
|
||||
raise ValidationError(msg % type(data).__name__)
|
||||
|
||||
if not re.match(r'^rgb\([0-9]+,[0-9]+,[0-9]+\)$', data):
|
||||
raise ValidationError('Incorrect format. Expected `rgb(#,#,#)`.')
|
||||
|
||||
data = data.strip('rgb(').rstrip(')')
|
||||
red, green, blue = [int(col) for col in data.split(',')]
|
||||
|
||||
if any([col > 255 or col < 0 for col in (red, green, blue)]):
|
||||
raise ValidationError('Value out of range. Must be between 0 and 255.')
|
||||
|
||||
return Color(red, green, blue)
|
||||
</code></pre>
|
||||
<p>The <code>.fail()</code> method is a shortcut for raising <code>ValidationError</code> that takes a message string from the <code>error_messages</code> dictionary. For example:</p>
|
||||
<pre><code>default_error_messages = {
|
||||
'incorrect_type': 'Incorrect type. Expected a string, but got {input_type}',
|
||||
'incorrect_format': 'Incorrect format. Expected `rgb(#,#,#)`.',
|
||||
'out_of_range': 'Value out of range. Must be between 0 and 255.'
|
||||
}
|
||||
|
||||
def to_internal_value(self, data):
|
||||
if not isinstance(data, six.text_type):
|
||||
msg = 'Incorrect type. Expected a string, but got %s'
|
||||
self.fail('incorrect_type', input_type=type(data).__name__)
|
||||
|
||||
if not re.match(r'^rgb\([0-9]+,[0-9]+,[0-9]+\)$', data):
|
||||
self.fail('incorrect_format')
|
||||
|
||||
data = data.strip('rgb(').rstrip(')')
|
||||
red, green, blue = [int(col) for col in data.split(',')]
|
||||
|
||||
if any([col > 255 or col < 0 for col in (red, green, blue)]):
|
||||
self.fail('out_of_range')
|
||||
|
||||
return Color(red, green, blue)
|
||||
</code></pre>
|
||||
<p>This style keeps you error messages more cleanly separated from your code, and should be preferred.</p>
|
||||
<h1 id="third-party-packages">Third party packages</h1>
|
||||
<p>The following third party packages are also available.</p>
|
||||
<h2 id="drf-compound-fields">DRF Compound Fields</h2>
|
||||
|
|
|
@ -461,6 +461,10 @@
|
|||
<a href="#customizing-multiple-update">Customizing multiple update</a>
|
||||
</li>
|
||||
|
||||
<li>
|
||||
<a href="#customizing-listserializer-initialization">Customizing ListSerializer initialization</a>
|
||||
</li>
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -617,7 +621,7 @@ serializer.validated_data
|
|||
</code></pre>
|
||||
<p>If your object instances correspond to Django models you'll also want to ensure that these methods save the object to the database. For example, if <code>Comment</code> was a Django model, the methods might look like this:</p>
|
||||
<pre><code> def create(self, validated_data):
|
||||
return Comment.objcts.create(**validated_data)
|
||||
return Comment.objects.create(**validated_data)
|
||||
|
||||
def update(self, instance, validated_data):
|
||||
instance.email = validated_data.get('email', instance.email)
|
||||
|
@ -982,12 +986,12 @@ AccountSerializer():
|
|||
<h2 id="how-hyperlinked-views-are-determined">How hyperlinked views are determined</h2>
|
||||
<p>There needs to be a way of determining which views should be used for hyperlinking to model instances.</p>
|
||||
<p>By default hyperlinks are expected to correspond to a view name that matches the style <code>'{model_name}-detail'</code>, and looks up the instance by a <code>pk</code> keyword argument.</p>
|
||||
<p>You can override a URL field view name and lookup field by using either, or both of, the <code>view_name</code> and <code>lookup_field</code> options in the <code>extra_field_kwargs</code> setting, like so:</p>
|
||||
<p>You can override a URL field view name and lookup field by using either, or both of, the <code>view_name</code> and <code>lookup_field</code> options in the <code>extra_kwargs</code> setting, like so:</p>
|
||||
<pre><code>class AccountSerializer(serializers.HyperlinkedModelSerializer):
|
||||
class Meta:
|
||||
model = Account
|
||||
fields = ('account_url', 'account_name', 'users', 'created')
|
||||
extra_field_kwargs = {
|
||||
extra_kwargs = {
|
||||
'url': {'view_name': 'accounts', 'lookup_field': 'account_name'}
|
||||
'users': {'lookup_field': 'username'}
|
||||
}
|
||||
|
@ -1084,6 +1088,17 @@ class BookSerializer(serializers.Serializer):
|
|||
list_serializer_class = BookListSerializer
|
||||
</code></pre>
|
||||
<p>It is possible that a third party package may be included alongside the 3.1 release that provides some automatic support for multiple update operations, similar to the <code>allow_add_remove</code> behavior that was present in REST framework 2.</p>
|
||||
<h4 id="customizing-listserializer-initialization">Customizing ListSerializer initialization</h4>
|
||||
<p>When a serializer with <code>many=True</code> is instantiated, we need to determine which arguments and keyword arguments should be passed to the <code>.__init__()</code> method for both the child <code>Serializer</code> class, and for the parent <code>ListSerializer</code> class.</p>
|
||||
<p>The default implementation is to pass all arguments to both classes, except for <code>validators</code>, and any custom keyword arguments, both of which are assumed to be intended for the child serializer class.</p>
|
||||
<p>Occasionally you might need to explicitly specify how the child and parent classes should be instantiated when <code>many=True</code> is passed. You can do so by using the <code>many_init</code> class method.</p>
|
||||
<pre><code> @classmethod
|
||||
def many_init(cls, *args, **kwargs):
|
||||
# Instantiate the child serializer.
|
||||
kwargs['child'] = cls()
|
||||
# Instantiate the parent list serializer.
|
||||
return CustomListSerializer(*args, **kwargs)
|
||||
</code></pre>
|
||||
<hr />
|
||||
<h1 id="baseserializer">BaseSerializer</h1>
|
||||
<p><code>BaseSerializer</code> class that can be used to easily support alternative serialization and deserialization styles.</p>
|
||||
|
|
|
@ -457,7 +457,7 @@
|
|||
<a href="https://twitter.com/share" class="twitter-share-button" data-url="django-rest-framework.org" data-text="Checking out the totally awesome Django REST framework! http://www.django-rest-framework.org" data-count="none"></a>
|
||||
<script>!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0];if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src="http://platform.twitter.com/widgets.js";fjs.parentNode.insertBefore(js,fjs);}}(document,"script","twitter-wjs");</script>
|
||||
|
||||
<img src="https://secure.travis-ci.org/tomchristie/django-rest-framework.png?branch=master" class="travis-build-image">
|
||||
<img src="https://secure.travis-ci.org/tomchristie/django-rest-framework.svg?branch=master" class="travis-build-image">
|
||||
</p>
|
||||
|
||||
<hr />
|
||||
|
@ -480,7 +480,7 @@
|
|||
<p>Django REST framework is a powerful and flexible toolkit that makes it easy to build Web APIs.</p>
|
||||
<p>Some reasons you might want to use REST framework:</p>
|
||||
<ul>
|
||||
<li>The <a href="http://restframework.herokuapp.com/">Web browseable API</a> is a huge usability win for your developers.</li>
|
||||
<li>The <a href="http://restframework.herokuapp.com/">Web browsable API</a> is a huge usability win for your developers.</li>
|
||||
<li><a href="api-guide/authentication/">Authentication policies</a> including <a href="./api-guide/authentication#oauthauthentication">OAuth1a</a> and <a href="./api-guide/authentication#oauth2authentication">OAuth2</a> out of the box.</li>
|
||||
<li><a href="api-guide/serializers/">Serialization</a> that supports both <a href="./api-guide/serializers#modelserializer">ORM</a> and <a href="./api-guide/serializers#serializers">non-ORM</a> data sources.</li>
|
||||
<li>Customizable all the way down - just use <a href="./api-guide/views#function-based-views">regular function-based views</a> if you don't need the <a href="api-guide/generic-views/">more</a> <a href="api-guide/viewsets/">powerful</a> <a href="api-guide/routers/">features</a>.</li>
|
||||
|
@ -564,7 +564,7 @@ router = routers.DefaultRouter()
|
|||
router.register(r'users', UserViewSet)
|
||||
|
||||
# Wire up our API using automatic URL routing.
|
||||
# Additionally, we include login URLs for the browseable API.
|
||||
# Additionally, we include login URLs for the browsable API.
|
||||
urlpatterns = [
|
||||
url(r'^', include(router.urls)),
|
||||
url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework'))
|
||||
|
|
|
@ -464,7 +464,7 @@ router.register(r'groups', GroupViewSet)
|
|||
|
||||
|
||||
# Wire up our API using automatic URL routing.
|
||||
# Additionally, we include login URLs for the browseable API.
|
||||
# Additionally, we include login URLs for the browsable API.
|
||||
urlpatterns = [
|
||||
url(r'^', include(router.urls)),
|
||||
url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework'))
|
||||
|
@ -586,8 +586,8 @@ urlpatterns = [
|
|||
<p>In the unlikely event that you're providing a custom serializer class, and implementing these methods you should note the new call signature for both methods is now <code>(self, model_field, related_model, to_many)</code>. For reverse relationships <code>model_field</code> will be <code>None</code>.</p>
|
||||
<p>The old-style signature will continue to function but will raise a <code>PendingDeprecationWarning</code>.</p>
|
||||
<h2 id="view-names-and-descriptions">View names and descriptions</h2>
|
||||
<p>The mechanics of how the names and descriptions used in the browseable API are generated has been modified and cleaned up somewhat.</p>
|
||||
<p>If you've been customizing this behavior, for example perhaps to use <code>rst</code> markup for the browseable API, then you'll need to take a look at the implementation to see what updates you need to make.</p>
|
||||
<p>The mechanics of how the names and descriptions used in the browsable API are generated has been modified and cleaned up somewhat.</p>
|
||||
<p>If you've been customizing this behavior, for example perhaps to use <code>rst</code> markup for the browsable API, then you'll need to take a look at the implementation to see what updates you need to make.</p>
|
||||
<p>Note that the relevant methods have always been private APIs, and the docstrings called them out as intended to be deprecated.</p>
|
||||
<hr />
|
||||
<h1 id="other-notes">Other notes</h1>
|
||||
|
|
|
@ -467,7 +467,6 @@
|
|||
virtualenv env
|
||||
source env/bin/activate
|
||||
pip install -r requirements.txt
|
||||
pip install -r requirements-test.txt
|
||||
|
||||
# Run the tests
|
||||
./runtests.py
|
||||
|
|
|
@ -498,7 +498,7 @@
|
|||
<li>Add <code>UnicodeYAMLRenderer</code> that extends <code>YAMLRenderer</code> with unicode.</li>
|
||||
<li>Fix <code>parse_header</code> argument convertion.</li>
|
||||
<li>Fix mediatype detection under Python 3.</li>
|
||||
<li>Web browseable API now offers blank option on dropdown when the field is not required.</li>
|
||||
<li>Web browsable API now offers blank option on dropdown when the field is not required.</li>
|
||||
<li><code>APIException</code> representation improved for logging purposes.</li>
|
||||
<li>Allow source="*" within nested serializers.</li>
|
||||
<li>Better support for custom oauth2 provider backends.</li>
|
||||
|
@ -571,7 +571,7 @@
|
|||
<li>Added <code>MAX_PAGINATE_BY</code> setting and <code>max_paginate_by</code> generic view attribute.</li>
|
||||
<li>Added <code>cache</code> attribute to throttles to allow overriding of default cache.</li>
|
||||
<li>'Raw data' tab in browsable API now contains pre-populated data.</li>
|
||||
<li>'Raw data' and 'HTML form' tab preference in browseable API now saved between page views.</li>
|
||||
<li>'Raw data' and 'HTML form' tab preference in browsable API now saved between page views.</li>
|
||||
<li>Bugfix: <code>required=True</code> argument fixed for boolean serializer fields.</li>
|
||||
<li>Bugfix: <code>client.force_authenticate(None)</code> should also clear session info if it exists.</li>
|
||||
<li>Bugfix: Client sending empty string instead of file now clears <code>FileField</code>.</li>
|
||||
|
|
|
@ -437,7 +437,7 @@ You probably want to also tag the version now:
|
|||
<h4 id="tests">Tests</h4>
|
||||
<p>The cookiecutter template includes a <code>runtests.py</code> which uses the <code>pytest</code> package as a test runner.</p>
|
||||
<p>Before running, you'll need to install a couple test requirements.</p>
|
||||
<pre><code>$ pip install -r requirements-test.txt
|
||||
<pre><code>$ pip install -r requirements.txt
|
||||
</code></pre>
|
||||
<p>Once requirements installed, you can run <code>runtests.py</code>.</p>
|
||||
<pre><code>$ ./runtests.py
|
||||
|
|
|
@ -483,7 +483,7 @@ from snippets.models import Snippet, LANGUAGE_CHOICES, STYLE_CHOICES
|
|||
|
||||
class SnippetSerializer(serializers.Serializer):
|
||||
pk = serializers.IntegerField(read_only=True)
|
||||
title = serializers.CharField(required=False,
|
||||
title = serializers.CharField(required=False, allow_blank=True
|
||||
max_length=100)
|
||||
code = serializers.CharField(style={'type': 'textarea'})
|
||||
linenos = serializers.BooleanField(required=False)
|
||||
|
@ -492,21 +492,21 @@ class SnippetSerializer(serializers.Serializer):
|
|||
style = serializers.ChoiceField(choices=STYLE_CHOICES,
|
||||
default='friendly')
|
||||
|
||||
def create(self, validated_attrs):
|
||||
def create(self, validated_data):
|
||||
"""
|
||||
Create and return a new `Snippet` instance, given the validated data.
|
||||
"""
|
||||
return Snippet.objects.create(**validated_attrs)
|
||||
return Snippet.objects.create(**validated_data)
|
||||
|
||||
def update(self, instance, validated_attrs):
|
||||
def update(self, instance, validated_data):
|
||||
"""
|
||||
Update and return an existing `Snippet` instance, given the validated data.
|
||||
"""
|
||||
instance.title = validated_attrs.get('title', instance.title)
|
||||
instance.code = validated_attrs.get('code', instance.code)
|
||||
instance.linenos = validated_attrs.get('linenos', instance.linenos)
|
||||
instance.language = validated_attrs.get('language', instance.language)
|
||||
instance.style = validated_attrs.get('style', instance.style)
|
||||
instance.title = validated_data.get('title', instance.title)
|
||||
instance.code = validated_data.get('code', instance.code)
|
||||
instance.linenos = validated_data.get('linenos', instance.linenos)
|
||||
instance.language = validated_data.get('language', instance.language)
|
||||
instance.style = validated_data.get('style', instance.style)
|
||||
instance.save()
|
||||
return instance
|
||||
</code></pre>
|
||||
|
@ -552,7 +552,7 @@ data = JSONParser().parse(stream)
|
|||
<pre><code>serializer = SnippetSerializer(data=data)
|
||||
serializer.is_valid()
|
||||
# True
|
||||
serializer.object
|
||||
serializer.save()
|
||||
# <Snippet: Snippet object>
|
||||
</code></pre>
|
||||
<p>Notice how similar the API is to working with forms. The similarity should become even more apparent when we start writing views that use our serializer.</p>
|
||||
|
@ -574,7 +574,7 @@ Open the file <code>snippets/serializers.py</code> again, and edit the <code>Sni
|
|||
<p>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 <code>python manange.py shell</code>, then try the following:</p>
|
||||
<pre><code>>>> from snippets.serializers import SnippetSerializer
|
||||
>>> serializer = SnippetSerializer()
|
||||
>>> print repr(serializer) # In python 3 use `print(repr(serializer))`
|
||||
>>> print(repr(serializer))
|
||||
SnippetSerializer():
|
||||
id = IntegerField(label='ID', read_only=True)
|
||||
title = CharField(allow_blank=True, max_length=100, required=False)
|
||||
|
@ -657,7 +657,7 @@ def snippet_detail(request, pk):
|
|||
return HttpResponse(status=204)
|
||||
</code></pre>
|
||||
<p>Finally we need to wire these views up. Create the <code>snippets/urls.py</code> file:</p>
|
||||
<pre><code>from django.conf.urls import patterns, url
|
||||
<pre><code>from django.conf.urls import url
|
||||
from snippets import views
|
||||
|
||||
urlpatterns = [
|
||||
|
|
|
@ -480,7 +480,7 @@ router.register(r'snippets', views.SnippetViewSet)
|
|||
router.register(r'users', views.UserViewSet)
|
||||
|
||||
# The API URLs are now determined automatically by the router.
|
||||
# Additionally, we include the login URLs for the browseable API.
|
||||
# Additionally, we include the login URLs for the browsable API.
|
||||
urlpatterns = [
|
||||
url(r'^', include(router.urls)),
|
||||
url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework'))
|
||||
|
@ -492,7 +492,7 @@ urlpatterns = [
|
|||
<p>Using viewsets can be a really useful abstraction. It helps ensure that URL conventions will be consistent across your API, minimizes the amount of code you need to write, and allows you to concentrate on the interactions and representations your API provides rather than the specifics of the URL conf.</p>
|
||||
<p>That doesn't mean it's always the right approach to take. There's a similar set of trade-offs to consider as when using class-based views instead of function based views. Using viewsets is less explicit than building your views individually.</p>
|
||||
<h2 id="reviewing-our-work">Reviewing our work</h2>
|
||||
<p>With an incredibly small amount of code, we've now got a complete pastebin Web API, which is fully web browseable, and comes complete with authentication, per-object permissions, and multiple renderer formats.</p>
|
||||
<p>With an incredibly small amount of code, we've now got a complete pastebin Web API, which is fully web browsable, and comes complete with authentication, per-object permissions, and multiple renderer formats.</p>
|
||||
<p>We've walked through each step of the design process, and seen how if we need to customize anything we can gradually work our way down to simply using regular Django views.</p>
|
||||
<p>You can review the final <a href="https://github.com/tomchristie/rest-framework-tutorial">tutorial code</a> on GitHub, or try out a live example in <a href="http://restframework.herokuapp.com/">the sandbox</a>.</p>
|
||||
<h2 id="onwards-and-upwards">Onwards and upwards</h2>
|
||||
|
|
|
@ -403,10 +403,9 @@ pip install django
|
|||
pip install djangorestframework
|
||||
|
||||
# Set up a new project with a single application
|
||||
django-admin.py startproject tutorial
|
||||
django-admin.py startproject tutorial .
|
||||
cd tutorial
|
||||
django-admin.py startapp quickstart
|
||||
cd ..
|
||||
</code></pre>
|
||||
<p>Now sync your database for the first time:</p>
|
||||
<pre><code>python manage.py migrate
|
||||
|
@ -470,7 +469,7 @@ router.register(r'users', views.UserViewSet)
|
|||
router.register(r'groups', views.GroupViewSet)
|
||||
|
||||
# Wire up our API using automatic URL routing.
|
||||
# Additionally, we include login URLs for the browseable API.
|
||||
# Additionally, we include login URLs for the browsable API.
|
||||
urlpatterns = [
|
||||
url(r'^', include(router.urls)),
|
||||
url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework'))
|
||||
|
|
Loading…
Reference in New Issue
Block a user