Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
D
django-rest-framework
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
edx
django-rest-framework
Commits
ed541864
Commit
ed541864
authored
Nov 06, 2014
by
Tom Christie
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Support for bulk create. Closes #1965.
parent
73daf407
Show whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
173 additions
and
130 deletions
+173
-130
rest_framework/fields.py
+1
-1
rest_framework/serializers.py
+51
-8
tests/test_fields.py
+1
-1
tests/test_serializer_bulk_update.py
+120
-120
No files found.
rest_framework/fields.py
View file @
ed541864
...
...
@@ -943,7 +943,7 @@ class ChoiceField(Field):
class
MultipleChoiceField
(
ChoiceField
):
default_error_messages
=
{
'invalid_choice'
:
_
(
'`{input}` is not a valid choice.'
),
'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}`
.
'
)
}
default_empty_html
=
[]
...
...
rest_framework/serializers.py
View file @
ed541864
...
...
@@ -90,10 +90,8 @@ class BaseSerializer(Field):
raise
NotImplementedError
(
'`create()` must be implemented.'
)
def
save
(
self
,
**
kwargs
):
validated_data
=
self
.
validated_data
if
kwargs
:
validated_data
=
dict
(
list
(
validated_data
.
items
())
+
list
(
self
.
validated_data
.
items
())
+
list
(
kwargs
.
items
())
)
...
...
@@ -210,9 +208,9 @@ class BoundField(object):
class
NestedBoundField
(
BoundField
):
"""
This
BoundField
additionally implements __iter__ and __getitem__
This
`BoundField`
additionally implements __iter__ and __getitem__
in order to support nested bound fields. This class is the type of
BoundField
that is used for serializer fields.
`BoundField`
that is used for serializer fields.
"""
def
__iter__
(
self
):
for
field
in
self
.
fields
.
values
():
...
...
@@ -460,6 +458,10 @@ class ListSerializer(BaseSerializer):
child
=
None
many
=
True
default_error_messages
=
{
'not_a_list'
:
_
(
'Expected a list of items but got type `{input_type}`.'
)
}
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.'
...
...
@@ -485,7 +487,31 @@ class ListSerializer(BaseSerializer):
"""
if
html
.
is_html_input
(
data
):
data
=
html
.
parse_html_list
(
data
)
return
[
self
.
child
.
run_validation
(
item
)
for
item
in
data
]
if
not
isinstance
(
data
,
list
):
message
=
self
.
error_messages
[
'not_a_list'
]
.
format
(
input_type
=
type
(
data
)
.
__name__
)
raise
ValidationError
({
api_settings
.
NON_FIELD_ERRORS_KEY
:
[
message
]
})
ret
=
[]
errors
=
ReturnList
(
serializer
=
self
)
for
item
in
data
:
try
:
validated
=
self
.
child
.
run_validation
(
item
)
except
ValidationError
,
exc
:
errors
.
append
(
exc
.
detail
)
else
:
ret
.
append
(
validated
)
errors
.
append
({})
if
any
(
errors
):
raise
ValidationError
(
errors
)
return
ret
def
to_representation
(
self
,
data
):
"""
...
...
@@ -497,8 +523,25 @@ class ListSerializer(BaseSerializer):
serializer
=
self
)
def
create
(
self
,
attrs_list
):
return
[
self
.
child
.
create
(
attrs
)
for
attrs
in
attrs_list
]
def
save
(
self
,
**
kwargs
):
assert
self
.
instance
is
None
,
(
"Serializers do not support multiple update by default, because "
"it would be unclear how to deal with insertions, updates and "
"deletions. If you need to support multiple update, use a "
"`ListSerializer` class and override `.save()` so you can specify "
"the behavior exactly."
)
validated_data
=
[
dict
(
list
(
attrs
.
items
())
+
list
(
kwargs
.
items
()))
for
attrs
in
self
.
validated_data
]
self
.
instance
=
[
self
.
child
.
create
(
attrs
)
for
attrs
in
validated_data
]
return
self
.
instance
def
__repr__
(
self
):
return
representation
.
list_repr
(
self
,
indent
=
1
)
...
...
tests/test_fields.py
View file @
ed541864
...
...
@@ -859,7 +859,7 @@ class TestMultipleChoiceField(FieldValues):
(
'aircon'
,
'manual'
):
set
([
'aircon'
,
'manual'
]),
}
invalid_inputs
=
{
'abc'
:
[
'Expected a list of items but got type `str`'
],
'abc'
:
[
'Expected a list of items but got type `str`
.
'
],
(
'aircon'
,
'incorrect'
):
[
'`incorrect` is not a valid choice.'
]
}
outputs
=
[
...
...
tests/test_serializer_bulk_update.py
View file @
ed541864
#
"""
#
Tests to cover bulk create and update using serializers.
#
"""
#
from __future__ import unicode_literals
#
from django.test import TestCase
#
from rest_framework import serializers
#
class BulkCreateSerializerTests(TestCase):
#
"""
#
Creating multiple instances using serializers.
#
"""
#
def setUp(self):
#
class BookSerializer(serializers.Serializer):
#
id = serializers.IntegerField()
#
title = serializers.CharField(max_length=100)
#
author = serializers.CharField(max_length=100)
#
self.BookSerializer = BookSerializer
#
def test_bulk_create_success(self):
#
"""
#
Correct bulk update serialization should return the input data.
#
"""
#
data = [
#
{
#
'id': 0,
#
'title': 'The electric kool-aid acid test',
#
'author': 'Tom Wolfe'
#
}, {
#
'id': 1,
#
'title': 'If this is a man',
#
'author': 'Primo Levi'
#
}, {
#
'id': 2,
#
'title': 'The wind-up bird chronicle',
#
'author': 'Haruki Murakami'
#
}
#
]
#
serializer = self.BookSerializer(data=data, many=True)
#
self.assertEqual(serializer.is_valid(), True)
# self.assertEqual(serializer.object
, data)
#
def test_bulk_create_errors(self):
#
"""
# Correct bulk update serialization should return the input data
.
#
"""
#
data = [
#
{
#
'id': 0,
#
'title': 'The electric kool-aid acid test',
#
'author': 'Tom Wolfe'
#
}, {
#
'id': 1,
#
'title': 'If this is a man',
#
'author': 'Primo Levi'
#
}, {
#
'id': 'foo',
#
'title': 'The wind-up bird chronicle',
#
'author': 'Haruki Murakami'
#
}
#
]
#
expected_errors = [
#
{},
#
{},
# {'id': ['Enter a whole number
.']}
#
]
#
serializer = self.BookSerializer(data=data, many=True)
#
self.assertEqual(serializer.is_valid(), False)
#
self.assertEqual(serializer.errors, expected_errors)
#
def test_invalid_list_datatype(self):
#
"""
#
Data containing list of incorrect data type should return errors.
#
"""
#
data = ['foo', 'bar', 'baz']
#
serializer = self.BookSerializer(data=data, many=True)
#
self.assertEqual(serializer.is_valid(), False)
#
expected_errors = [
# {'non_field_errors': ['Invalid data
']},
# {'non_field_errors': ['Invalid data
']},
# {'non_field_errors': ['Invalid data
']}
#
]
#
self.assertEqual(serializer.errors, expected_errors)
#
def test_invalid_single_datatype(self):
#
"""
#
Data containing a single incorrect data type should return errors.
#
"""
#
data = 123
#
serializer = self.BookSerializer(data=data, many=True)
#
self.assertEqual(serializer.is_valid(), False)
# expected_errors = {'non_field_errors': ['Expected a list of items
.']}
#
self.assertEqual(serializer.errors, expected_errors)
#
def test_invalid_single_object(self):
#
"""
#
Data containing only a single object, instead of a list of objects
#
should return errors.
#
"""
#
data = {
#
'id': 0,
#
'title': 'The electric kool-aid acid test',
#
'author': 'Tom Wolfe'
#
}
#
serializer = self.BookSerializer(data=data, many=True)
#
self.assertEqual(serializer.is_valid(), False)
# expected_errors = {'non_field_errors': ['Expected a list of items
.']}
#
self.assertEqual(serializer.errors, expected_errors)
"""
Tests to cover bulk create and update using serializers.
"""
from
__future__
import
unicode_literals
from
django.test
import
TestCase
from
rest_framework
import
serializers
class
BulkCreateSerializerTests
(
TestCase
):
"""
Creating multiple instances using serializers.
"""
def
setUp
(
self
):
class
BookSerializer
(
serializers
.
Serializer
):
id
=
serializers
.
IntegerField
()
title
=
serializers
.
CharField
(
max_length
=
100
)
author
=
serializers
.
CharField
(
max_length
=
100
)
self
.
BookSerializer
=
BookSerializer
def
test_bulk_create_success
(
self
):
"""
Correct bulk update serialization should return the input data.
"""
data
=
[
{
'id'
:
0
,
'title'
:
'The electric kool-aid acid test'
,
'author'
:
'Tom Wolfe'
},
{
'id'
:
1
,
'title'
:
'If this is a man'
,
'author'
:
'Primo Levi'
},
{
'id'
:
2
,
'title'
:
'The wind-up bird chronicle'
,
'author'
:
'Haruki Murakami'
}
]
serializer
=
self
.
BookSerializer
(
data
=
data
,
many
=
True
)
self
.
assertEqual
(
serializer
.
is_valid
(),
True
)
self
.
assertEqual
(
serializer
.
validated_data
,
data
)
def
test_bulk_create_errors
(
self
):
"""
Incorrect bulk create serialization should return errors
.
"""
data
=
[
{
'id'
:
0
,
'title'
:
'The electric kool-aid acid test'
,
'author'
:
'Tom Wolfe'
},
{
'id'
:
1
,
'title'
:
'If this is a man'
,
'author'
:
'Primo Levi'
},
{
'id'
:
'foo'
,
'title'
:
'The wind-up bird chronicle'
,
'author'
:
'Haruki Murakami'
}
]
expected_errors
=
[
{},
{},
{
'id'
:
[
'A valid integer is required
.'
]}
]
serializer
=
self
.
BookSerializer
(
data
=
data
,
many
=
True
)
self
.
assertEqual
(
serializer
.
is_valid
(),
False
)
self
.
assertEqual
(
serializer
.
errors
,
expected_errors
)
def
test_invalid_list_datatype
(
self
):
"""
Data containing list of incorrect data type should return errors.
"""
data
=
[
'foo'
,
'bar'
,
'baz'
]
serializer
=
self
.
BookSerializer
(
data
=
data
,
many
=
True
)
self
.
assertEqual
(
serializer
.
is_valid
(),
False
)
expected_errors
=
[
{
'non_field_errors'
:
[
'Invalid data. Expected a dictionary, but got unicode.
'
]},
{
'non_field_errors'
:
[
'Invalid data. Expected a dictionary, but got unicode.
'
]},
{
'non_field_errors'
:
[
'Invalid data. Expected a dictionary, but got unicode.
'
]}
]
self
.
assertEqual
(
serializer
.
errors
,
expected_errors
)
def
test_invalid_single_datatype
(
self
):
"""
Data containing a single incorrect data type should return errors.
"""
data
=
123
serializer
=
self
.
BookSerializer
(
data
=
data
,
many
=
True
)
self
.
assertEqual
(
serializer
.
is_valid
(),
False
)
expected_errors
=
{
'non_field_errors'
:
[
'Expected a list of items but got type `int`
.'
]}
self
.
assertEqual
(
serializer
.
errors
,
expected_errors
)
def
test_invalid_single_object
(
self
):
"""
Data containing only a single object, instead of a list of objects
should return errors.
"""
data
=
{
'id'
:
0
,
'title'
:
'The electric kool-aid acid test'
,
'author'
:
'Tom Wolfe'
}
serializer
=
self
.
BookSerializer
(
data
=
data
,
many
=
True
)
self
.
assertEqual
(
serializer
.
is_valid
(),
False
)
expected_errors
=
{
'non_field_errors'
:
[
'Expected a list of items but got type `dict`
.'
]}
self
.
assertEqual
(
serializer
.
errors
,
expected_errors
)
# class BulkUpdateSerializerTests(TestCase):
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment