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):
|
||||
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 = []
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 = [
|
||||
|
|
|
@ -1,123 +1,123 @@
|
|||
# """
|
||||
# Tests to cover bulk create and update using serializers.
|
||||
# """
|
||||
# from __future__ import unicode_literals
|
||||
# from django.test import TestCase
|
||||
# from rest_framework import serializers
|
||||
"""
|
||||
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.
|
||||
# """
|
||||
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)
|
||||
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
|
||||
self.BookSerializer = BookSerializer
|
||||
|
||||
# def test_bulk_create_success(self):
|
||||
# """
|
||||
# Correct bulk update serialization should return the input data.
|
||||
# """
|
||||
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'
|
||||
# }
|
||||
# ]
|
||||
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)
|
||||
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):
|
||||
# """
|
||||
# Correct bulk update serialization should return the input 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': ['Enter a whole number.']}
|
||||
# ]
|
||||
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)
|
||||
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)
|
||||
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']}
|
||||
# ]
|
||||
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)
|
||||
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)
|
||||
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.']}
|
||||
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):
|
||||
# """
|
||||
# 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)
|
||||
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.']}
|
||||
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):
|
||||
|
|
Loading…
Reference in New Issue
Block a user