mirror of
https://github.com/encode/django-rest-framework.git
synced 2024-11-22 09:36:49 +03:00
Added write_only and write_only_fields. Refs #1306
This commit is contained in:
parent
bc6c5df109
commit
85d74fc86a
|
@ -28,7 +28,13 @@ Defaults to the name of the field.
|
|||
|
||||
### `read_only`
|
||||
|
||||
Set this to `True` to ensure that the field is used when serializing a representation, but is not used when updating an instance during deserialization.
|
||||
Set this to `True` to ensure that the field is used when serializing a representation, but is not used when creating or updating an instance during deserialization.
|
||||
|
||||
Defaults to `False`
|
||||
|
||||
### `write_only`
|
||||
|
||||
Set this to `True` to ensure that the field may be used when updating or creating an instance, but is not included when serializing the representation.
|
||||
|
||||
Defaults to `False`
|
||||
|
||||
|
|
|
@ -373,6 +373,25 @@ You may wish to specify multiple fields as read-only. Instead of adding each fi
|
|||
|
||||
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.
|
||||
|
||||
## Specifying which fields should be write-only
|
||||
|
||||
You may wish to specify multiple fields as write-only. Instead of adding each field explicitly with the `write_only=True` attribute, you may use the `write_only_fields` Meta option, like so:
|
||||
|
||||
class CreateUserSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = User
|
||||
fields = ('email', 'username', 'password')
|
||||
write_only_fields = ('password',) # Note: Password field is write-only
|
||||
|
||||
def restore_object(self, attrs, instance=None):
|
||||
"""
|
||||
Instantiate a new User instance.
|
||||
"""
|
||||
assert instance is None, 'Cannot update users with CreateUserSerializer'
|
||||
user = User(email=attrs['email'], username=attrs['username'])
|
||||
user.set_password(attrs['password'])
|
||||
return user
|
||||
|
||||
## Specifying fields explicitly
|
||||
|
||||
You can add extra fields to a `ModelSerializer` or override the default fields by declaring fields on the class, just as you would for a `Serializer` class.
|
||||
|
|
|
@ -40,8 +40,12 @@ You can determine your currently installed version using `pip freeze`:
|
|||
|
||||
## 2.3.x series
|
||||
|
||||
### Master
|
||||
### 2.3.11
|
||||
|
||||
**Date**: 14th January 2014
|
||||
|
||||
* Added `write_only` serializer field argument.
|
||||
* Added `write_only_fields` option to `ModelSerializer` classes.
|
||||
* JSON renderer now deals with objects that implement a dict-like interface.
|
||||
* Fix compatiblity with newer versions of `django-oauth-plus`.
|
||||
* Bugfix: Refine behavior that calls model manager `all()` across nested serializer relationships, preventing erronous behavior with some non-ORM objects, and preventing unneccessary queryset re-evaluations.
|
||||
|
|
|
@ -114,6 +114,10 @@ def strip_multiple_choice_msg(help_text):
|
|||
return help_text.replace(multiple_choice_msg, '')
|
||||
|
||||
|
||||
class IgnoreFieldException(Exception):
|
||||
pass
|
||||
|
||||
|
||||
class Field(object):
|
||||
read_only = True
|
||||
creation_counter = 0
|
||||
|
@ -246,6 +250,7 @@ class WritableField(Field):
|
|||
"""
|
||||
Base for read/write fields.
|
||||
"""
|
||||
write_only = False
|
||||
default_validators = []
|
||||
default_error_messages = {
|
||||
'required': _('This field is required.'),
|
||||
|
@ -255,7 +260,7 @@ class WritableField(Field):
|
|||
default = None
|
||||
|
||||
def __init__(self, source=None, label=None, help_text=None,
|
||||
read_only=False, required=None,
|
||||
read_only=False, write_only=False, required=None,
|
||||
validators=[], error_messages=None, widget=None,
|
||||
default=None, blank=None):
|
||||
|
||||
|
@ -269,6 +274,10 @@ class WritableField(Field):
|
|||
super(WritableField, self).__init__(source=source, label=label, help_text=help_text)
|
||||
|
||||
self.read_only = read_only
|
||||
self.write_only = write_only
|
||||
|
||||
assert not (read_only and write_only), "Cannot set read_only=True and write_only=True"
|
||||
|
||||
if required is None:
|
||||
self.required = not(read_only)
|
||||
else:
|
||||
|
@ -318,6 +327,11 @@ class WritableField(Field):
|
|||
if errors:
|
||||
raise ValidationError(errors)
|
||||
|
||||
def field_to_native(self, obj, field_name):
|
||||
if self.write_only:
|
||||
raise IgnoreFieldException()
|
||||
return super(WritableField, self).field_to_native(obj, field_name)
|
||||
|
||||
def field_from_native(self, data, files, field_name, into):
|
||||
"""
|
||||
Given a dictionary and a field name, updates the dictionary `into`,
|
||||
|
|
|
@ -344,7 +344,10 @@ class BaseSerializer(WritableField):
|
|||
continue
|
||||
field.initialize(parent=self, field_name=field_name)
|
||||
key = self.get_field_key(field_name)
|
||||
value = field.field_to_native(obj, field_name)
|
||||
try:
|
||||
value = field.field_to_native(obj, field_name)
|
||||
except IgnoreFieldException:
|
||||
continue
|
||||
method = getattr(self, 'transform_%s' % field_name, None)
|
||||
if callable(method):
|
||||
value = method(obj, value)
|
||||
|
@ -383,6 +386,9 @@ class BaseSerializer(WritableField):
|
|||
Override default so that the serializer can be used as a nested field
|
||||
across relationships.
|
||||
"""
|
||||
if self.write_only:
|
||||
raise IgnoreFieldException()
|
||||
|
||||
if self.source == '*':
|
||||
return self.to_native(obj)
|
||||
|
||||
|
@ -615,6 +621,7 @@ class ModelSerializerOptions(SerializerOptions):
|
|||
super(ModelSerializerOptions, self).__init__(meta)
|
||||
self.model = getattr(meta, 'model', None)
|
||||
self.read_only_fields = getattr(meta, 'read_only_fields', ())
|
||||
self.write_only_fields = getattr(meta, 'write_only_fields', ())
|
||||
|
||||
|
||||
class ModelSerializer(Serializer):
|
||||
|
@ -754,17 +761,29 @@ class ModelSerializer(Serializer):
|
|||
# Add the `read_only` flag to any fields that have bee specified
|
||||
# in the `read_only_fields` option
|
||||
for field_name in self.opts.read_only_fields:
|
||||
assert field_name not in self.base_fields.keys(), \
|
||||
"field '%s' on serializer '%s' specified in " \
|
||||
"`read_only_fields`, but also added " \
|
||||
"as an explicit field. Remove it from `read_only_fields`." % \
|
||||
(field_name, self.__class__.__name__)
|
||||
assert field_name in ret, \
|
||||
"Non-existant field '%s' specified in `read_only_fields` " \
|
||||
"on serializer '%s'." % \
|
||||
(field_name, self.__class__.__name__)
|
||||
assert field_name not in self.base_fields.keys(), (
|
||||
"field '%s' on serializer '%s' specified in "
|
||||
"`read_only_fields`, but also added "
|
||||
"as an explicit field. Remove it from `read_only_fields`." %
|
||||
(field_name, self.__class__.__name__))
|
||||
assert field_name in ret, (
|
||||
"Non-existant field '%s' specified in `read_only_fields` "
|
||||
"on serializer '%s'." %
|
||||
(field_name, self.__class__.__name__))
|
||||
ret[field_name].read_only = True
|
||||
|
||||
for field_name in self.opts.write_only_fields:
|
||||
assert field_name not in self.base_fields.keys(), (
|
||||
"field '%s' on serializer '%s' specified in "
|
||||
"`write_only_fields`, but also added "
|
||||
"as an explicit field. Remove it from `write_only_fields`." %
|
||||
(field_name, self.__class__.__name__))
|
||||
assert field_name in ret, (
|
||||
"Non-existant field '%s' specified in `write_only_fields` "
|
||||
"on serializer '%s'." %
|
||||
(field_name, self.__class__.__name__))
|
||||
ret[field_name].write_only = True
|
||||
|
||||
return ret
|
||||
|
||||
def get_pk_field(self, model_field):
|
||||
|
|
42
rest_framework/tests/test_write_only_fields.py
Normal file
42
rest_framework/tests/test_write_only_fields.py
Normal file
|
@ -0,0 +1,42 @@
|
|||
from django.db import models
|
||||
from django.test import TestCase
|
||||
from rest_framework import serializers
|
||||
|
||||
|
||||
class ExampleModel(models.Model):
|
||||
email = models.EmailField(max_length=100)
|
||||
password = models.CharField(max_length=100)
|
||||
|
||||
|
||||
class WriteOnlyFieldTests(TestCase):
|
||||
def test_write_only_fields(self):
|
||||
class ExampleSerializer(serializers.Serializer):
|
||||
email = serializers.EmailField()
|
||||
password = serializers.CharField(write_only=True)
|
||||
|
||||
data = {
|
||||
'email': 'foo@example.com',
|
||||
'password': '123'
|
||||
}
|
||||
serializer = ExampleSerializer(data=data)
|
||||
self.assertTrue(serializer.is_valid())
|
||||
self.assertEquals(serializer.object, data)
|
||||
self.assertEquals(serializer.data, {'email': 'foo@example.com'})
|
||||
|
||||
def test_write_only_fields_meta(self):
|
||||
class ExampleSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = ExampleModel
|
||||
fields = ('email', 'password')
|
||||
write_only_fields = ('password',)
|
||||
|
||||
data = {
|
||||
'email': 'foo@example.com',
|
||||
'password': '123'
|
||||
}
|
||||
serializer = ExampleSerializer(data=data)
|
||||
self.assertTrue(serializer.is_valid())
|
||||
self.assertTrue(isinstance(serializer.object, ExampleModel))
|
||||
self.assertEquals(serializer.object.email, data['email'])
|
||||
self.assertEquals(serializer.object.password, data['password'])
|
||||
self.assertEquals(serializer.data, {'email': 'foo@example.com'})
|
Loading…
Reference in New Issue
Block a user