mirror of
https://github.com/encode/django-rest-framework.git
synced 2024-11-26 03:23:59 +03:00
Added DictField and support for HStoreField.
This commit is contained in:
parent
889a07f556
commit
35f6a82462
|
@ -380,7 +380,7 @@ A field class that validates a list of objects.
|
|||
|
||||
**Signature**: `ListField(child)`
|
||||
|
||||
- `child` - A field instance that should be used for validating the objects in the list.
|
||||
- `child` - A field instance that should be used for validating the objects in the list. If this argument is not provided then objects in the list will not be validated.
|
||||
|
||||
For example, to validate a list of integers you might use something like the following:
|
||||
|
||||
|
@ -395,6 +395,23 @@ The `ListField` class also supports a declarative style that allows you to write
|
|||
|
||||
We can now reuse our custom `StringListField` class throughout our application, without having to provide a `child` argument to it.
|
||||
|
||||
## DictField
|
||||
|
||||
A field class that validates a dictionary of objects. The keys in `DictField` are always assumed to be string values.
|
||||
|
||||
**Signature**: `DictField(child)`
|
||||
|
||||
- `child` - A field instance that should be used for validating the values in the dictionary. If this argument is not provided then values in the mapping will not be validated.
|
||||
|
||||
For example, to create a field that validates a mapping of strings to strings, you would write something like this:
|
||||
|
||||
document = DictField(child=CharField())
|
||||
|
||||
You can also use the declarative style, as with `ListField`. For example:
|
||||
|
||||
class DocumentField(DictField):
|
||||
child = CharField()
|
||||
|
||||
---
|
||||
|
||||
# Miscellaneous fields
|
||||
|
|
|
@ -58,6 +58,13 @@ except ImportError:
|
|||
from django.http import HttpResponse as HttpResponseBase
|
||||
|
||||
|
||||
# contrib.postgres only supported from 1.8 onwards.
|
||||
try:
|
||||
from django.contrib.postgres import fields as postgres_fields
|
||||
except ImportError:
|
||||
postgres_fields = None
|
||||
|
||||
|
||||
# request only provides `resolver_match` from 1.5 onwards.
|
||||
def get_resolver_match(request):
|
||||
try:
|
||||
|
|
|
@ -1132,8 +1132,21 @@ class ImageField(FileField):
|
|||
|
||||
# Composite field types...
|
||||
|
||||
class _UnvalidatedField(Field):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(_UnvalidatedField, self).__init__(*args, **kwargs)
|
||||
self.allow_blank = True
|
||||
self.allow_null = True
|
||||
|
||||
def to_internal_value(self, data):
|
||||
return data
|
||||
|
||||
def to_representation(self, value):
|
||||
return value
|
||||
|
||||
|
||||
class ListField(Field):
|
||||
child = None
|
||||
child = _UnvalidatedField()
|
||||
initial = []
|
||||
default_error_messages = {
|
||||
'not_a_list': _('Expected a list of items but got type `{input_type}`')
|
||||
|
@ -1141,7 +1154,6 @@ class ListField(Field):
|
|||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.child = kwargs.pop('child', copy.deepcopy(self.child))
|
||||
assert self.child is not None, '`child` is a required argument.'
|
||||
assert not inspect.isclass(self.child), '`child` has not been instantiated.'
|
||||
super(ListField, self).__init__(*args, **kwargs)
|
||||
self.child.bind(field_name='', parent=self)
|
||||
|
@ -1170,6 +1182,49 @@ class ListField(Field):
|
|||
return [self.child.to_representation(item) for item in data]
|
||||
|
||||
|
||||
class DictField(Field):
|
||||
child = _UnvalidatedField()
|
||||
initial = []
|
||||
default_error_messages = {
|
||||
'not_a_dict': _('Expected a dictionary of items but got type `{input_type}`')
|
||||
}
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.child = kwargs.pop('child', copy.deepcopy(self.child))
|
||||
assert not inspect.isclass(self.child), '`child` has not been instantiated.'
|
||||
super(DictField, self).__init__(*args, **kwargs)
|
||||
self.child.bind(field_name='', parent=self)
|
||||
|
||||
def get_value(self, dictionary):
|
||||
# We override the default field access in order to support
|
||||
# lists in HTML forms.
|
||||
if html.is_html_input(dictionary):
|
||||
return html.parse_html_list(dictionary, prefix=self.field_name)
|
||||
return dictionary.get(self.field_name, empty)
|
||||
|
||||
def to_internal_value(self, data):
|
||||
"""
|
||||
Dicts of native values <- Dicts of primitive datatypes.
|
||||
"""
|
||||
if html.is_html_input(data):
|
||||
data = html.parse_html_dict(data)
|
||||
if not isinstance(data, dict):
|
||||
self.fail('not_a_dict', input_type=type(data).__name__)
|
||||
return dict([
|
||||
(six.text_type(key), self.child.run_validation(value))
|
||||
for key, value in data.items()
|
||||
])
|
||||
|
||||
def to_representation(self, value):
|
||||
"""
|
||||
List of object instances -> List of dicts of primitive datatypes.
|
||||
"""
|
||||
return dict([
|
||||
(six.text_type(key), self.child.to_representation(val))
|
||||
for key, val in value.items()
|
||||
])
|
||||
|
||||
|
||||
# Miscellaneous field types...
|
||||
|
||||
class ReadOnlyField(Field):
|
||||
|
|
|
@ -14,7 +14,7 @@ from __future__ import unicode_literals
|
|||
from django.db import models
|
||||
from django.db.models.fields import FieldDoesNotExist, Field as DjangoField
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from rest_framework.compat import unicode_to_repr
|
||||
from rest_framework.compat import postgres_fields, unicode_to_repr
|
||||
from rest_framework.utils import model_meta
|
||||
from rest_framework.utils.field_mapping import (
|
||||
get_url_kwargs, get_field_kwargs,
|
||||
|
@ -1137,6 +1137,12 @@ class ModelSerializer(Serializer):
|
|||
if hasattr(models, 'UUIDField'):
|
||||
ModelSerializer._field_mapping[models.UUIDField] = UUIDField
|
||||
|
||||
if postgres_fields:
|
||||
class CharMappingField(DictField):
|
||||
child = CharField()
|
||||
|
||||
ModelSerializer._field_mapping[postgres_fields.HStoreField] = CharMappingField
|
||||
|
||||
|
||||
class HyperlinkedModelSerializer(ModelSerializer):
|
||||
"""
|
||||
|
|
|
@ -1047,7 +1047,7 @@ class TestValidImageField(FieldValues):
|
|||
|
||||
class TestListField(FieldValues):
|
||||
"""
|
||||
Values for `ListField`.
|
||||
Values for `ListField` with IntegerField as child.
|
||||
"""
|
||||
valid_inputs = [
|
||||
([1, 2, 3], [1, 2, 3]),
|
||||
|
@ -1064,6 +1064,55 @@ class TestListField(FieldValues):
|
|||
field = serializers.ListField(child=serializers.IntegerField())
|
||||
|
||||
|
||||
class TestUnvalidatedListField(FieldValues):
|
||||
"""
|
||||
Values for `ListField` with no `child` argument.
|
||||
"""
|
||||
valid_inputs = [
|
||||
([1, '2', True, [4, 5, 6]], [1, '2', True, [4, 5, 6]]),
|
||||
]
|
||||
invalid_inputs = [
|
||||
('not a list', ['Expected a list of items but got type `str`']),
|
||||
]
|
||||
outputs = [
|
||||
([1, '2', True, [4, 5, 6]], [1, '2', True, [4, 5, 6]]),
|
||||
]
|
||||
field = serializers.ListField()
|
||||
|
||||
|
||||
class TestDictField(FieldValues):
|
||||
"""
|
||||
Values for `ListField` with CharField as child.
|
||||
"""
|
||||
valid_inputs = [
|
||||
({'a': 1, 'b': '2', 3: 3}, {'a': '1', 'b': '2', '3': '3'}),
|
||||
]
|
||||
invalid_inputs = [
|
||||
({'a': 1, 'b': None}, ['This field may not be null.']),
|
||||
('not a dict', ['Expected a dictionary of items but got type `str`']),
|
||||
]
|
||||
outputs = [
|
||||
({'a': 1, 'b': '2', 3: 3}, {'a': '1', 'b': '2', '3': '3'}),
|
||||
]
|
||||
field = serializers.DictField(child=serializers.CharField())
|
||||
|
||||
|
||||
class TestUnvalidatedDictField(FieldValues):
|
||||
"""
|
||||
Values for `ListField` with no `child` argument.
|
||||
"""
|
||||
valid_inputs = [
|
||||
({'a': 1, 'b': [4, 5, 6], 1: 123}, {'a': 1, 'b': [4, 5, 6], '1': 123}),
|
||||
]
|
||||
invalid_inputs = [
|
||||
('not a dict', ['Expected a dictionary of items but got type `str`']),
|
||||
]
|
||||
outputs = [
|
||||
({'a': 1, 'b': [4, 5, 6]}, {'a': 1, 'b': [4, 5, 6]}),
|
||||
]
|
||||
field = serializers.DictField()
|
||||
|
||||
|
||||
# Tests for FieldField.
|
||||
# ---------------------
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user