This commit is contained in:
pySilver 2014-08-18 14:55:44 +00:00
commit c8ac7f6092
5 changed files with 38 additions and 9 deletions

View File

@ -429,6 +429,11 @@ class ModelField(WritableField):
"type": self.model_field.get_internal_type() "type": self.model_field.get_internal_type()
} }
def validate(self, value):
super(ModelField, self).validate(value)
if value is None and not self.model_field.null:
raise ValidationError(self.error_messages['invalid'])
##### Typed Fields ##### ##### Typed Fields #####
@ -474,10 +479,7 @@ class CharField(WritableField):
self.validators.append(validators.MaxLengthValidator(max_length)) self.validators.append(validators.MaxLengthValidator(max_length))
def from_native(self, value): def from_native(self, value):
if isinstance(value, six.string_types): if value in validators.EMPTY_VALUES:
return value
if value is None:
return '' return ''
return smart_text(value) return smart_text(value)

View File

@ -33,7 +33,7 @@ class RelatedField(WritableField):
many_widget = widgets.SelectMultiple many_widget = widgets.SelectMultiple
form_field_class = forms.ChoiceField form_field_class = forms.ChoiceField
many_form_field_class = forms.MultipleChoiceField many_form_field_class = forms.MultipleChoiceField
null_values = (None, '', 'None') null_values = (None, '', 'None', [], (), {})
cache_choices = False cache_choices = False
empty_label = None empty_label = None
@ -182,7 +182,7 @@ class RelatedField(WritableField):
if value in self.null_values: if value in self.null_values:
if self.required: if self.required:
raise ValidationError(self.error_messages['required']) raise ValidationError(self.error_messages['required'])
into[(self.source or field_name)] = None into[(self.source or field_name)] = [] if self.many else None
elif self.many: elif self.many:
into[(self.source or field_name)] = [self.from_native(item) for item in value] into[(self.source or field_name)] = [self.from_native(item) for item in value]
else: else:

View File

@ -18,6 +18,7 @@ import types
from decimal import Decimal from decimal import Decimal
from django.contrib.contenttypes.generic import GenericForeignKey from django.contrib.contenttypes.generic import GenericForeignKey
from django.core.paginator import Page from django.core.paginator import Page
from django.core.exceptions import ObjectDoesNotExist
from django.db import models from django.db import models
from django.forms import widgets from django.forms import widgets
from django.utils.datastructures import SortedDict from django.utils.datastructures import SortedDict
@ -912,9 +913,19 @@ class ModelSerializer(Serializer):
for field_name, field in self.fields.items(): for field_name, field in self.fields.items():
field_name = field.source or field_name field_name = field.source or field_name
# PY3 compat problem with ManyToManyField & OneToOneField
# hasattr in PY2 catches all exceptions, but in PY3 it only looks
# for AttributeError
# @see: https://code.djangoproject.com/ticket/22839
try:
field_exists = hasattr(instance, field_name)
except (AttributeError, ValueError, ObjectDoesNotExist):
field_exists = False
if field_name in exclusions \ if field_name in exclusions \
and not field.read_only \ and not field.read_only \
and (field.required or hasattr(instance, field_name)) \ and (field.required or field_exists) \
and not isinstance(field, Serializer): and not isinstance(field, Serializer):
exclusions.remove(field_name) exclusions.remove(field_name)
return exclusions return exclusions

View File

@ -52,7 +52,7 @@ class CallableDefaultValueModel(RESTFrameworkModel):
class ManyToManyModel(RESTFrameworkModel): class ManyToManyModel(RESTFrameworkModel):
rel = models.ManyToManyField(Anchor, help_text='Some help text.') rel = models.ManyToManyField(Anchor, help_text='Some help text.', blank=True)
class ReadOnlyManyToManyModel(RESTFrameworkModel): class ReadOnlyManyToManyModel(RESTFrameworkModel):

View File

@ -3,6 +3,7 @@ General tests for relational fields.
""" """
from __future__ import unicode_literals from __future__ import unicode_literals
from django import get_version from django import get_version
from django.core.exceptions import ValidationError
from django.db import models from django.db import models
from django.test import TestCase from django.test import TestCase
from django.utils import unittest from django.utils import unittest
@ -43,12 +44,27 @@ class TestManyRelatedMixin(TestCase):
https://github.com/tomchristie/django-rest-framework/pull/632 https://github.com/tomchristie/django-rest-framework/pull/632
''' '''
field = serializers.RelatedField(many=True, read_only=False) field = serializers.RelatedField(many=True, read_only=False, required=False)
into = {} into = {}
field.field_from_native({}, None, 'field_name', into) field.field_from_native({}, None, 'field_name', into)
self.assertEqual(into['field_name'], []) self.assertEqual(into['field_name'], [])
def test_missing_required_many_to_many_related_field(self):
'''
Missing but required many-to-many relationship should cause
ValidationError
'''
field = serializers.RelatedField(many=True, read_only=False)
into = {}
self.assertRaises(
ValidationError,
field.field_from_native,
{}, None, 'field_name', into
)
self.assertDictEqual(into, {})
# Regression tests for #694 (`source` attribute on related fields) # Regression tests for #694 (`source` attribute on related fields)