mirror of
https://github.com/encode/django-rest-framework.git
synced 2024-11-25 11:04:02 +03:00
Add HStoreField, postgres fields tests (#5654)
* Test postgres field mapping * Add HStoreField * Ensure 'HStoreField' child is a 'CharField' * Add HStoreField docs
This commit is contained in:
parent
d3f3c3d9c1
commit
2709de1310
|
@ -473,6 +473,16 @@ You can also use the declarative style, as with `ListField`. For example:
|
|||
class DocumentField(DictField):
|
||||
child = CharField()
|
||||
|
||||
## HStoreField
|
||||
|
||||
A preconfigured `DictField` that is compatible with Django's postgres `HStoreField`.
|
||||
|
||||
**Signature**: `HStoreField(child=<A_FIELD_INSTANCE>)`
|
||||
|
||||
- `child` - A field instance that is used for validating the values in the dictionary. The default child field accepts both empty strings and null values.
|
||||
|
||||
Note that the child field **must** be an instance of `CharField`, as the hstore extension stores values as strings.
|
||||
|
||||
## JSONField
|
||||
|
||||
A field class that validates that the incoming data structure consists of valid JSON primitives. In its alternate binary mode, it will represent and validate JSON-encoded binary strings.
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
# Optional packages which may be used with REST framework.
|
||||
pytz==2017.2
|
||||
psycopg2==2.7.3
|
||||
markdown==2.6.4
|
||||
django-guardian==1.4.9
|
||||
django-filter==1.1.0
|
||||
|
|
|
@ -1711,6 +1711,17 @@ class DictField(Field):
|
|||
raise ValidationError(errors)
|
||||
|
||||
|
||||
class HStoreField(DictField):
|
||||
child = CharField(allow_blank=True, allow_null=True)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(HStoreField, self).__init__(*args, **kwargs)
|
||||
assert isinstance(self.child, CharField), (
|
||||
"The `child` argument must be an instance of `CharField`, "
|
||||
"as the hstore extension stores values as strings."
|
||||
)
|
||||
|
||||
|
||||
class JSONField(Field):
|
||||
default_error_messages = {
|
||||
'invalid': _('Value must be valid JSON.')
|
||||
|
|
|
@ -54,9 +54,9 @@ from rest_framework.validators import (
|
|||
from rest_framework.fields import ( # NOQA # isort:skip
|
||||
BooleanField, CharField, ChoiceField, DateField, DateTimeField, DecimalField,
|
||||
DictField, DurationField, EmailField, Field, FileField, FilePathField, FloatField,
|
||||
HiddenField, IPAddressField, ImageField, IntegerField, JSONField, ListField,
|
||||
ModelField, MultipleChoiceField, NullBooleanField, ReadOnlyField, RegexField,
|
||||
SerializerMethodField, SlugField, TimeField, URLField, UUIDField,
|
||||
HiddenField, HStoreField, IPAddressField, ImageField, IntegerField, JSONField,
|
||||
ListField, ModelField, MultipleChoiceField, NullBooleanField, ReadOnlyField,
|
||||
RegexField, SerializerMethodField, SlugField, TimeField, URLField, UUIDField,
|
||||
)
|
||||
from rest_framework.relations import ( # NOQA # isort:skip
|
||||
HyperlinkedIdentityField, HyperlinkedRelatedField, ManyRelatedField,
|
||||
|
@ -1541,10 +1541,7 @@ if hasattr(models, 'IPAddressField'):
|
|||
ModelSerializer.serializer_field_mapping[models.IPAddressField] = IPAddressField
|
||||
|
||||
if postgres_fields:
|
||||
class CharMappingField(DictField):
|
||||
child = CharField(allow_blank=True)
|
||||
|
||||
ModelSerializer.serializer_field_mapping[postgres_fields.HStoreField] = CharMappingField
|
||||
ModelSerializer.serializer_field_mapping[postgres_fields.HStoreField] = HStoreField
|
||||
ModelSerializer.serializer_field_mapping[postgres_fields.ArrayField] = ListField
|
||||
ModelSerializer.serializer_field_mapping[postgres_fields.JSONField] = JSONField
|
||||
|
||||
|
|
|
@ -1933,6 +1933,49 @@ class TestUnvalidatedDictField(FieldValues):
|
|||
field = serializers.DictField()
|
||||
|
||||
|
||||
class TestHStoreField(FieldValues):
|
||||
"""
|
||||
Values for `ListField` with CharField as child.
|
||||
"""
|
||||
valid_inputs = [
|
||||
({'a': 1, 'b': '2', 3: 3}, {'a': '1', 'b': '2', '3': '3'}),
|
||||
({'a': 1, 'b': None}, {'a': '1', 'b': None}),
|
||||
]
|
||||
invalid_inputs = [
|
||||
('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.HStoreField()
|
||||
|
||||
def test_child_is_charfield(self):
|
||||
with pytest.raises(AssertionError) as exc_info:
|
||||
serializers.HStoreField(child=serializers.IntegerField())
|
||||
|
||||
assert str(exc_info.value) == (
|
||||
"The `child` argument must be an instance of `CharField`, "
|
||||
"as the hstore extension stores values as strings."
|
||||
)
|
||||
|
||||
def test_no_source_on_child(self):
|
||||
with pytest.raises(AssertionError) as exc_info:
|
||||
serializers.HStoreField(child=serializers.CharField(source='other'))
|
||||
|
||||
assert str(exc_info.value) == (
|
||||
"The `source` argument is not meaningful when applied to a `child=` field. "
|
||||
"Remove `source=` from the field declaration."
|
||||
)
|
||||
|
||||
def test_allow_null(self):
|
||||
"""
|
||||
If `allow_null=True` then `None` is a valid input.
|
||||
"""
|
||||
field = serializers.HStoreField(allow_null=True)
|
||||
output = field.run_validation(None)
|
||||
assert output is None
|
||||
|
||||
|
||||
class TestJSONField(FieldValues):
|
||||
"""
|
||||
Values for `JSONField`.
|
||||
|
|
|
@ -21,7 +21,7 @@ from django.test import TestCase
|
|||
from django.utils import six
|
||||
|
||||
from rest_framework import serializers
|
||||
from rest_framework.compat import unicode_repr
|
||||
from rest_framework.compat import postgres_fields, unicode_repr
|
||||
|
||||
|
||||
def dedent(blocktext):
|
||||
|
@ -379,6 +379,54 @@ class TestGenericIPAddressFieldValidation(TestCase):
|
|||
'{0}'.format(s.errors))
|
||||
|
||||
|
||||
@pytest.mark.skipUnless(postgres_fields, 'postgres is required')
|
||||
class TestPosgresFieldsMapping(TestCase):
|
||||
def test_hstore_field(self):
|
||||
class HStoreFieldModel(models.Model):
|
||||
hstore_field = postgres_fields.HStoreField()
|
||||
|
||||
class TestSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = HStoreFieldModel
|
||||
fields = ['hstore_field']
|
||||
|
||||
expected = dedent("""
|
||||
TestSerializer():
|
||||
hstore_field = HStoreField()
|
||||
""")
|
||||
self.assertEqual(unicode_repr(TestSerializer()), expected)
|
||||
|
||||
def test_array_field(self):
|
||||
class ArrayFieldModel(models.Model):
|
||||
array_field = postgres_fields.ArrayField(base_field=models.CharField())
|
||||
|
||||
class TestSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = ArrayFieldModel
|
||||
fields = ['array_field']
|
||||
|
||||
expected = dedent("""
|
||||
TestSerializer():
|
||||
array_field = ListField(child=CharField(label='Array field', validators=[<django.core.validators.MaxLengthValidator object>]))
|
||||
""")
|
||||
self.assertEqual(unicode_repr(TestSerializer()), expected)
|
||||
|
||||
def test_json_field(self):
|
||||
class JSONFieldModel(models.Model):
|
||||
json_field = postgres_fields.JSONField()
|
||||
|
||||
class TestSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = JSONFieldModel
|
||||
fields = ['json_field']
|
||||
|
||||
expected = dedent("""
|
||||
TestSerializer():
|
||||
json_field = JSONField(style={'base_template': 'textarea.html'})
|
||||
""")
|
||||
self.assertEqual(unicode_repr(TestSerializer()), expected)
|
||||
|
||||
|
||||
# Tests for relational field mappings.
|
||||
# ------------------------------------
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user