Coverage for /var/srv/projects/api.amasfac.comuna18.com/tmp/venv/lib/python3.9/site-packages/rest_framework/serializers.py: 73%
676 statements
« prev ^ index » next coverage.py v6.4.4, created at 2023-07-17 14:22 -0600
« prev ^ index » next coverage.py v6.4.4, created at 2023-07-17 14:22 -0600
1"""
2Serializers and ModelSerializers are similar to Forms and ModelForms.
3Unlike forms, they are not constrained to dealing with HTML output, and
4form encoded input.
6Serialization in REST framework is a two-phase process:
81. Serializers marshal between complex types like model instances, and
9python primitives.
102. The process of marshalling between python primitives and request and
11response content is handled by parsers and renderers.
12"""
13import copy
14import inspect
15import traceback
16from collections import OrderedDict, defaultdict
17from collections.abc import Mapping
19from django.core.exceptions import FieldDoesNotExist, ImproperlyConfigured
20from django.core.exceptions import ValidationError as DjangoValidationError
21from django.db import models
22from django.db.models.fields import Field as DjangoModelField
23from django.utils import timezone
24from django.utils.functional import cached_property
25from django.utils.translation import gettext_lazy as _
27from rest_framework.compat import postgres_fields
28from rest_framework.exceptions import ErrorDetail, ValidationError
29from rest_framework.fields import get_error_detail, set_value
30from rest_framework.settings import api_settings
31from rest_framework.utils import html, model_meta, representation
32from rest_framework.utils.field_mapping import (
33 ClassLookupDict, get_field_kwargs, get_nested_relation_kwargs,
34 get_relation_kwargs, get_url_kwargs
35)
36from rest_framework.utils.serializer_helpers import (
37 BindingDict, BoundField, JSONBoundField, NestedBoundField, ReturnDict,
38 ReturnList
39)
40from rest_framework.validators import (
41 UniqueForDateValidator, UniqueForMonthValidator, UniqueForYearValidator,
42 UniqueTogetherValidator
43)
45# Note: We do the following so that users of the framework can use this style:
46#
47# example_field = serializers.CharField(...)
48#
49# This helps keep the separation between model fields, form fields, and
50# serializer fields more explicit.
51from rest_framework.fields import ( # NOQA # isort:skip
52 BooleanField, CharField, ChoiceField, DateField, DateTimeField, DecimalField,
53 DictField, DurationField, EmailField, Field, FileField, FilePathField, FloatField,
54 HiddenField, HStoreField, IPAddressField, ImageField, IntegerField, JSONField,
55 ListField, ModelField, MultipleChoiceField, NullBooleanField, ReadOnlyField,
56 RegexField, SerializerMethodField, SlugField, TimeField, URLField, UUIDField,
57)
58from rest_framework.relations import ( # NOQA # isort:skip
59 HyperlinkedIdentityField, HyperlinkedRelatedField, ManyRelatedField,
60 PrimaryKeyRelatedField, RelatedField, SlugRelatedField, StringRelatedField,
61)
63# Non-field imports, but public API
64from rest_framework.fields import ( # NOQA # isort:skip
65 CreateOnlyDefault, CurrentUserDefault, SkipField, empty
66)
67from rest_framework.relations import Hyperlink, PKOnlyObject # NOQA # isort:skip
69# We assume that 'validators' are intended for the child serializer,
70# rather than the parent serializer.
71LIST_SERIALIZER_KWARGS = (
72 'read_only', 'write_only', 'required', 'default', 'initial', 'source',
73 'label', 'help_text', 'style', 'error_messages', 'allow_empty',
74 'instance', 'data', 'partial', 'context', 'allow_null',
75 'max_length', 'min_length'
76)
78ALL_FIELDS = '__all__'
81# BaseSerializer
82# --------------
84class BaseSerializer(Field):
85 """
86 The BaseSerializer class provides a minimal class which may be used
87 for writing custom serializer implementations.
89 Note that we strongly restrict the ordering of operations/properties
90 that may be used on the serializer in order to enforce correct usage.
92 In particular, if a `data=` argument is passed then:
94 .is_valid() - Available.
95 .initial_data - Available.
96 .validated_data - Only available after calling `is_valid()`
97 .errors - Only available after calling `is_valid()`
98 .data - Only available after calling `is_valid()`
100 If a `data=` argument is not passed then:
102 .is_valid() - Not available.
103 .initial_data - Not available.
104 .validated_data - Not available.
105 .errors - Not available.
106 .data - Available.
107 """
109 def __init__(self, instance=None, data=empty, **kwargs):
110 self.instance = instance
111 if data is not empty:
112 self.initial_data = data
113 self.partial = kwargs.pop('partial', False)
114 self._context = kwargs.pop('context', {})
115 kwargs.pop('many', None)
116 super().__init__(**kwargs)
118 def __new__(cls, *args, **kwargs):
119 # We override this method in order to automatically create
120 # `ListSerializer` classes instead when `many=True` is set.
121 if kwargs.pop('many', False):
122 return cls.many_init(*args, **kwargs)
123 return super().__new__(cls, *args, **kwargs)
125 # Allow type checkers to make serializers generic.
126 def __class_getitem__(cls, *args, **kwargs):
127 return cls
129 @classmethod
130 def many_init(cls, *args, **kwargs):
131 """
132 This method implements the creation of a `ListSerializer` parent
133 class when `many=True` is used. You can customize it if you need to
134 control which keyword arguments are passed to the parent, and
135 which are passed to the child.
137 Note that we're over-cautious in passing most arguments to both parent
138 and child classes in order to try to cover the general case. If you're
139 overriding this method you'll probably want something much simpler, eg:
141 @classmethod
142 def many_init(cls, *args, **kwargs):
143 kwargs['child'] = cls()
144 return CustomListSerializer(*args, **kwargs)
145 """
146 allow_empty = kwargs.pop('allow_empty', None)
147 max_length = kwargs.pop('max_length', None)
148 min_length = kwargs.pop('min_length', None)
149 child_serializer = cls(*args, **kwargs)
150 list_kwargs = {
151 'child': child_serializer,
152 }
153 if allow_empty is not None: 153 ↛ 154line 153 didn't jump to line 154, because the condition on line 153 was never true
154 list_kwargs['allow_empty'] = allow_empty
155 if max_length is not None: 155 ↛ 156line 155 didn't jump to line 156, because the condition on line 155 was never true
156 list_kwargs['max_length'] = max_length
157 if min_length is not None: 157 ↛ 158line 157 didn't jump to line 158, because the condition on line 157 was never true
158 list_kwargs['min_length'] = min_length
159 list_kwargs.update({
160 key: value for key, value in kwargs.items()
161 if key in LIST_SERIALIZER_KWARGS
162 })
163 meta = getattr(cls, 'Meta', None)
164 list_serializer_class = getattr(meta, 'list_serializer_class', ListSerializer)
165 return list_serializer_class(*args, **list_kwargs)
167 def to_internal_value(self, data):
168 raise NotImplementedError('`to_internal_value()` must be implemented.')
170 def to_representation(self, instance):
171 raise NotImplementedError('`to_representation()` must be implemented.')
173 def update(self, instance, validated_data):
174 raise NotImplementedError('`update()` must be implemented.')
176 def create(self, validated_data):
177 raise NotImplementedError('`create()` must be implemented.')
179 def save(self, **kwargs):
180 assert hasattr(self, '_errors'), (
181 'You must call `.is_valid()` before calling `.save()`.'
182 )
184 assert not self.errors, (
185 'You cannot call `.save()` on a serializer with invalid data.'
186 )
188 # Guard against incorrect use of `serializer.save(commit=False)`
189 assert 'commit' not in kwargs, (
190 "'commit' is not a valid keyword argument to the 'save()' method. "
191 "If you need to access data before committing to the database then "
192 "inspect 'serializer.validated_data' instead. "
193 "You can also pass additional keyword arguments to 'save()' if you "
194 "need to set extra attributes on the saved model instance. "
195 "For example: 'serializer.save(owner=request.user)'.'"
196 )
198 assert not hasattr(self, '_data'), (
199 "You cannot call `.save()` after accessing `serializer.data`."
200 "If you need to access data before committing to the database then "
201 "inspect 'serializer.validated_data' instead. "
202 )
204 validated_data = {**self.validated_data, **kwargs}
206 if self.instance is not None:
207 self.instance = self.update(self.instance, validated_data)
208 assert self.instance is not None, (
209 '`update()` did not return an object instance.'
210 )
211 else:
212 self.instance = self.create(validated_data)
213 assert self.instance is not None, (
214 '`create()` did not return an object instance.'
215 )
217 return self.instance
219 def is_valid(self, raise_exception=False):
220 assert hasattr(self, 'initial_data'), (
221 'Cannot call `.is_valid()` as no `data=` keyword argument was '
222 'passed when instantiating the serializer instance.'
223 )
225 if not hasattr(self, '_validated_data'): 225 ↛ 234line 225 didn't jump to line 234, because the condition on line 225 was never false
226 try:
227 self._validated_data = self.run_validation(self.initial_data)
228 except ValidationError as exc:
229 self._validated_data = {}
230 self._errors = exc.detail
231 else:
232 self._errors = {}
234 if self._errors and raise_exception:
235 raise ValidationError(self.errors)
237 return not bool(self._errors)
239 @property
240 def data(self):
241 if hasattr(self, 'initial_data') and not hasattr(self, '_validated_data'): 241 ↛ 242line 241 didn't jump to line 242
242 msg = (
243 'When a serializer is passed a `data` keyword argument you '
244 'must call `.is_valid()` before attempting to access the '
245 'serialized `.data` representation.\n'
246 'You should either call `.is_valid()` first, '
247 'or access `.initial_data` instead.'
248 )
249 raise AssertionError(msg)
251 if not hasattr(self, '_data'):
252 if self.instance is not None and not getattr(self, '_errors', None): 252 ↛ 254line 252 didn't jump to line 254, because the condition on line 252 was never false
253 self._data = self.to_representation(self.instance)
254 elif hasattr(self, '_validated_data') and not getattr(self, '_errors', None):
255 self._data = self.to_representation(self.validated_data)
256 else:
257 self._data = self.get_initial()
258 return self._data
260 @property
261 def errors(self):
262 if not hasattr(self, '_errors'): 262 ↛ 263line 262 didn't jump to line 263, because the condition on line 262 was never true
263 msg = 'You must call `.is_valid()` before accessing `.errors`.'
264 raise AssertionError(msg)
265 return self._errors
267 @property
268 def validated_data(self):
269 if not hasattr(self, '_validated_data'): 269 ↛ 270line 269 didn't jump to line 270, because the condition on line 269 was never true
270 msg = 'You must call `.is_valid()` before accessing `.validated_data`.'
271 raise AssertionError(msg)
272 return self._validated_data
275# Serializer & ListSerializer classes
276# -----------------------------------
278class SerializerMetaclass(type):
279 """
280 This metaclass sets a dictionary named `_declared_fields` on the class.
282 Any instances of `Field` included as attributes on either the class
283 or on any of its superclasses will be include in the
284 `_declared_fields` dictionary.
285 """
287 @classmethod
288 def _get_declared_fields(cls, bases, attrs):
289 fields = [(field_name, attrs.pop(field_name))
290 for field_name, obj in list(attrs.items())
291 if isinstance(obj, Field)]
292 fields.sort(key=lambda x: x[1]._creation_counter)
294 # Ensures a base class field doesn't override cls attrs, and maintains
295 # field precedence when inheriting multiple parents. e.g. if there is a
296 # class C(A, B), and A and B both define 'field', use 'field' from A.
297 known = set(attrs)
299 def visit(name):
300 known.add(name)
301 return name
303 base_fields = [
304 (visit(name), f)
305 for base in bases if hasattr(base, '_declared_fields')
306 for name, f in base._declared_fields.items() if name not in known
307 ]
309 return OrderedDict(base_fields + fields)
311 def __new__(cls, name, bases, attrs):
312 attrs['_declared_fields'] = cls._get_declared_fields(bases, attrs)
313 return super().__new__(cls, name, bases, attrs)
316def as_serializer_error(exc):
317 assert isinstance(exc, (ValidationError, DjangoValidationError))
319 if isinstance(exc, DjangoValidationError): 319 ↛ 320line 319 didn't jump to line 320, because the condition on line 319 was never true
320 detail = get_error_detail(exc)
321 else:
322 detail = exc.detail
324 if isinstance(detail, Mapping):
325 # If errors may be a dict we use the standard {key: list of values}.
326 # Here we ensure that all the values are *lists* of errors.
327 return {
328 key: value if isinstance(value, (list, Mapping)) else [value]
329 for key, value in detail.items()
330 }
331 elif isinstance(detail, list): 331 ↛ 337line 331 didn't jump to line 337, because the condition on line 331 was never false
332 # Errors raised as a list are non-field errors.
333 return {
334 api_settings.NON_FIELD_ERRORS_KEY: detail
335 }
336 # Errors raised as a string are non-field errors.
337 return {
338 api_settings.NON_FIELD_ERRORS_KEY: [detail]
339 }
342class Serializer(BaseSerializer, metaclass=SerializerMetaclass):
343 default_error_messages = {
344 'invalid': _('Invalid data. Expected a dictionary, but got {datatype}.')
345 }
347 @cached_property
348 def fields(self):
349 """
350 A dictionary of {field_name: field_instance}.
351 """
352 # `fields` is evaluated lazily. We do this to ensure that we don't
353 # have issues importing modules that use ModelSerializers as fields,
354 # even if Django's app-loading stage has not yet run.
355 fields = BindingDict(self)
356 for key, value in self.get_fields().items():
357 fields[key] = value
358 return fields
360 @property
361 def _writable_fields(self):
362 for field in self.fields.values():
363 if not field.read_only:
364 yield field
366 @property
367 def _readable_fields(self):
368 for field in self.fields.values():
369 if not field.write_only:
370 yield field
372 def get_fields(self):
373 """
374 Returns a dictionary of {field_name: field_instance}.
375 """
376 # Every new serializer is created with a clone of the field instances.
377 # This allows users to dynamically modify the fields on a serializer
378 # instance without affecting every other serializer instance.
379 return copy.deepcopy(self._declared_fields)
381 def get_validators(self):
382 """
383 Returns a list of validator callables.
384 """
385 # Used by the lazily-evaluated `validators` property.
386 meta = getattr(self, 'Meta', None)
387 validators = getattr(meta, 'validators', None)
388 return list(validators) if validators else []
390 def get_initial(self):
391 if hasattr(self, 'initial_data'):
392 # initial_data may not be a valid type
393 if not isinstance(self.initial_data, Mapping): 393 ↛ 394line 393 didn't jump to line 394, because the condition on line 393 was never true
394 return OrderedDict()
396 return OrderedDict([
397 (field_name, field.get_value(self.initial_data))
398 for field_name, field in self.fields.items()
399 if (field.get_value(self.initial_data) is not empty) and
400 not field.read_only
401 ])
403 return OrderedDict([
404 (field.field_name, field.get_initial())
405 for field in self.fields.values()
406 if not field.read_only
407 ])
409 def get_value(self, dictionary):
410 # We override the default field access in order to support
411 # nested HTML forms.
412 if html.is_html_input(dictionary):
413 return html.parse_html_dict(dictionary, prefix=self.field_name) or empty
414 return dictionary.get(self.field_name, empty)
416 def run_validation(self, data=empty):
417 """
418 We override the default `run_validation`, because the validation
419 performed by validators and the `.validate()` method should
420 be coerced into an error dictionary with a 'non_fields_error' key.
421 """
422 (is_empty_value, data) = self.validate_empty_values(data)
423 if is_empty_value: 423 ↛ 424line 423 didn't jump to line 424, because the condition on line 423 was never true
424 return data
426 value = self.to_internal_value(data)
427 try:
428 self.run_validators(value)
429 value = self.validate(value)
430 assert value is not None, '.validate() should return the validated data'
431 except (ValidationError, DjangoValidationError) as exc:
432 raise ValidationError(detail=as_serializer_error(exc))
434 return value
436 def _read_only_defaults(self):
437 fields = [
438 field for field in self.fields.values()
439 if (field.read_only) and (field.default != empty) and (field.source != '*') and ('.' not in field.source)
440 ]
442 defaults = OrderedDict()
443 for field in fields: 443 ↛ 444line 443 didn't jump to line 444, because the loop on line 443 never started
444 try:
445 default = field.get_default()
446 except SkipField:
447 continue
448 defaults[field.source] = default
450 return defaults
452 def run_validators(self, value):
453 """
454 Add read_only fields with defaults to value before running validators.
455 """
456 if isinstance(value, dict): 456 ↛ 460line 456 didn't jump to line 460, because the condition on line 456 was never false
457 to_validate = self._read_only_defaults()
458 to_validate.update(value)
459 else:
460 to_validate = value
461 super().run_validators(to_validate)
463 def to_internal_value(self, data):
464 """
465 Dict of native values <- Dict of primitive datatypes.
466 """
467 if not isinstance(data, Mapping): 467 ↛ 468line 467 didn't jump to line 468, because the condition on line 467 was never true
468 message = self.error_messages['invalid'].format(
469 datatype=type(data).__name__
470 )
471 raise ValidationError({
472 api_settings.NON_FIELD_ERRORS_KEY: [message]
473 }, code='invalid')
475 ret = OrderedDict()
476 errors = OrderedDict()
477 fields = self._writable_fields
479 for field in fields:
480 validate_method = getattr(self, 'validate_' + field.field_name, None)
481 primitive_value = field.get_value(data)
482 try:
483 validated_value = field.run_validation(primitive_value)
484 if validate_method is not None:
485 validated_value = validate_method(validated_value)
486 except ValidationError as exc:
487 errors[field.field_name] = exc.detail
488 except DjangoValidationError as exc: 488 ↛ 489line 488 didn't jump to line 489, because the exception caught by line 488 didn't happen
489 errors[field.field_name] = get_error_detail(exc)
490 except SkipField:
491 pass
492 else:
493 set_value(ret, field.source_attrs, validated_value)
495 if errors:
496 raise ValidationError(errors)
498 return ret
500 def to_representation(self, instance):
501 """
502 Object instance -> Dict of primitive datatypes.
503 """
504 ret = OrderedDict()
505 fields = self._readable_fields
507 for field in fields:
508 try:
509 attribute = field.get_attribute(instance)
510 except SkipField:
511 continue
513 # We skip `to_representation` for `None` values so that fields do
514 # not have to explicitly deal with that case.
515 #
516 # For related fields with `use_pk_only_optimization` we need to
517 # resolve the pk value.
518 check_for_none = attribute.pk if isinstance(attribute, PKOnlyObject) else attribute
519 if check_for_none is None:
520 ret[field.field_name] = None
521 else:
522 ret[field.field_name] = field.to_representation(attribute)
524 return ret
526 def validate(self, attrs):
527 return attrs
529 def __repr__(self):
530 return representation.serializer_repr(self, indent=1)
532 # The following are used for accessing `BoundField` instances on the
533 # serializer, for the purposes of presenting a form-like API onto the
534 # field values and field errors.
536 def __iter__(self):
537 for field in self.fields.values():
538 yield self[field.field_name]
540 def __getitem__(self, key):
541 field = self.fields[key]
542 value = self.data.get(key)
543 error = self.errors.get(key) if hasattr(self, '_errors') else None
544 if isinstance(field, Serializer):
545 return NestedBoundField(field, value, error)
546 if isinstance(field, JSONField):
547 return JSONBoundField(field, value, error)
548 return BoundField(field, value, error)
550 # Include a backlink to the serializer class on return objects.
551 # Allows renderers such as HTMLFormRenderer to get the full field info.
553 @property
554 def data(self):
555 ret = super().data
556 return ReturnDict(ret, serializer=self)
558 @property
559 def errors(self):
560 ret = super().errors
561 if isinstance(ret, list) and len(ret) == 1 and getattr(ret[0], 'code', None) == 'null': 561 ↛ 564line 561 didn't jump to line 564, because the condition on line 561 was never true
562 # Edge case. Provide a more descriptive error than
563 # "this field may not be null", when no data is passed.
564 detail = ErrorDetail('No data provided', code='null')
565 ret = {api_settings.NON_FIELD_ERRORS_KEY: [detail]}
566 return ReturnDict(ret, serializer=self)
569# There's some replication of `ListField` here,
570# but that's probably better than obfuscating the call hierarchy.
572class ListSerializer(BaseSerializer):
573 child = None
574 many = True
576 default_error_messages = {
577 'not_a_list': _('Expected a list of items but got type "{input_type}".'),
578 'empty': _('This list may not be empty.'),
579 'max_length': _('Ensure this field has no more than {max_length} elements.'),
580 'min_length': _('Ensure this field has at least {min_length} elements.')
581 }
583 def __init__(self, *args, **kwargs):
584 self.child = kwargs.pop('child', copy.deepcopy(self.child))
585 self.allow_empty = kwargs.pop('allow_empty', True)
586 self.max_length = kwargs.pop('max_length', None)
587 self.min_length = kwargs.pop('min_length', None)
588 assert self.child is not None, '`child` is a required argument.'
589 assert not inspect.isclass(self.child), '`child` has not been instantiated.'
590 super().__init__(*args, **kwargs)
591 self.child.bind(field_name='', parent=self)
593 def get_initial(self):
594 if hasattr(self, 'initial_data'):
595 return self.to_representation(self.initial_data)
596 return []
598 def get_value(self, dictionary):
599 """
600 Given the input dictionary, return the field value.
601 """
602 # We override the default field access in order to support
603 # lists in HTML forms.
604 if html.is_html_input(dictionary):
605 return html.parse_html_list(dictionary, prefix=self.field_name, default=empty)
606 return dictionary.get(self.field_name, empty)
608 def run_validation(self, data=empty):
609 """
610 We override the default `run_validation`, because the validation
611 performed by validators and the `.validate()` method should
612 be coerced into an error dictionary with a 'non_fields_error' key.
613 """
614 (is_empty_value, data) = self.validate_empty_values(data)
615 if is_empty_value: 615 ↛ 616line 615 didn't jump to line 616, because the condition on line 615 was never true
616 return data
618 value = self.to_internal_value(data)
619 try:
620 self.run_validators(value)
621 value = self.validate(value)
622 assert value is not None, '.validate() should return the validated data'
623 except (ValidationError, DjangoValidationError) as exc:
624 raise ValidationError(detail=as_serializer_error(exc))
626 return value
628 def to_internal_value(self, data):
629 """
630 List of dicts of native values <- List of dicts of primitive datatypes.
631 """
632 if html.is_html_input(data): 632 ↛ 633line 632 didn't jump to line 633, because the condition on line 632 was never true
633 data = html.parse_html_list(data, default=[])
635 if not isinstance(data, list): 635 ↛ 636line 635 didn't jump to line 636, because the condition on line 635 was never true
636 message = self.error_messages['not_a_list'].format(
637 input_type=type(data).__name__
638 )
639 raise ValidationError({
640 api_settings.NON_FIELD_ERRORS_KEY: [message]
641 }, code='not_a_list')
643 if not self.allow_empty and len(data) == 0: 643 ↛ 644line 643 didn't jump to line 644, because the condition on line 643 was never true
644 message = self.error_messages['empty']
645 raise ValidationError({
646 api_settings.NON_FIELD_ERRORS_KEY: [message]
647 }, code='empty')
649 if self.max_length is not None and len(data) > self.max_length: 649 ↛ 650line 649 didn't jump to line 650, because the condition on line 649 was never true
650 message = self.error_messages['max_length'].format(max_length=self.max_length)
651 raise ValidationError({
652 api_settings.NON_FIELD_ERRORS_KEY: [message]
653 }, code='max_length')
655 if self.min_length is not None and len(data) < self.min_length: 655 ↛ 656line 655 didn't jump to line 656, because the condition on line 655 was never true
656 message = self.error_messages['min_length'].format(min_length=self.min_length)
657 raise ValidationError({
658 api_settings.NON_FIELD_ERRORS_KEY: [message]
659 }, code='min_length')
661 ret = []
662 errors = []
664 for item in data:
665 try:
666 validated = self.child.run_validation(item)
667 except ValidationError as exc:
668 errors.append(exc.detail)
669 else:
670 ret.append(validated)
671 errors.append({})
673 if any(errors): 673 ↛ 674line 673 didn't jump to line 674, because the condition on line 673 was never true
674 raise ValidationError(errors)
676 return ret
678 def to_representation(self, data):
679 """
680 List of object instances -> List of dicts of primitive datatypes.
681 """
682 # Dealing with nested relationships, data can be a Manager,
683 # so, first get a queryset from the Manager if needed
684 iterable = data.all() if isinstance(data, models.Manager) else data
686 return [
687 self.child.to_representation(item) for item in iterable
688 ]
690 def validate(self, attrs):
691 return attrs
693 def update(self, instance, validated_data):
694 raise NotImplementedError(
695 "Serializers with many=True do not support multiple update by "
696 "default, only multiple create. For updates it is unclear how to "
697 "deal with insertions and deletions. If you need to support "
698 "multiple update, use a `ListSerializer` class and override "
699 "`.update()` so you can specify the behavior exactly."
700 )
702 def create(self, validated_data):
703 return [
704 self.child.create(attrs) for attrs in validated_data
705 ]
707 def save(self, **kwargs):
708 """
709 Save and return a list of object instances.
710 """
711 # Guard against incorrect use of `serializer.save(commit=False)`
712 assert 'commit' not in kwargs, (
713 "'commit' is not a valid keyword argument to the 'save()' method. "
714 "If you need to access data before committing to the database then "
715 "inspect 'serializer.validated_data' instead. "
716 "You can also pass additional keyword arguments to 'save()' if you "
717 "need to set extra attributes on the saved model instance. "
718 "For example: 'serializer.save(owner=request.user)'.'"
719 )
721 validated_data = [
722 {**attrs, **kwargs} for attrs in self.validated_data
723 ]
725 if self.instance is not None:
726 self.instance = self.update(self.instance, validated_data)
727 assert self.instance is not None, (
728 '`update()` did not return an object instance.'
729 )
730 else:
731 self.instance = self.create(validated_data)
732 assert self.instance is not None, (
733 '`create()` did not return an object instance.'
734 )
736 return self.instance
738 def is_valid(self, raise_exception=False):
739 # This implementation is the same as the default,
740 # except that we use lists, rather than dicts, as the empty case.
741 assert hasattr(self, 'initial_data'), (
742 'Cannot call `.is_valid()` as no `data=` keyword argument was '
743 'passed when instantiating the serializer instance.'
744 )
746 if not hasattr(self, '_validated_data'):
747 try:
748 self._validated_data = self.run_validation(self.initial_data)
749 except ValidationError as exc:
750 self._validated_data = []
751 self._errors = exc.detail
752 else:
753 self._errors = []
755 if self._errors and raise_exception:
756 raise ValidationError(self.errors)
758 return not bool(self._errors)
760 def __repr__(self):
761 return representation.list_repr(self, indent=1)
763 # Include a backlink to the serializer class on return objects.
764 # Allows renderers such as HTMLFormRenderer to get the full field info.
766 @property
767 def data(self):
768 ret = super().data
769 return ReturnList(ret, serializer=self)
771 @property
772 def errors(self):
773 ret = super().errors
774 if isinstance(ret, list) and len(ret) == 1 and getattr(ret[0], 'code', None) == 'null':
775 # Edge case. Provide a more descriptive error than
776 # "this field may not be null", when no data is passed.
777 detail = ErrorDetail('No data provided', code='null')
778 ret = {api_settings.NON_FIELD_ERRORS_KEY: [detail]}
779 if isinstance(ret, dict):
780 return ReturnDict(ret, serializer=self)
781 return ReturnList(ret, serializer=self)
784# ModelSerializer & HyperlinkedModelSerializer
785# --------------------------------------------
787def raise_errors_on_nested_writes(method_name, serializer, validated_data):
788 """
789 Give explicit errors when users attempt to pass writable nested data.
791 If we don't do this explicitly they'd get a less helpful error when
792 calling `.save()` on the serializer.
794 We don't *automatically* support these sorts of nested writes because
795 there are too many ambiguities to define a default behavior.
797 Eg. Suppose we have a `UserSerializer` with a nested profile. How should
798 we handle the case of an update, where the `profile` relationship does
799 not exist? Any of the following might be valid:
801 * Raise an application error.
802 * Silently ignore the nested part of the update.
803 * Automatically create a profile instance.
804 """
805 ModelClass = serializer.Meta.model
806 model_field_info = model_meta.get_field_info(ModelClass)
808 # Ensure we don't have a writable nested field. For example:
809 #
810 # class UserSerializer(ModelSerializer):
811 # ...
812 # profile = ProfileSerializer()
813 assert not any(
814 isinstance(field, BaseSerializer) and
815 (field.source in validated_data) and
816 (field.source in model_field_info.relations) and
817 isinstance(validated_data[field.source], (list, dict))
818 for field in serializer._writable_fields
819 ), (
820 'The `.{method_name}()` method does not support writable nested '
821 'fields by default.\nWrite an explicit `.{method_name}()` method for '
822 'serializer `{module}.{class_name}`, or set `read_only=True` on '
823 'nested serializer fields.'.format(
824 method_name=method_name,
825 module=serializer.__class__.__module__,
826 class_name=serializer.__class__.__name__
827 )
828 )
830 # Ensure we don't have a writable dotted-source field. For example:
831 #
832 # class UserSerializer(ModelSerializer):
833 # ...
834 # address = serializer.CharField('profile.address')
835 #
836 # Though, non-relational fields (e.g., JSONField) are acceptable. For example:
837 #
838 # class NonRelationalPersonModel(models.Model):
839 # profile = JSONField()
840 #
841 # class UserSerializer(ModelSerializer):
842 # ...
843 # address = serializer.CharField('profile.address')
844 assert not any(
845 len(field.source_attrs) > 1 and
846 (field.source_attrs[0] in validated_data) and
847 (field.source_attrs[0] in model_field_info.relations) and
848 isinstance(validated_data[field.source_attrs[0]], (list, dict))
849 for field in serializer._writable_fields
850 ), (
851 'The `.{method_name}()` method does not support writable dotted-source '
852 'fields by default.\nWrite an explicit `.{method_name}()` method for '
853 'serializer `{module}.{class_name}`, or set `read_only=True` on '
854 'dotted-source serializer fields.'.format(
855 method_name=method_name,
856 module=serializer.__class__.__module__,
857 class_name=serializer.__class__.__name__
858 )
859 )
862class ModelSerializer(Serializer):
863 """
864 A `ModelSerializer` is just a regular `Serializer`, except that:
866 * A set of default fields are automatically populated.
867 * A set of default validators are automatically populated.
868 * Default `.create()` and `.update()` implementations are provided.
870 The process of automatically determining a set of serializer fields
871 based on the model fields is reasonably complex, but you almost certainly
872 don't need to dig into the implementation.
874 If the `ModelSerializer` class *doesn't* generate the set of fields that
875 you need you should either declare the extra/differing fields explicitly on
876 the serializer class, or simply use a `Serializer` class.
877 """
878 serializer_field_mapping = {
879 models.AutoField: IntegerField,
880 models.BigIntegerField: IntegerField,
881 models.BooleanField: BooleanField,
882 models.CharField: CharField,
883 models.CommaSeparatedIntegerField: CharField,
884 models.DateField: DateField,
885 models.DateTimeField: DateTimeField,
886 models.DecimalField: DecimalField,
887 models.DurationField: DurationField,
888 models.EmailField: EmailField,
889 models.Field: ModelField,
890 models.FileField: FileField,
891 models.FloatField: FloatField,
892 models.ImageField: ImageField,
893 models.IntegerField: IntegerField,
894 models.NullBooleanField: BooleanField,
895 models.PositiveIntegerField: IntegerField,
896 models.PositiveSmallIntegerField: IntegerField,
897 models.SlugField: SlugField,
898 models.SmallIntegerField: IntegerField,
899 models.TextField: CharField,
900 models.TimeField: TimeField,
901 models.URLField: URLField,
902 models.UUIDField: UUIDField,
903 models.GenericIPAddressField: IPAddressField,
904 models.FilePathField: FilePathField,
905 }
906 if hasattr(models, 'JSONField'): 906 ↛ 908line 906 didn't jump to line 908, because the condition on line 906 was never false
907 serializer_field_mapping[models.JSONField] = JSONField
908 if postgres_fields: 908 ↛ 912line 908 didn't jump to line 912, because the condition on line 908 was never false
909 serializer_field_mapping[postgres_fields.HStoreField] = HStoreField
910 serializer_field_mapping[postgres_fields.ArrayField] = ListField
911 serializer_field_mapping[postgres_fields.JSONField] = JSONField
912 serializer_related_field = PrimaryKeyRelatedField
913 serializer_related_to_field = SlugRelatedField
914 serializer_url_field = HyperlinkedIdentityField
915 serializer_choice_field = ChoiceField
917 # The field name for hyperlinked identity fields. Defaults to 'url'.
918 # You can modify this using the API setting.
919 #
920 # Note that if you instead need modify this on a per-serializer basis,
921 # you'll also need to ensure you update the `create` method on any generic
922 # views, to correctly handle the 'Location' response header for
923 # "HTTP 201 Created" responses.
924 url_field_name = None
926 # Default `create` and `update` behavior...
927 def create(self, validated_data):
928 """
929 We have a bit of extra checking around this in order to provide
930 descriptive messages when something goes wrong, but this method is
931 essentially just:
933 return ExampleModel.objects.create(**validated_data)
935 If there are many to many fields present on the instance then they
936 cannot be set until the model is instantiated, in which case the
937 implementation is like so:
939 example_relationship = validated_data.pop('example_relationship')
940 instance = ExampleModel.objects.create(**validated_data)
941 instance.example_relationship = example_relationship
942 return instance
944 The default implementation also does not handle nested relationships.
945 If you want to support writable nested relationships you'll need
946 to write an explicit `.create()` method.
947 """
948 raise_errors_on_nested_writes('create', self, validated_data)
950 ModelClass = self.Meta.model
952 # Remove many-to-many relationships from validated_data.
953 # They are not valid arguments to the default `.create()` method,
954 # as they require that the instance has already been saved.
955 info = model_meta.get_field_info(ModelClass)
956 many_to_many = {}
957 for field_name, relation_info in info.relations.items():
958 if relation_info.to_many and (field_name in validated_data):
959 many_to_many[field_name] = validated_data.pop(field_name)
961 try:
962 instance = ModelClass._default_manager.create(**validated_data)
963 except TypeError:
964 tb = traceback.format_exc()
965 msg = (
966 'Got a `TypeError` when calling `%s.%s.create()`. '
967 'This may be because you have a writable field on the '
968 'serializer class that is not a valid argument to '
969 '`%s.%s.create()`. You may need to make the field '
970 'read-only, or override the %s.create() method to handle '
971 'this correctly.\nOriginal exception was:\n %s' %
972 (
973 ModelClass.__name__,
974 ModelClass._default_manager.name,
975 ModelClass.__name__,
976 ModelClass._default_manager.name,
977 self.__class__.__name__,
978 tb
979 )
980 )
981 raise TypeError(msg)
983 # Save many-to-many relationships after the instance is created.
984 if many_to_many:
985 for field_name, value in many_to_many.items():
986 field = getattr(instance, field_name)
987 field.set(value)
989 return instance
991 def update(self, instance, validated_data):
992 raise_errors_on_nested_writes('update', self, validated_data)
993 info = model_meta.get_field_info(instance)
995 # Simply set each attribute on the instance, and then save it.
996 # Note that unlike `.create()` we don't need to treat many-to-many
997 # relationships as being a special case. During updates we already
998 # have an instance pk for the relationships to be associated with.
999 m2m_fields = []
1000 for attr, value in validated_data.items():
1001 if attr in info.relations and info.relations[attr].to_many:
1002 m2m_fields.append((attr, value))
1003 else:
1004 setattr(instance, attr, value)
1006 instance.save()
1008 # Note that many-to-many fields are set after updating instance.
1009 # Setting m2m fields triggers signals which could potentially change
1010 # updated instance and we do not want it to collide with .update()
1011 for attr, value in m2m_fields:
1012 field = getattr(instance, attr)
1013 field.set(value)
1015 return instance
1017 # Determine the fields to apply...
1019 def get_fields(self):
1020 """
1021 Return the dict of field names -> field instances that should be
1022 used for `self.fields` when instantiating the serializer.
1023 """
1024 if self.url_field_name is None: 1024 ↛ 1027line 1024 didn't jump to line 1027, because the condition on line 1024 was never false
1025 self.url_field_name = api_settings.URL_FIELD_NAME
1027 assert hasattr(self, 'Meta'), (
1028 'Class {serializer_class} missing "Meta" attribute'.format(
1029 serializer_class=self.__class__.__name__
1030 )
1031 )
1032 assert hasattr(self.Meta, 'model'), (
1033 'Class {serializer_class} missing "Meta.model" attribute'.format(
1034 serializer_class=self.__class__.__name__
1035 )
1036 )
1037 if model_meta.is_abstract_model(self.Meta.model): 1037 ↛ 1038line 1037 didn't jump to line 1038, because the condition on line 1037 was never true
1038 raise ValueError(
1039 'Cannot use ModelSerializer with Abstract Models.'
1040 )
1042 declared_fields = copy.deepcopy(self._declared_fields)
1043 model = getattr(self.Meta, 'model')
1044 depth = getattr(self.Meta, 'depth', 0)
1046 if depth is not None: 1046 ↛ 1051line 1046 didn't jump to line 1051, because the condition on line 1046 was never false
1047 assert depth >= 0, "'depth' may not be negative."
1048 assert depth <= 10, "'depth' may not be greater than 10."
1050 # Retrieve metadata about fields & relationships on the model class.
1051 info = model_meta.get_field_info(model)
1052 field_names = self.get_field_names(declared_fields, info)
1054 # Determine any extra field arguments and hidden fields that
1055 # should be included
1056 extra_kwargs = self.get_extra_kwargs()
1057 extra_kwargs, hidden_fields = self.get_uniqueness_extra_kwargs(
1058 field_names, declared_fields, extra_kwargs
1059 )
1061 # Determine the fields that should be included on the serializer.
1062 fields = OrderedDict()
1064 for field_name in field_names:
1065 # If the field is explicitly declared on the class then use that.
1066 if field_name in declared_fields:
1067 fields[field_name] = declared_fields[field_name]
1068 continue
1070 extra_field_kwargs = extra_kwargs.get(field_name, {})
1071 source = extra_field_kwargs.get('source', '*')
1072 if source == '*': 1072 ↛ 1076line 1072 didn't jump to line 1076, because the condition on line 1072 was never false
1073 source = field_name
1075 # Determine the serializer field class and keyword arguments.
1076 field_class, field_kwargs = self.build_field(
1077 source, info, model, depth
1078 )
1080 # Include any kwargs defined in `Meta.extra_kwargs`
1081 field_kwargs = self.include_extra_kwargs(
1082 field_kwargs, extra_field_kwargs
1083 )
1085 # Create the serializer field.
1086 fields[field_name] = field_class(**field_kwargs)
1088 # Add in any hidden fields.
1089 fields.update(hidden_fields)
1091 return fields
1093 # Methods for determining the set of field names to include...
1095 def get_field_names(self, declared_fields, info):
1096 """
1097 Returns the list of all field names that should be created when
1098 instantiating this serializer class. This is based on the default
1099 set of fields, but also takes into account the `Meta.fields` or
1100 `Meta.exclude` options if they have been specified.
1101 """
1102 fields = getattr(self.Meta, 'fields', None)
1103 exclude = getattr(self.Meta, 'exclude', None)
1105 if fields and fields != ALL_FIELDS and not isinstance(fields, (list, tuple)): 1105 ↛ 1106line 1105 didn't jump to line 1106, because the condition on line 1105 was never true
1106 raise TypeError(
1107 'The `fields` option must be a list or tuple or "__all__". '
1108 'Got %s.' % type(fields).__name__
1109 )
1111 if exclude and not isinstance(exclude, (list, tuple)): 1111 ↛ 1112line 1111 didn't jump to line 1112, because the condition on line 1111 was never true
1112 raise TypeError(
1113 'The `exclude` option must be a list or tuple. Got %s.' %
1114 type(exclude).__name__
1115 )
1117 assert not (fields and exclude), (
1118 "Cannot set both 'fields' and 'exclude' options on "
1119 "serializer {serializer_class}.".format(
1120 serializer_class=self.__class__.__name__
1121 )
1122 )
1124 assert not (fields is None and exclude is None), (
1125 "Creating a ModelSerializer without either the 'fields' attribute "
1126 "or the 'exclude' attribute has been deprecated since 3.3.0, "
1127 "and is now disallowed. Add an explicit fields = '__all__' to the "
1128 "{serializer_class} serializer.".format(
1129 serializer_class=self.__class__.__name__
1130 ),
1131 )
1133 if fields == ALL_FIELDS: 1133 ↛ 1134line 1133 didn't jump to line 1134, because the condition on line 1133 was never true
1134 fields = None
1136 if fields is not None: 1136 ↛ 1159line 1136 didn't jump to line 1159, because the condition on line 1136 was never false
1137 # Ensure that all declared fields have also been included in the
1138 # `Meta.fields` option.
1140 # Do not require any fields that are declared in a parent class,
1141 # in order to allow serializer subclasses to only include
1142 # a subset of fields.
1143 required_field_names = set(declared_fields)
1144 for cls in self.__class__.__bases__:
1145 required_field_names -= set(getattr(cls, '_declared_fields', []))
1147 for field_name in required_field_names:
1148 assert field_name in fields, (
1149 "The field '{field_name}' was declared on serializer "
1150 "{serializer_class}, but has not been included in the "
1151 "'fields' option.".format(
1152 field_name=field_name,
1153 serializer_class=self.__class__.__name__
1154 )
1155 )
1156 return fields
1158 # Use the default set of field names if `Meta.fields` is not specified.
1159 fields = self.get_default_field_names(declared_fields, info)
1161 if exclude is not None:
1162 # If `Meta.exclude` is included, then remove those fields.
1163 for field_name in exclude:
1164 assert field_name not in self._declared_fields, (
1165 "Cannot both declare the field '{field_name}' and include "
1166 "it in the {serializer_class} 'exclude' option. Remove the "
1167 "field or, if inherited from a parent serializer, disable "
1168 "with `{field_name} = None`."
1169 .format(
1170 field_name=field_name,
1171 serializer_class=self.__class__.__name__
1172 )
1173 )
1175 assert field_name in fields, (
1176 "The field '{field_name}' was included on serializer "
1177 "{serializer_class} in the 'exclude' option, but does "
1178 "not match any model field.".format(
1179 field_name=field_name,
1180 serializer_class=self.__class__.__name__
1181 )
1182 )
1183 fields.remove(field_name)
1185 return fields
1187 def get_default_field_names(self, declared_fields, model_info):
1188 """
1189 Return the default list of field names that will be used if the
1190 `Meta.fields` option is not specified.
1191 """
1192 return (
1193 [model_info.pk.name] +
1194 list(declared_fields) +
1195 list(model_info.fields) +
1196 list(model_info.forward_relations)
1197 )
1199 # Methods for constructing serializer fields...
1201 def build_field(self, field_name, info, model_class, nested_depth):
1202 """
1203 Return a two tuple of (cls, kwargs) to build a serializer field with.
1204 """
1205 if field_name in info.fields_and_pk:
1206 model_field = info.fields_and_pk[field_name]
1207 return self.build_standard_field(field_name, model_field)
1209 elif field_name in info.relations: 1209 ↛ 1216line 1209 didn't jump to line 1216, because the condition on line 1209 was never false
1210 relation_info = info.relations[field_name]
1211 if not nested_depth: 1211 ↛ 1214line 1211 didn't jump to line 1214, because the condition on line 1211 was never false
1212 return self.build_relational_field(field_name, relation_info)
1213 else:
1214 return self.build_nested_field(field_name, relation_info, nested_depth)
1216 elif hasattr(model_class, field_name):
1217 return self.build_property_field(field_name, model_class)
1219 elif field_name == self.url_field_name:
1220 return self.build_url_field(field_name, model_class)
1222 return self.build_unknown_field(field_name, model_class)
1224 def build_standard_field(self, field_name, model_field):
1225 """
1226 Create regular model fields.
1227 """
1228 field_mapping = ClassLookupDict(self.serializer_field_mapping)
1230 field_class = field_mapping[model_field]
1231 field_kwargs = get_field_kwargs(field_name, model_field)
1233 # Special case to handle when a OneToOneField is also the primary key
1234 if model_field.one_to_one and model_field.primary_key: 1234 ↛ 1235line 1234 didn't jump to line 1235, because the condition on line 1234 was never true
1235 field_class = self.serializer_related_field
1236 field_kwargs['queryset'] = model_field.related_model.objects
1238 if 'choices' in field_kwargs:
1239 # Fields with choices get coerced into `ChoiceField`
1240 # instead of using their regular typed field.
1241 field_class = self.serializer_choice_field
1242 # Some model fields may introduce kwargs that would not be valid
1243 # for the choice field. We need to strip these out.
1244 # Eg. models.DecimalField(max_digits=3, decimal_places=1, choices=DECIMAL_CHOICES)
1245 valid_kwargs = {
1246 'read_only', 'write_only',
1247 'required', 'default', 'initial', 'source',
1248 'label', 'help_text', 'style',
1249 'error_messages', 'validators', 'allow_null', 'allow_blank',
1250 'choices'
1251 }
1252 for key in list(field_kwargs):
1253 if key not in valid_kwargs:
1254 field_kwargs.pop(key)
1256 if not issubclass(field_class, ModelField): 1256 ↛ 1262line 1256 didn't jump to line 1262, because the condition on line 1256 was never false
1257 # `model_field` is only valid for the fallback case of
1258 # `ModelField`, which is used when no other typed field
1259 # matched to the model field.
1260 field_kwargs.pop('model_field', None)
1262 if not issubclass(field_class, CharField) and not issubclass(field_class, ChoiceField):
1263 # `allow_blank` is only valid for textual fields.
1264 field_kwargs.pop('allow_blank', None)
1266 is_django_jsonfield = hasattr(models, 'JSONField') and isinstance(model_field, models.JSONField)
1267 if (postgres_fields and isinstance(model_field, postgres_fields.JSONField)) or is_django_jsonfield:
1268 # Populate the `encoder` argument of `JSONField` instances generated
1269 # for the model `JSONField`.
1270 field_kwargs['encoder'] = getattr(model_field, 'encoder', None)
1271 if is_django_jsonfield: 1271 ↛ 1274line 1271 didn't jump to line 1274, because the condition on line 1271 was never false
1272 field_kwargs['decoder'] = getattr(model_field, 'decoder', None)
1274 if postgres_fields and isinstance(model_field, postgres_fields.ArrayField): 1274 ↛ 1277line 1274 didn't jump to line 1277, because the condition on line 1274 was never true
1275 # Populate the `child` argument on `ListField` instances generated
1276 # for the PostgreSQL specific `ArrayField`.
1277 child_model_field = model_field.base_field
1278 child_field_class, child_field_kwargs = self.build_standard_field(
1279 'child', child_model_field
1280 )
1281 field_kwargs['child'] = child_field_class(**child_field_kwargs)
1283 return field_class, field_kwargs
1285 def build_relational_field(self, field_name, relation_info):
1286 """
1287 Create fields for forward and reverse relationships.
1288 """
1289 field_class = self.serializer_related_field
1290 field_kwargs = get_relation_kwargs(field_name, relation_info)
1292 to_field = field_kwargs.pop('to_field', None)
1293 if to_field and not relation_info.reverse and not relation_info.related_model._meta.get_field(to_field).primary_key: 1293 ↛ 1294line 1293 didn't jump to line 1294, because the condition on line 1293 was never true
1294 field_kwargs['slug_field'] = to_field
1295 field_class = self.serializer_related_to_field
1297 # `view_name` is only valid for hyperlinked relationships.
1298 if not issubclass(field_class, HyperlinkedRelatedField): 1298 ↛ 1301line 1298 didn't jump to line 1301, because the condition on line 1298 was never false
1299 field_kwargs.pop('view_name', None)
1301 return field_class, field_kwargs
1303 def build_nested_field(self, field_name, relation_info, nested_depth):
1304 """
1305 Create nested fields for forward and reverse relationships.
1306 """
1307 class NestedSerializer(ModelSerializer):
1308 class Meta:
1309 model = relation_info.related_model
1310 depth = nested_depth - 1
1311 fields = '__all__'
1313 field_class = NestedSerializer
1314 field_kwargs = get_nested_relation_kwargs(relation_info)
1316 return field_class, field_kwargs
1318 def build_property_field(self, field_name, model_class):
1319 """
1320 Create a read only field for model methods and properties.
1321 """
1322 field_class = ReadOnlyField
1323 field_kwargs = {}
1325 return field_class, field_kwargs
1327 def build_url_field(self, field_name, model_class):
1328 """
1329 Create a field representing the object's own URL.
1330 """
1331 field_class = self.serializer_url_field
1332 field_kwargs = get_url_kwargs(model_class)
1334 return field_class, field_kwargs
1336 def build_unknown_field(self, field_name, model_class):
1337 """
1338 Raise an error on any unknown fields.
1339 """
1340 raise ImproperlyConfigured(
1341 'Field name `%s` is not valid for model `%s`.' %
1342 (field_name, model_class.__name__)
1343 )
1345 def include_extra_kwargs(self, kwargs, extra_kwargs):
1346 """
1347 Include any 'extra_kwargs' that have been included for this field,
1348 possibly removing any incompatible existing keyword arguments.
1349 """
1350 if extra_kwargs.get('read_only', False):
1351 for attr in [
1352 'required', 'default', 'allow_blank', 'min_length',
1353 'max_length', 'min_value', 'max_value', 'validators', 'queryset'
1354 ]:
1355 kwargs.pop(attr, None)
1357 if extra_kwargs.get('default') and kwargs.get('required') is False: 1357 ↛ 1358line 1357 didn't jump to line 1358, because the condition on line 1357 was never true
1358 kwargs.pop('required')
1360 if extra_kwargs.get('read_only', kwargs.get('read_only', False)):
1361 extra_kwargs.pop('required', None) # Read only fields should always omit the 'required' argument.
1363 kwargs.update(extra_kwargs)
1365 return kwargs
1367 # Methods for determining additional keyword arguments to apply...
1369 def get_extra_kwargs(self):
1370 """
1371 Return a dictionary mapping field names to a dictionary of
1372 additional keyword arguments.
1373 """
1374 extra_kwargs = copy.deepcopy(getattr(self.Meta, 'extra_kwargs', {}))
1376 read_only_fields = getattr(self.Meta, 'read_only_fields', None)
1377 if read_only_fields is not None:
1378 if not isinstance(read_only_fields, (list, tuple)): 1378 ↛ 1379line 1378 didn't jump to line 1379, because the condition on line 1378 was never true
1379 raise TypeError(
1380 'The `read_only_fields` option must be a list or tuple. '
1381 'Got %s.' % type(read_only_fields).__name__
1382 )
1383 for field_name in read_only_fields:
1384 kwargs = extra_kwargs.get(field_name, {})
1385 kwargs['read_only'] = True
1386 extra_kwargs[field_name] = kwargs
1388 else:
1389 # Guard against the possible misspelling `readonly_fields` (used
1390 # by the Django admin and others).
1391 assert not hasattr(self.Meta, 'readonly_fields'), (
1392 'Serializer `%s.%s` has field `readonly_fields`; '
1393 'the correct spelling for the option is `read_only_fields`.' %
1394 (self.__class__.__module__, self.__class__.__name__)
1395 )
1397 return extra_kwargs
1399 def get_uniqueness_extra_kwargs(self, field_names, declared_fields, extra_kwargs):
1400 """
1401 Return any additional field options that need to be included as a
1402 result of uniqueness constraints on the model. This is returned as
1403 a two-tuple of:
1405 ('dict of updated extra kwargs', 'mapping of hidden fields')
1406 """
1407 if getattr(self.Meta, 'validators', None) is not None: 1407 ↛ 1408line 1407 didn't jump to line 1408, because the condition on line 1407 was never true
1408 return (extra_kwargs, {})
1410 model = getattr(self.Meta, 'model')
1411 model_fields = self._get_model_fields(
1412 field_names, declared_fields, extra_kwargs
1413 )
1415 # Determine if we need any additional `HiddenField` or extra keyword
1416 # arguments to deal with `unique_for` dates that are required to
1417 # be in the input data in order to validate it.
1418 unique_constraint_names = set()
1420 for model_field in model_fields.values():
1421 # Include each of the `unique_for_*` field names.
1422 unique_constraint_names |= {model_field.unique_for_date, model_field.unique_for_month,
1423 model_field.unique_for_year}
1425 unique_constraint_names -= {None}
1427 # Include each of the `unique_together` field names,
1428 # so long as all the field names are included on the serializer.
1429 for parent_class in [model] + list(model._meta.parents):
1430 for unique_together_list in parent_class._meta.unique_together:
1431 if set(field_names).issuperset(unique_together_list):
1432 unique_constraint_names |= set(unique_together_list)
1434 # Now we have all the field names that have uniqueness constraints
1435 # applied, we can add the extra 'required=...' or 'default=...'
1436 # arguments that are appropriate to these fields, or add a `HiddenField` for it.
1437 hidden_fields = {}
1438 uniqueness_extra_kwargs = {}
1440 for unique_constraint_name in unique_constraint_names:
1441 # Get the model field that is referred too.
1442 unique_constraint_field = model._meta.get_field(unique_constraint_name)
1444 if getattr(unique_constraint_field, 'auto_now_add', None): 1444 ↛ 1445line 1444 didn't jump to line 1445, because the condition on line 1444 was never true
1445 default = CreateOnlyDefault(timezone.now)
1446 elif getattr(unique_constraint_field, 'auto_now', None): 1446 ↛ 1447line 1446 didn't jump to line 1447, because the condition on line 1446 was never true
1447 default = timezone.now
1448 elif unique_constraint_field.has_default(): 1448 ↛ 1449line 1448 didn't jump to line 1449, because the condition on line 1448 was never true
1449 default = unique_constraint_field.default
1450 else:
1451 default = empty
1453 if unique_constraint_name in model_fields: 1453 ↛ 1459line 1453 didn't jump to line 1459, because the condition on line 1453 was never false
1454 # The corresponding field is present in the serializer
1455 if default is empty: 1455 ↛ 1458line 1455 didn't jump to line 1458, because the condition on line 1455 was never false
1456 uniqueness_extra_kwargs[unique_constraint_name] = {'required': True}
1457 else:
1458 uniqueness_extra_kwargs[unique_constraint_name] = {'default': default}
1459 elif default is not empty:
1460 # The corresponding field is not present in the
1461 # serializer. We have a default to use for it, so
1462 # add in a hidden field that populates it.
1463 hidden_fields[unique_constraint_name] = HiddenField(default=default)
1465 # Update `extra_kwargs` with any new options.
1466 for key, value in uniqueness_extra_kwargs.items():
1467 if key in extra_kwargs:
1468 value.update(extra_kwargs[key])
1469 extra_kwargs[key] = value
1471 return extra_kwargs, hidden_fields
1473 def _get_model_fields(self, field_names, declared_fields, extra_kwargs):
1474 """
1475 Returns all the model fields that are being mapped to by fields
1476 on the serializer class.
1477 Returned as a dict of 'model field name' -> 'model field'.
1478 Used internally by `get_uniqueness_field_options`.
1479 """
1480 model = getattr(self.Meta, 'model')
1481 model_fields = {}
1483 for field_name in field_names:
1484 if field_name in declared_fields:
1485 # If the field is declared on the serializer
1486 field = declared_fields[field_name]
1487 source = field.source or field_name
1488 else:
1489 try:
1490 source = extra_kwargs[field_name]['source']
1491 except KeyError:
1492 source = field_name
1494 if '.' in source or source == '*':
1495 # Model fields will always have a simple source mapping,
1496 # they can't be nested attribute lookups.
1497 continue
1499 try:
1500 field = model._meta.get_field(source)
1501 if isinstance(field, DjangoModelField):
1502 model_fields[source] = field
1503 except FieldDoesNotExist:
1504 pass
1506 return model_fields
1508 # Determine the validators to apply...
1510 def get_validators(self):
1511 """
1512 Determine the set of validators to use when instantiating serializer.
1513 """
1514 # If the validators have been declared explicitly then use that.
1515 validators = getattr(getattr(self, 'Meta', None), 'validators', None)
1516 if validators is not None: 1516 ↛ 1517line 1516 didn't jump to line 1517, because the condition on line 1516 was never true
1517 return list(validators)
1519 # Otherwise use the default set of validators.
1520 return (
1521 self.get_unique_together_validators() +
1522 self.get_unique_for_date_validators()
1523 )
1525 def get_unique_together_validators(self):
1526 """
1527 Determine a default set of validators for any unique_together constraints.
1528 """
1529 model_class_inheritance_tree = (
1530 [self.Meta.model] +
1531 list(self.Meta.model._meta.parents)
1532 )
1534 # The field names we're passing though here only include fields
1535 # which may map onto a model field. Any dotted field name lookups
1536 # cannot map to a field, and must be a traversal, so we're not
1537 # including those.
1538 field_sources = OrderedDict(
1539 (field.field_name, field.source) for field in self._writable_fields
1540 if (field.source != '*') and ('.' not in field.source)
1541 )
1543 # Special Case: Add read_only fields with defaults.
1544 field_sources.update(OrderedDict(
1545 (field.field_name, field.source) for field in self.fields.values()
1546 if (field.read_only) and (field.default != empty) and (field.source != '*') and ('.' not in field.source)
1547 ))
1549 # Invert so we can find the serializer field names that correspond to
1550 # the model field names in the unique_together sets. This also allows
1551 # us to check that multiple fields don't map to the same source.
1552 source_map = defaultdict(list)
1553 for name, source in field_sources.items():
1554 source_map[source].append(name)
1556 # Note that we make sure to check `unique_together` both on the
1557 # base model class, but also on any parent classes.
1558 validators = []
1559 for parent_class in model_class_inheritance_tree:
1560 for unique_together in parent_class._meta.unique_together:
1561 # Skip if serializer does not map to all unique together sources
1562 if not set(source_map).issuperset(unique_together): 1562 ↛ 1565line 1562 didn't jump to line 1565, because the condition on line 1562 was never false
1563 continue
1565 for source in unique_together:
1566 assert len(source_map[source]) == 1, (
1567 "Unable to create `UniqueTogetherValidator` for "
1568 "`{model}.{field}` as `{serializer}` has multiple "
1569 "fields ({fields}) that map to this model field. "
1570 "Either remove the extra fields, or override "
1571 "`Meta.validators` with a `UniqueTogetherValidator` "
1572 "using the desired field names."
1573 .format(
1574 model=self.Meta.model.__name__,
1575 serializer=self.__class__.__name__,
1576 field=source,
1577 fields=', '.join(source_map[source]),
1578 )
1579 )
1581 field_names = tuple(source_map[f][0] for f in unique_together)
1582 validator = UniqueTogetherValidator(
1583 queryset=parent_class._default_manager,
1584 fields=field_names
1585 )
1586 validators.append(validator)
1587 return validators
1589 def get_unique_for_date_validators(self):
1590 """
1591 Determine a default set of validators for the following constraints:
1593 * unique_for_date
1594 * unique_for_month
1595 * unique_for_year
1596 """
1597 info = model_meta.get_field_info(self.Meta.model)
1598 default_manager = self.Meta.model._default_manager
1599 field_names = [field.source for field in self.fields.values()]
1601 validators = []
1603 for field_name, field in info.fields_and_pk.items():
1604 if field.unique_for_date and field_name in field_names: 1604 ↛ 1605line 1604 didn't jump to line 1605, because the condition on line 1604 was never true
1605 validator = UniqueForDateValidator(
1606 queryset=default_manager,
1607 field=field_name,
1608 date_field=field.unique_for_date
1609 )
1610 validators.append(validator)
1612 if field.unique_for_month and field_name in field_names: 1612 ↛ 1613line 1612 didn't jump to line 1613, because the condition on line 1612 was never true
1613 validator = UniqueForMonthValidator(
1614 queryset=default_manager,
1615 field=field_name,
1616 date_field=field.unique_for_month
1617 )
1618 validators.append(validator)
1620 if field.unique_for_year and field_name in field_names: 1620 ↛ 1621line 1620 didn't jump to line 1621, because the condition on line 1620 was never true
1621 validator = UniqueForYearValidator(
1622 queryset=default_manager,
1623 field=field_name,
1624 date_field=field.unique_for_year
1625 )
1626 validators.append(validator)
1628 return validators
1631class HyperlinkedModelSerializer(ModelSerializer):
1632 """
1633 A type of `ModelSerializer` that uses hyperlinked relationships instead
1634 of primary key relationships. Specifically:
1636 * A 'url' field is included instead of the 'id' field.
1637 * Relationships to other instances are hyperlinks, instead of primary keys.
1638 """
1639 serializer_related_field = HyperlinkedRelatedField
1641 def get_default_field_names(self, declared_fields, model_info):
1642 """
1643 Return the default list of field names that will be used if the
1644 `Meta.fields` option is not specified.
1645 """
1646 return (
1647 [self.url_field_name] +
1648 list(declared_fields) +
1649 list(model_info.fields) +
1650 list(model_info.forward_relations)
1651 )
1653 def build_nested_field(self, field_name, relation_info, nested_depth):
1654 """
1655 Create nested fields for forward and reverse relationships.
1656 """
1657 class NestedSerializer(HyperlinkedModelSerializer):
1658 class Meta:
1659 model = relation_info.related_model
1660 depth = nested_depth - 1
1661 fields = '__all__'
1663 field_class = NestedSerializer
1664 field_kwargs = get_nested_relation_kwargs(relation_info)
1666 return field_class, field_kwargs