mirror of
https://github.com/encode/django-rest-framework.git
synced 2025-01-23 15:54:16 +03:00
Support for bulk create. Closes #1965.
This commit is contained in:
parent
73daf40715
commit
ed541864e6
|
@ -943,7 +943,7 @@ class ChoiceField(Field):
|
||||||
class MultipleChoiceField(ChoiceField):
|
class MultipleChoiceField(ChoiceField):
|
||||||
default_error_messages = {
|
default_error_messages = {
|
||||||
'invalid_choice': _('`{input}` is not a valid choice.'),
|
'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 = []
|
default_empty_html = []
|
||||||
|
|
||||||
|
|
|
@ -90,12 +90,10 @@ class BaseSerializer(Field):
|
||||||
raise NotImplementedError('`create()` must be implemented.')
|
raise NotImplementedError('`create()` must be implemented.')
|
||||||
|
|
||||||
def save(self, **kwargs):
|
def save(self, **kwargs):
|
||||||
validated_data = self.validated_data
|
validated_data = dict(
|
||||||
if kwargs:
|
list(self.validated_data.items()) +
|
||||||
validated_data = dict(
|
list(kwargs.items())
|
||||||
list(validated_data.items()) +
|
)
|
||||||
list(kwargs.items())
|
|
||||||
)
|
|
||||||
|
|
||||||
if self.instance is not None:
|
if self.instance is not None:
|
||||||
self.instance = self.update(self.instance, validated_data)
|
self.instance = self.update(self.instance, validated_data)
|
||||||
|
@ -210,9 +208,9 @@ class BoundField(object):
|
||||||
|
|
||||||
class NestedBoundField(BoundField):
|
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
|
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):
|
def __iter__(self):
|
||||||
for field in self.fields.values():
|
for field in self.fields.values():
|
||||||
|
@ -460,6 +458,10 @@ class ListSerializer(BaseSerializer):
|
||||||
child = None
|
child = None
|
||||||
many = True
|
many = True
|
||||||
|
|
||||||
|
default_error_messages = {
|
||||||
|
'not_a_list': _('Expected a list of items but got type `{input_type}`.')
|
||||||
|
}
|
||||||
|
|
||||||
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 self.child is not None, '`child` is a required argument.'
|
||||||
|
@ -485,7 +487,31 @@ class ListSerializer(BaseSerializer):
|
||||||
"""
|
"""
|
||||||
if html.is_html_input(data):
|
if html.is_html_input(data):
|
||||||
data = html.parse_html_list(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):
|
def to_representation(self, data):
|
||||||
"""
|
"""
|
||||||
|
@ -497,8 +523,25 @@ class ListSerializer(BaseSerializer):
|
||||||
serializer=self
|
serializer=self
|
||||||
)
|
)
|
||||||
|
|
||||||
def create(self, attrs_list):
|
def save(self, **kwargs):
|
||||||
return [self.child.create(attrs) for attrs in attrs_list]
|
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):
|
def __repr__(self):
|
||||||
return representation.list_repr(self, indent=1)
|
return representation.list_repr(self, indent=1)
|
||||||
|
|
|
@ -859,7 +859,7 @@ class TestMultipleChoiceField(FieldValues):
|
||||||
('aircon', 'manual'): set(['aircon', 'manual']),
|
('aircon', 'manual'): set(['aircon', 'manual']),
|
||||||
}
|
}
|
||||||
invalid_inputs = {
|
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.']
|
('aircon', 'incorrect'): ['`incorrect` is not a valid choice.']
|
||||||
}
|
}
|
||||||
outputs = [
|
outputs = [
|
||||||
|
|
|
@ -1,123 +1,123 @@
|
||||||
# """
|
"""
|
||||||
# Tests to cover bulk create and update using serializers.
|
Tests to cover bulk create and update using serializers.
|
||||||
# """
|
"""
|
||||||
# from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
# from django.test import TestCase
|
from django.test import TestCase
|
||||||
# from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
|
|
||||||
|
|
||||||
# class BulkCreateSerializerTests(TestCase):
|
class BulkCreateSerializerTests(TestCase):
|
||||||
# """
|
"""
|
||||||
# Creating multiple instances using serializers.
|
Creating multiple instances using serializers.
|
||||||
# """
|
"""
|
||||||
|
|
||||||
# def setUp(self):
|
def setUp(self):
|
||||||
# class BookSerializer(serializers.Serializer):
|
class BookSerializer(serializers.Serializer):
|
||||||
# id = serializers.IntegerField()
|
id = serializers.IntegerField()
|
||||||
# title = serializers.CharField(max_length=100)
|
title = serializers.CharField(max_length=100)
|
||||||
# author = serializers.CharField(max_length=100)
|
author = serializers.CharField(max_length=100)
|
||||||
|
|
||||||
# self.BookSerializer = BookSerializer
|
self.BookSerializer = BookSerializer
|
||||||
|
|
||||||
# def test_bulk_create_success(self):
|
def test_bulk_create_success(self):
|
||||||
# """
|
"""
|
||||||
# Correct bulk update serialization should return the input data.
|
Correct bulk update serialization should return the input data.
|
||||||
# """
|
"""
|
||||||
|
|
||||||
# data = [
|
data = [
|
||||||
# {
|
{
|
||||||
# 'id': 0,
|
'id': 0,
|
||||||
# 'title': 'The electric kool-aid acid test',
|
'title': 'The electric kool-aid acid test',
|
||||||
# 'author': 'Tom Wolfe'
|
'author': 'Tom Wolfe'
|
||||||
# }, {
|
}, {
|
||||||
# 'id': 1,
|
'id': 1,
|
||||||
# 'title': 'If this is a man',
|
'title': 'If this is a man',
|
||||||
# 'author': 'Primo Levi'
|
'author': 'Primo Levi'
|
||||||
# }, {
|
}, {
|
||||||
# 'id': 2,
|
'id': 2,
|
||||||
# 'title': 'The wind-up bird chronicle',
|
'title': 'The wind-up bird chronicle',
|
||||||
# 'author': 'Haruki Murakami'
|
'author': 'Haruki Murakami'
|
||||||
# }
|
}
|
||||||
# ]
|
]
|
||||||
|
|
||||||
# serializer = self.BookSerializer(data=data, many=True)
|
serializer = self.BookSerializer(data=data, many=True)
|
||||||
# self.assertEqual(serializer.is_valid(), True)
|
self.assertEqual(serializer.is_valid(), True)
|
||||||
# self.assertEqual(serializer.object, data)
|
self.assertEqual(serializer.validated_data, data)
|
||||||
|
|
||||||
# def test_bulk_create_errors(self):
|
def test_bulk_create_errors(self):
|
||||||
# """
|
"""
|
||||||
# Correct bulk update serialization should return the input data.
|
Incorrect bulk create serialization should return errors.
|
||||||
# """
|
"""
|
||||||
|
|
||||||
# data = [
|
data = [
|
||||||
# {
|
{
|
||||||
# 'id': 0,
|
'id': 0,
|
||||||
# 'title': 'The electric kool-aid acid test',
|
'title': 'The electric kool-aid acid test',
|
||||||
# 'author': 'Tom Wolfe'
|
'author': 'Tom Wolfe'
|
||||||
# }, {
|
}, {
|
||||||
# 'id': 1,
|
'id': 1,
|
||||||
# 'title': 'If this is a man',
|
'title': 'If this is a man',
|
||||||
# 'author': 'Primo Levi'
|
'author': 'Primo Levi'
|
||||||
# }, {
|
}, {
|
||||||
# 'id': 'foo',
|
'id': 'foo',
|
||||||
# 'title': 'The wind-up bird chronicle',
|
'title': 'The wind-up bird chronicle',
|
||||||
# 'author': 'Haruki Murakami'
|
'author': 'Haruki Murakami'
|
||||||
# }
|
}
|
||||||
# ]
|
]
|
||||||
# expected_errors = [
|
expected_errors = [
|
||||||
# {},
|
{},
|
||||||
# {},
|
{},
|
||||||
# {'id': ['Enter a whole number.']}
|
{'id': ['A valid integer is required.']}
|
||||||
# ]
|
]
|
||||||
|
|
||||||
# serializer = self.BookSerializer(data=data, many=True)
|
serializer = self.BookSerializer(data=data, many=True)
|
||||||
# self.assertEqual(serializer.is_valid(), False)
|
self.assertEqual(serializer.is_valid(), False)
|
||||||
# self.assertEqual(serializer.errors, expected_errors)
|
self.assertEqual(serializer.errors, expected_errors)
|
||||||
|
|
||||||
# def test_invalid_list_datatype(self):
|
def test_invalid_list_datatype(self):
|
||||||
# """
|
"""
|
||||||
# Data containing list of incorrect data type should return errors.
|
Data containing list of incorrect data type should return errors.
|
||||||
# """
|
"""
|
||||||
# data = ['foo', 'bar', 'baz']
|
data = ['foo', 'bar', 'baz']
|
||||||
# serializer = self.BookSerializer(data=data, many=True)
|
serializer = self.BookSerializer(data=data, many=True)
|
||||||
# self.assertEqual(serializer.is_valid(), False)
|
self.assertEqual(serializer.is_valid(), False)
|
||||||
|
|
||||||
# expected_errors = [
|
expected_errors = [
|
||||||
# {'non_field_errors': ['Invalid data']},
|
{'non_field_errors': ['Invalid data. Expected a dictionary, but got unicode.']},
|
||||||
# {'non_field_errors': ['Invalid data']},
|
{'non_field_errors': ['Invalid data. Expected a dictionary, but got unicode.']},
|
||||||
# {'non_field_errors': ['Invalid data']}
|
{'non_field_errors': ['Invalid data. Expected a dictionary, but got unicode.']}
|
||||||
# ]
|
]
|
||||||
|
|
||||||
# self.assertEqual(serializer.errors, expected_errors)
|
self.assertEqual(serializer.errors, expected_errors)
|
||||||
|
|
||||||
# def test_invalid_single_datatype(self):
|
def test_invalid_single_datatype(self):
|
||||||
# """
|
"""
|
||||||
# Data containing a single incorrect data type should return errors.
|
Data containing a single incorrect data type should return errors.
|
||||||
# """
|
"""
|
||||||
# data = 123
|
data = 123
|
||||||
# serializer = self.BookSerializer(data=data, many=True)
|
serializer = self.BookSerializer(data=data, many=True)
|
||||||
# self.assertEqual(serializer.is_valid(), False)
|
self.assertEqual(serializer.is_valid(), False)
|
||||||
|
|
||||||
# expected_errors = {'non_field_errors': ['Expected a list of items.']}
|
expected_errors = {'non_field_errors': ['Expected a list of items but got type `int`.']}
|
||||||
|
|
||||||
# self.assertEqual(serializer.errors, expected_errors)
|
self.assertEqual(serializer.errors, expected_errors)
|
||||||
|
|
||||||
# def test_invalid_single_object(self):
|
def test_invalid_single_object(self):
|
||||||
# """
|
"""
|
||||||
# Data containing only a single object, instead of a list of objects
|
Data containing only a single object, instead of a list of objects
|
||||||
# should return errors.
|
should return errors.
|
||||||
# """
|
"""
|
||||||
# data = {
|
data = {
|
||||||
# 'id': 0,
|
'id': 0,
|
||||||
# 'title': 'The electric kool-aid acid test',
|
'title': 'The electric kool-aid acid test',
|
||||||
# 'author': 'Tom Wolfe'
|
'author': 'Tom Wolfe'
|
||||||
# }
|
}
|
||||||
# serializer = self.BookSerializer(data=data, many=True)
|
serializer = self.BookSerializer(data=data, many=True)
|
||||||
# self.assertEqual(serializer.is_valid(), False)
|
self.assertEqual(serializer.is_valid(), False)
|
||||||
|
|
||||||
# expected_errors = {'non_field_errors': ['Expected a list of items.']}
|
expected_errors = {'non_field_errors': ['Expected a list of items but got type `dict`.']}
|
||||||
|
|
||||||
# self.assertEqual(serializer.errors, expected_errors)
|
self.assertEqual(serializer.errors, expected_errors)
|
||||||
|
|
||||||
|
|
||||||
# class BulkUpdateSerializerTests(TestCase):
|
# class BulkUpdateSerializerTests(TestCase):
|
||||||
|
|
Loading…
Reference in New Issue
Block a user