mirror of
https://github.com/encode/django-rest-framework.git
synced 2024-11-22 17:47:04 +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)`
|
**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:
|
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.
|
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
|
# Miscellaneous fields
|
||||||
|
|
|
@ -58,6 +58,13 @@ except ImportError:
|
||||||
from django.http import HttpResponse as HttpResponseBase
|
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.
|
# request only provides `resolver_match` from 1.5 onwards.
|
||||||
def get_resolver_match(request):
|
def get_resolver_match(request):
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -1132,8 +1132,21 @@ class ImageField(FileField):
|
||||||
|
|
||||||
# Composite field types...
|
# 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):
|
class ListField(Field):
|
||||||
child = None
|
child = _UnvalidatedField()
|
||||||
initial = []
|
initial = []
|
||||||
default_error_messages = {
|
default_error_messages = {
|
||||||
'not_a_list': _('Expected a list of items but got type `{input_type}`')
|
'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):
|
def __init__(self, *args, **kwargs):
|
||||||
self.child = kwargs.pop('child', copy.deepcopy(self.child))
|
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.'
|
assert not inspect.isclass(self.child), '`child` has not been instantiated.'
|
||||||
super(ListField, self).__init__(*args, **kwargs)
|
super(ListField, self).__init__(*args, **kwargs)
|
||||||
self.child.bind(field_name='', parent=self)
|
self.child.bind(field_name='', parent=self)
|
||||||
|
@ -1170,6 +1182,49 @@ class ListField(Field):
|
||||||
return [self.child.to_representation(item) for item in data]
|
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...
|
# Miscellaneous field types...
|
||||||
|
|
||||||
class ReadOnlyField(Field):
|
class ReadOnlyField(Field):
|
||||||
|
|
|
@ -14,7 +14,7 @@ from __future__ import unicode_literals
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.db.models.fields import FieldDoesNotExist, Field as DjangoField
|
from django.db.models.fields import FieldDoesNotExist, Field as DjangoField
|
||||||
from django.utils.translation import ugettext_lazy as _
|
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 import model_meta
|
||||||
from rest_framework.utils.field_mapping import (
|
from rest_framework.utils.field_mapping import (
|
||||||
get_url_kwargs, get_field_kwargs,
|
get_url_kwargs, get_field_kwargs,
|
||||||
|
@ -1137,6 +1137,12 @@ class ModelSerializer(Serializer):
|
||||||
if hasattr(models, 'UUIDField'):
|
if hasattr(models, 'UUIDField'):
|
||||||
ModelSerializer._field_mapping[models.UUIDField] = 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):
|
class HyperlinkedModelSerializer(ModelSerializer):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -1047,7 +1047,7 @@ class TestValidImageField(FieldValues):
|
||||||
|
|
||||||
class TestListField(FieldValues):
|
class TestListField(FieldValues):
|
||||||
"""
|
"""
|
||||||
Values for `ListField`.
|
Values for `ListField` with IntegerField as child.
|
||||||
"""
|
"""
|
||||||
valid_inputs = [
|
valid_inputs = [
|
||||||
([1, 2, 3], [1, 2, 3]),
|
([1, 2, 3], [1, 2, 3]),
|
||||||
|
@ -1064,6 +1064,55 @@ class TestListField(FieldValues):
|
||||||
field = serializers.ListField(child=serializers.IntegerField())
|
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.
|
# Tests for FieldField.
|
||||||
# ---------------------
|
# ---------------------
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user