aboutsummaryrefslogtreecommitdiffstats
path: root/rest_framework
diff options
context:
space:
mode:
authorBrickXu2014-12-05 13:30:56 +0800
committerBrickXu2014-12-05 13:30:56 +0800
commit4042180392fb7809d1c8d2f6ca0bc6e18c114e6c (patch)
tree5fab017db281948eecf54d9dd8d5f0a8b323fa77 /rest_framework
parent81870b6e1a7b0c3c149d4bfba0e20924ebf1b187 (diff)
parente8cbf41bd9066a21bf102bb60fbb42b4b15e05f6 (diff)
downloaddjango-rest-framework-4042180392fb7809d1c8d2f6ca0bc6e18c114e6c.tar.bz2
Merge pull request #3 from tomchristie/master
Merge upstream
Diffstat (limited to 'rest_framework')
-rw-r--r--rest_framework/parsers.py13
-rw-r--r--rest_framework/renderers.py4
-rw-r--r--rest_framework/serializers.py53
-rw-r--r--rest_framework/utils/serializer_helpers.py14
-rw-r--r--rest_framework/viewsets.py6
5 files changed, 62 insertions, 28 deletions
diff --git a/rest_framework/parsers.py b/rest_framework/parsers.py
index ccb82f03..d229abec 100644
--- a/rest_framework/parsers.py
+++ b/rest_framework/parsers.py
@@ -256,23 +256,24 @@ class FileUploadParser(BaseParser):
chunks = ChunkIter(stream, chunk_size)
counters = [0] * len(upload_handlers)
- for handler in upload_handlers:
+ for index, handler in enumerate(upload_handlers):
try:
handler.new_file(None, filename, content_type,
content_length, encoding)
except StopFutureHandlers:
+ upload_handlers = upload_handlers[:index + 1]
break
for chunk in chunks:
- for i, handler in enumerate(upload_handlers):
+ for index, handler in enumerate(upload_handlers):
chunk_length = len(chunk)
- chunk = handler.receive_data_chunk(chunk, counters[i])
- counters[i] += chunk_length
+ chunk = handler.receive_data_chunk(chunk, counters[index])
+ counters[index] += chunk_length
if chunk is None:
break
- for i, handler in enumerate(upload_handlers):
- file_obj = handler.file_complete(counters[i])
+ for index, handler in enumerate(upload_handlers):
+ file_obj = handler.file_complete(counters[index])
if file_obj:
return DataAndFiles(None, {'file': file_obj})
raise ParseError("FileUpload parse error - "
diff --git a/rest_framework/renderers.py b/rest_framework/renderers.py
index e87d16d0..31d3ef5f 100644
--- a/rest_framework/renderers.py
+++ b/rest_framework/renderers.py
@@ -374,6 +374,10 @@ class HTMLFormRenderer(BaseRenderer):
'base_template': 'input.html',
'input_type': 'time'
},
+ serializers.FileField: {
+ 'base_template': 'input.html',
+ 'input_type': 'file'
+ },
serializers.BooleanField: {
'base_template': 'checkbox.html'
},
diff --git a/rest_framework/serializers.py b/rest_framework/serializers.py
index 0d0a4d9a..af8aeb48 100644
--- a/rest_framework/serializers.py
+++ b/rest_framework/serializers.py
@@ -127,6 +127,14 @@ class BaseSerializer(Field):
(self.__class__.__module__, self.__class__.__name__)
)
+ assert hasattr(self, '_errors'), (
+ 'You must call `.is_valid()` before calling `.save()`.'
+ )
+
+ assert not self.errors, (
+ 'You cannot call `.save()` on a serializer with invalid data.'
+ )
+
validated_data = dict(
list(self.validated_data.items()) +
list(kwargs.items())
@@ -600,20 +608,20 @@ class ModelSerializer(Serializer):
})
_related_class = PrimaryKeyRelatedField
- def create(self, validated_attrs):
+ def create(self, validated_data):
"""
We have a bit of extra checking around this in order to provide
descriptive messages when something goes wrong, but this method is
essentially just:
- return ExampleModel.objects.create(**validated_attrs)
+ return ExampleModel.objects.create(**validated_data)
If there are many to many fields present on the instance then they
cannot be set until the model is instantiated, in which case the
implementation is like so:
- example_relationship = validated_attrs.pop('example_relationship')
- instance = ExampleModel.objects.create(**validated_attrs)
+ example_relationship = validated_data.pop('example_relationship')
+ instance = ExampleModel.objects.create(**validated_data)
instance.example_relationship = example_relationship
return instance
@@ -625,8 +633,8 @@ class ModelSerializer(Serializer):
# If we don't do this explicitly they'd likely get a confusing
# error at the point of calling `Model.objects.create()`.
assert not any(
- isinstance(field, BaseSerializer) and not field.read_only
- for field in self.fields.values()
+ isinstance(field, BaseSerializer) and (key in validated_attrs)
+ for key, field in self.fields.items()
), (
'The `.create()` method does not suport nested writable fields '
'by default. Write an explicit `.create()` method for serializer '
@@ -636,16 +644,33 @@ class ModelSerializer(Serializer):
ModelClass = self.Meta.model
- # Remove many-to-many relationships from validated_attrs.
+ # Remove many-to-many relationships from validated_data.
# They are not valid arguments to the default `.create()` method,
# as they require that the instance has already been saved.
info = model_meta.get_field_info(ModelClass)
many_to_many = {}
for field_name, relation_info in info.relations.items():
- if relation_info.to_many and (field_name in validated_attrs):
- many_to_many[field_name] = validated_attrs.pop(field_name)
+ if relation_info.to_many and (field_name in validated_data):
+ many_to_many[field_name] = validated_data.pop(field_name)
- instance = ModelClass.objects.create(**validated_attrs)
+ try:
+ instance = ModelClass.objects.create(**validated_data)
+ except TypeError as exc:
+ msg = (
+ 'Got a `TypeError` when calling `%s.objects.create()`. '
+ 'This may be because you have a writable field on the '
+ 'serializer class that is not a valid argument to '
+ '`%s.objects.create()`. You may need to make the field '
+ 'read-only, or override the %s.create() method to handle '
+ 'this correctly.\nOriginal exception text was: %s.' %
+ (
+ ModelClass.__name__,
+ ModelClass.__name__,
+ self.__class__.__name__,
+ exc
+ )
+ )
+ raise TypeError(msg)
# Save many-to-many relationships after the instance is created.
if many_to_many:
@@ -654,10 +679,10 @@ class ModelSerializer(Serializer):
return instance
- def update(self, instance, validated_attrs):
+ def update(self, instance, validated_data):
assert not any(
- isinstance(field, BaseSerializer) and not field.read_only
- for field in self.fields.values()
+ isinstance(field, BaseSerializer) and (key in validated_attrs)
+ for key, field in self.fields.items()
), (
'The `.update()` method does not suport nested writable fields '
'by default. Write an explicit `.update()` method for serializer '
@@ -665,7 +690,7 @@ class ModelSerializer(Serializer):
(self.__class__.__module__, self.__class__.__name__)
)
- for attr, value in validated_attrs.items():
+ for attr, value in validated_data.items():
setattr(instance, attr, value)
instance.save()
return instance
diff --git a/rest_framework/utils/serializer_helpers.py b/rest_framework/utils/serializer_helpers.py
index 92d19857..277cf649 100644
--- a/rest_framework/utils/serializer_helpers.py
+++ b/rest_framework/utils/serializer_helpers.py
@@ -1,3 +1,4 @@
+import collections
from rest_framework.compat import OrderedDict
@@ -70,7 +71,7 @@ class NestedBoundField(BoundField):
return BoundField(field, value, error, prefix=self.name + '.')
-class BindingDict(object):
+class BindingDict(collections.MutableMapping):
"""
This dict-like object is used to store fields on a serializer.
@@ -92,11 +93,8 @@ class BindingDict(object):
def __delitem__(self, key):
del self.fields[key]
- def items(self):
- return self.fields.items()
-
- def keys(self):
- return self.fields.keys()
+ def __iter__(self):
+ return iter(self.fields)
- def values(self):
- return self.fields.values()
+ def __len__(self):
+ return len(self.fields)
diff --git a/rest_framework/viewsets.py b/rest_framework/viewsets.py
index 84b4bd8d..70d14695 100644
--- a/rest_framework/viewsets.py
+++ b/rest_framework/viewsets.py
@@ -48,6 +48,12 @@ class ViewSetMixin(object):
# eg. 'List' or 'Instance'.
cls.suffix = None
+ # actions must not be empty
+ if not actions:
+ raise TypeError("The `actions` argument must be provided when "
+ "calling `.as_view()` on a ViewSet. For example "
+ "`.as_view({'get': 'list'})`")
+
# sanitize keyword arguments
for key in initkwargs:
if key in cls.http_method_names: