aboutsummaryrefslogtreecommitdiffstats
path: root/docs/api-guide
diff options
context:
space:
mode:
authorTom Christie2014-12-12 15:37:43 +0000
committerTom Christie2014-12-12 15:37:43 +0000
commitbaaa356489dd51d7c68161db40e99cd59b1124c3 (patch)
tree23dc5c4cbe1065580ff88ddd1bfa6dcda956ac68 /docs/api-guide
parent5e6052811716a494e995a84c497579867ee6acaa (diff)
parentfd473aa905337908b41c9a1087967a19f0558f89 (diff)
downloaddjango-rest-framework-baaa356489dd51d7c68161db40e99cd59b1124c3.tar.bz2
Merge master
Diffstat (limited to 'docs/api-guide')
-rw-r--r--docs/api-guide/fields.md55
-rw-r--r--docs/api-guide/serializers.md58
-rw-r--r--docs/api-guide/validators.md2
-rw-r--r--docs/api-guide/views.md15
-rw-r--r--docs/api-guide/viewsets.md3
5 files changed, 113 insertions, 20 deletions
diff --git a/docs/api-guide/fields.md b/docs/api-guide/fields.md
index 391a52e5..e4ef1d4a 100644
--- a/docs/api-guide/fields.md
+++ b/docs/api-guide/fields.md
@@ -310,6 +310,9 @@ Used by `ModelSerializer` to automatically generate fields if the corresponding
**Signature:** `ChoiceField(choices)`
- `choices` - A list of valid values, or a list of `(key, display_name)` tuples.
+- `allow_blank` - If set to `True` then the empty string should be considered a valid value. If set to `False` then the empty string is considered invalid and will raise a validation error. Defaults to `False`.
+
+Both the `allow_blank` and `allow_null` are valid options on `ChoiceField`, although it is highly recommended that you only use one and not both. `allow_blank` should be preferred for textual choices, and `allow_null` should be preferred for numeric or other non-textual choices.
## MultipleChoiceField
@@ -318,6 +321,9 @@ A field that can accept a set of zero, one or many values, chosen from a limited
**Signature:** `MultipleChoiceField(choices)`
- `choices` - A list of valid values, or a list of `(key, display_name)` tuples.
+- `allow_blank` - If set to `True` then the empty string should be considered a valid value. If set to `False` then the empty string is considered invalid and will raise a validation error. Defaults to `False`.
+
+As with `ChoiceField`, both the `allow_blank` and `allow_null` options are valid, although it is highly recommended that you only use one and not both. `allow_blank` should be preferred for textual choices, and `allow_null` should be preferred for numeric or other non-textual choices.
---
@@ -453,7 +459,7 @@ If you want to create a custom field, you'll need to subclass `Field` and then o
The `.to_representation()` method is called to convert the initial datatype into a primitive, serializable datatype.
-The `to_internal_value()` method is called to restore a primitive datatype into its internal python representation.
+The `to_internal_value()` method is called to restore a primitive datatype into its internal python representation. This method should raise a `serializer.ValidationError` if the data is invalid.
Note that the `WritableField` class that was present in version 2.x no longer exists. You should subclass `Field` and override `to_internal_value()` if the field supports data input.
@@ -498,6 +504,53 @@ As an example, let's create a field that can be used represent the class name of
"""
return obj.__class__.__name__
+#### Raising validation errors
+
+Our `ColorField` class above currently does not perform any data validation.
+To indicate invalid data, we should raise a `serializers.ValidationError`, like so:
+
+ 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)
+
+The `.fail()` method is a shortcut for raising `ValidationError` that takes a message string from the `error_messages` dictionary. For example:
+
+ 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)
+
+This style keeps you error messages more cleanly separated from your code, and should be preferred.
+
# Third party packages
The following third party packages are also available.
diff --git a/docs/api-guide/serializers.md b/docs/api-guide/serializers.md
index 0ee80d53..5fe6b4c2 100644
--- a/docs/api-guide/serializers.md
+++ b/docs/api-guide/serializers.md
@@ -96,7 +96,7 @@ If we want to be able to return complete object instances based on the validated
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 `Comment` was a Django model, the methods might look like this:
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)
@@ -104,7 +104,7 @@ If your object instances correspond to Django models you'll also want to ensure
instance.created = validated_data.get('created', instance.created)
instance.save()
return instance
-
+
Now when deserializing data, we can call `.save()` to return an object instance, based on the validated data.
comment = serializer.save()
@@ -113,7 +113,7 @@ Calling `.save()` will either create a new instance, or update an existing insta
# .save() will create a new instance.
serializer = CommentSerializer(data=data)
-
+
# .save() will update the existing `comment` instance.
serializer = CommentSerializer(comment, data=data)
@@ -140,7 +140,7 @@ For example:
class ContactForm(serializers.Serializer):
email = serializers.EmailField()
message = serializers.CharField()
-
+
def save(self):
email = self.validated_data['email']
message = self.validated_data['message']
@@ -230,7 +230,7 @@ Serializer classes can also include reusable validators that are applied to the
name = serializers.CharField()
room_number = serializers.IntegerField(choices=[101, 102, 103, 201])
date = serializers.DateField()
-
+
class Meta:
# Each room only has one event per day.
validators = UniqueTogetherValidator(
@@ -326,9 +326,9 @@ Here's an example for an `update()` method on our previous `UserSerializer` clas
# would need to be handled.
profile = instance.profile
- user.username = validated_data.get('username', instance.username)
- user.email = validated_data.get('email', instance.email)
- user.save()
+ instance.username = validated_data.get('username', instance.username)
+ instance.email = validated_data.get('email', instance.email)
+ instance.save()
profile.is_premium_member = profile_data.get(
'is_premium_member',
@@ -340,7 +340,7 @@ Here's an example for an `update()` method on our previous `UserSerializer` clas
)
profile.save()
- return user
+ return instance
Because the behavior of nested creates and updates can be ambiguous, and may require complex dependancies between related models, REST framework 3 requires you to always write these methods explicitly. The default `ModelSerializer` `.create()` and `.update()` methods do not include support for writable nested representations.
@@ -448,7 +448,7 @@ To do so, open the Django shell, using `python manage.py shell`, then import the
id = IntegerField(label='ID', read_only=True)
name = CharField(allow_blank=True, max_length=100, required=False)
owner = PrimaryKeyRelatedField(queryset=User.objects.all())
-
+
## Specifying which fields should be included
If you only want a subset of the default fields to be used in a model serializer, you can do so using `fields` or `exclude` options, just as you would with a `ModelForm`.
@@ -505,6 +505,21 @@ This option should be a list or tuple of field names, and is declared as follows
Model fields which have `editable=False` set, and `AutoField` fields will be set to read-only by default, and do not need to be added to the `read_only_fields` option.
+---
+
+**Note**: There is a special-case where a read-only field is part of a `unique_together` constraint at the model level. In this case the field is required by the serializer class in order to validate the constraint, but should also not be editable by the user.
+
+The right way to deal with this is to specify the field explicitly on the serializer, providing both the `read_only=True` and `default=…` keyword arguments.
+
+One example of this is a read-only relation to the currently authenticated `User` which is `unique_together` with another identifier. In this case you would declare the user field like so:
+
+ user = serializers.PrimaryKeyRelatedField(read_only=True, default=serializers.CurrentUserDefault())
+
+Please review the [Validators Documentation](/api-guide/validators/) for details on the [UniqueTogetherValidator](/api-guide/validators/#uniquetogethervalidator) and [CurrentUserDefault](/api-guide/validators/#currentuserdefault) classes.
+
+---
+
+
## Specifying additional keyword arguments for fields.
There is also a shortcut allowing you to specify arbitrary additional keyword arguments on fields, using the `extra_kwargs` option. Similarly to `read_only_fields` this means you do not need to explicitly declare the field on the serializer.
@@ -516,7 +531,7 @@ This option is a dictionary, mapping field names to a dictionary of keyword argu
model = User
fields = ('email', 'username', 'password')
extra_kwargs = {'password': {'write_only': True}}
-
+
def create(self, validated_data):
user = User(
email=validated_data['email'],
@@ -567,13 +582,13 @@ There needs to be a way of determining which views should be used for hyperlinki
By default hyperlinks are expected to correspond to a view name that matches the style `'{model_name}-detail'`, and looks up the instance by a `pk` keyword argument.
-You can override a URL field view name and lookup field by using either, or both of, the `view_name` and `lookup_field` options in the `extra_field_kwargs` setting, like so:
+You can override a URL field view name and lookup field by using either, or both of, the `view_name` and `lookup_field` options in the `extra_kwargs` setting, like so:
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'}
}
@@ -656,7 +671,7 @@ To support multiple updates you'll need to do so explicitly. When writing your m
* How do you determine which instance should be updated for each item in the list of data?
* How should insertions be handled? Are they invalid, or do they create new objects?
* How should removals be handled? Do they imply object deletion, or removing a relationship? Should they be silently ignored, or are they invalid?
-* How should ordering be handled? Does changing the position of two items imply any state change or is it ignored?
+* How should ordering be handled? Does changing the position of two items imply any state change or is it ignored?
Here's an example of how you might choose to implement multiple updates:
@@ -689,6 +704,21 @@ Here's an example of how you might choose to implement multiple updates:
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 `allow_add_remove` behavior that was present in REST framework 2.
+#### Customizing ListSerializer initialization
+
+When a serializer with `many=True` is instantiated, we need to determine which arguments and keyword arguments should be passed to the `.__init__()` method for both the child `Serializer` class, and for the parent `ListSerializer` class.
+
+The default implementation is to pass all arguments to both classes, except for `validators`, and any custom keyword arguments, both of which are assumed to be intended for the child serializer class.
+
+Occasionally you might need to explicitly specify how the child and parent classes should be instantiated when `many=True` is passed. You can do so by using the `many_init` class method.
+
+ @classmethod
+ def many_init(cls, *args, **kwargs):
+ # Instantiate the child serializer.
+ kwargs['child'] = cls()
+ # Instantiate the parent list serializer.
+ return CustomListSerializer(*args, **kwargs)
+
---
# BaseSerializer
diff --git a/docs/api-guide/validators.md b/docs/api-guide/validators.md
index f087e191..8f5a8929 100644
--- a/docs/api-guide/validators.md
+++ b/docs/api-guide/validators.md
@@ -1,4 +1,4 @@
-<a class="github" href="validators.py"></a>
+source: validators.py
---
diff --git a/docs/api-guide/views.md b/docs/api-guide/views.md
index 31c62682..291fe737 100644
--- a/docs/api-guide/views.md
+++ b/docs/api-guide/views.md
@@ -127,19 +127,26 @@ REST framework also allows you to work with regular function based views. It pr
## @api_view()
-**Signature:** `@api_view(http_method_names)`
+**Signature:** `@api_view(http_method_names=['GET'])`
-The core of this functionality is the `api_view` decorator, which takes a list of HTTP methods that your view should respond to. For example, this is how you would write a very simple view that just manually returns some data:
+The core of this functionality is the `api_view` decorator, which takes a list of HTTP methods that your view should respond to. For example, this is how you would write a very simple view that just manually returns some data:
from rest_framework.decorators import api_view
- @api_view(['GET'])
+ @api_view()
def hello_world(request):
return Response({"message": "Hello, world!"})
-
This view will use the default renderers, parsers, authentication classes etc specified in the [settings].
+By default only `GET` methods will be accepted. Other methods will respond with "405 Method Not Allowed". To alter this behavior, specify which methods the view allows, like so:
+
+ @api_view(['GET', 'POST'])
+ def hello_world(request):
+ if request.method == 'POST':
+ return Response({"message": "Got some data!", "data": request.data})
+ return Response({"message": "Hello, world!"})
+
## API policy decorators
To override the default settings, REST framework provides a set of additional decorators which can be added to your views. These must come *after* (below) the `@api_view` decorator. For example, to create a view that uses a [throttle][throttling] to ensure it can only be called once per day by a particular user, use the `@throttle_classes` decorator, passing a list of throttle classes:
diff --git a/docs/api-guide/viewsets.md b/docs/api-guide/viewsets.md
index 28186c64..3e37cef8 100644
--- a/docs/api-guide/viewsets.md
+++ b/docs/api-guide/viewsets.md
@@ -201,6 +201,8 @@ Note that you can use any of the standard attributes or method overrides provide
def get_queryset(self):
return self.request.user.accounts.all()
+Note however that upon removal of the `queryset` property from your `ViewSet`, any associated [router][routers] will be unable to derive the base_name of your Model automatically, and so you you will have to specify the `base_name` kwarg as part of your [router registration][routers].
+
Also note that although this class provides the complete set of create/list/retrieve/update/destroy actions by default, you can restrict the available operations by using the standard permission classes.
## ReadOnlyModelViewSet
@@ -243,3 +245,4 @@ To create a base viewset class that provides `create`, `list` and `retrieve` ope
By creating your own base `ViewSet` classes, you can provide common behavior that can be reused in multiple viewsets across your API.
[cite]: http://guides.rubyonrails.org/routing.html
+[routers]: routers.md