serializers.py 2.1 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364
  1. from __future__ import print_function, unicode_literals
  2. from rest_framework.exceptions import ValidationError
  3. from rest_framework.serializers import ListSerializer
  4. __all__ = [
  5. 'BulkListSerializer',
  6. 'BulkSerializerMixin',
  7. ]
  8. class BulkSerializerMixin(object):
  9. def to_internal_value(self, data):
  10. ret = super(BulkSerializerMixin, self).to_internal_value(data)
  11. id_attr = getattr(self.Meta, 'update_lookup_field', 'id')
  12. request_method = getattr(getattr(self.context.get('view'), 'request'), 'method', '')
  13. # add update_lookup_field field back to validated data
  14. # since super by default strips out read-only fields
  15. # hence id will no longer be present in validated_data
  16. if all((isinstance(self.root, BulkListSerializer),
  17. id_attr,
  18. request_method in ('PUT', 'PATCH'))):
  19. id_field = self.fields[id_attr]
  20. id_value = id_field.get_value(data)
  21. ret[id_attr] = id_value
  22. return ret
  23. class BulkListSerializer(ListSerializer):
  24. update_lookup_field = 'id'
  25. def update(self, queryset, all_validated_data):
  26. id_attr = getattr(self.child.Meta, 'update_lookup_field', 'id')
  27. all_validated_data_by_id = {
  28. i.pop(id_attr): i
  29. for i in all_validated_data
  30. }
  31. # since this method is given a queryset which can have many
  32. # model instances, first find all objects to update
  33. # and only then update the models
  34. objects_to_update = queryset.filter(**{
  35. '{}__in'.format(id_attr): all_validated_data_by_id.keys(),
  36. })
  37. if len(all_validated_data_by_id) != objects_to_update.count():
  38. raise ValidationError('Could not find all objects to update.')
  39. updated_objects = []
  40. for obj in objects_to_update:
  41. obj_id = getattr(obj, id_attr)
  42. obj_validated_data = all_validated_data_by_id.get(obj_id)
  43. # use model serializer to actually update the model
  44. # in case that method is overwritten
  45. updated_objects.append(self.child.update(obj, obj_validated_data))
  46. return updated_objects