123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926 |
- import copy
- import datetime
- import decimal
- import functools
- import inspect
- import re
- import uuid
- import warnings
- from collections import OrderedDict
- from collections.abc import Mapping
- from django.conf import settings
- from django.core.exceptions import ObjectDoesNotExist
- from django.core.exceptions import ValidationError as DjangoValidationError
- from django.core.validators import (
- EmailValidator, MaxLengthValidator, MaxValueValidator, MinLengthValidator,
- MinValueValidator, ProhibitNullCharactersValidator, RegexValidator,
- URLValidator, ip_address_validators
- )
- from django.forms import FilePathField as DjangoFilePathField
- from django.forms import ImageField as DjangoImageField
- from django.utils import timezone
- from django.utils.dateparse import (
- parse_date, parse_datetime, parse_duration, parse_time
- )
- from django.utils.duration import duration_string
- from django.utils.encoding import is_protected_type, smart_str
- from django.utils.formats import localize_input, sanitize_separators
- from django.utils.ipv6 import clean_ipv6_address
- from django.utils.timezone import utc
- from django.utils.translation import gettext_lazy as _
- from pytz.exceptions import InvalidTimeError
- from rest_framework import (
- ISO_8601, RemovedInDRF313Warning, RemovedInDRF314Warning
- )
- from rest_framework.exceptions import ErrorDetail, ValidationError
- from rest_framework.settings import api_settings
- from rest_framework.utils import html, humanize_datetime, json, representation
- from rest_framework.utils.formatting import lazy_format
- from rest_framework.validators import ProhibitSurrogateCharactersValidator
- class empty:
- """
- This class is used to represent no data being provided for a given input
- or output value.
- It is required because `None` may be a valid input or output value.
- """
- pass
- class BuiltinSignatureError(Exception):
- """
- Built-in function signatures are not inspectable. This exception is raised
- so the serializer can raise a helpful error message.
- """
- pass
- def is_simple_callable(obj):
- """
- True if the object is a callable that takes no arguments.
- """
- # Bail early since we cannot inspect built-in function signatures.
- if inspect.isbuiltin(obj):
- raise BuiltinSignatureError(
- 'Built-in function signatures are not inspectable. '
- 'Wrap the function call in a simple, pure Python function.')
- if not (inspect.isfunction(obj) or inspect.ismethod(obj) or isinstance(obj, functools.partial)):
- return False
- sig = inspect.signature(obj)
- params = sig.parameters.values()
- return all(
- param.kind == param.VAR_POSITIONAL or
- param.kind == param.VAR_KEYWORD or
- param.default != param.empty
- for param in params
- )
- def get_attribute(instance, attrs):
- """
- Similar to Python's built in `getattr(instance, attr)`,
- but takes a list of nested attributes, instead of a single attribute.
- Also accepts either attribute lookup on objects or dictionary lookups.
- """
- for attr in attrs:
- try:
- if isinstance(instance, Mapping):
- instance = instance[attr]
- else:
- instance = getattr(instance, attr)
- except ObjectDoesNotExist:
- return None
- if is_simple_callable(instance):
- try:
- instance = instance()
- except (AttributeError, KeyError) as exc:
- # If we raised an Attribute or KeyError here it'd get treated
- # as an omitted field in `Field.get_attribute()`. Instead we
- # raise a ValueError to ensure the exception is not masked.
- raise ValueError('Exception raised in callable attribute "{}"; original exception was: {}'.format(attr, exc))
- return instance
- def set_value(dictionary, keys, value):
- """
- Similar to Python's built in `dictionary[key] = value`,
- but takes a list of nested keys instead of a single key.
- set_value({'a': 1}, [], {'b': 2}) -> {'a': 1, 'b': 2}
- set_value({'a': 1}, ['x'], 2) -> {'a': 1, 'x': 2}
- set_value({'a': 1}, ['x', 'y'], 2) -> {'a': 1, 'x': {'y': 2}}
- """
- if not keys:
- dictionary.update(value)
- return
- for key in keys[:-1]:
- if key not in dictionary:
- dictionary[key] = {}
- dictionary = dictionary[key]
- dictionary[keys[-1]] = value
- def to_choices_dict(choices):
- """
- Convert choices into key/value dicts.
- to_choices_dict([1]) -> {1: 1}
- to_choices_dict([(1, '1st'), (2, '2nd')]) -> {1: '1st', 2: '2nd'}
- to_choices_dict([('Group', ((1, '1st'), 2))]) -> {'Group': {1: '1st', 2: '2'}}
- """
- # Allow single, paired or grouped choices style:
- # choices = [1, 2, 3]
- # choices = [(1, 'First'), (2, 'Second'), (3, 'Third')]
- # choices = [('Category', ((1, 'First'), (2, 'Second'))), (3, 'Third')]
- ret = OrderedDict()
- for choice in choices:
- if not isinstance(choice, (list, tuple)):
- # single choice
- ret[choice] = choice
- else:
- key, value = choice
- if isinstance(value, (list, tuple)):
- # grouped choices (category, sub choices)
- ret[key] = to_choices_dict(value)
- else:
- # paired choice (key, display value)
- ret[key] = value
- return ret
- def flatten_choices_dict(choices):
- """
- Convert a group choices dict into a flat dict of choices.
- flatten_choices_dict({1: '1st', 2: '2nd'}) -> {1: '1st', 2: '2nd'}
- flatten_choices_dict({'Group': {1: '1st', 2: '2nd'}}) -> {1: '1st', 2: '2nd'}
- """
- ret = OrderedDict()
- for key, value in choices.items():
- if isinstance(value, dict):
- # grouped choices (category, sub choices)
- for sub_key, sub_value in value.items():
- ret[sub_key] = sub_value
- else:
- # choice (key, display value)
- ret[key] = value
- return ret
- def iter_options(grouped_choices, cutoff=None, cutoff_text=None):
- """
- Helper function for options and option groups in templates.
- """
- class StartOptionGroup:
- start_option_group = True
- end_option_group = False
- def __init__(self, label):
- self.label = label
- class EndOptionGroup:
- start_option_group = False
- end_option_group = True
- class Option:
- start_option_group = False
- end_option_group = False
- def __init__(self, value, display_text, disabled=False):
- self.value = value
- self.display_text = display_text
- self.disabled = disabled
- count = 0
- for key, value in grouped_choices.items():
- if cutoff and count >= cutoff:
- break
- if isinstance(value, dict):
- yield StartOptionGroup(label=key)
- for sub_key, sub_value in value.items():
- if cutoff and count >= cutoff:
- break
- yield Option(value=sub_key, display_text=sub_value)
- count += 1
- yield EndOptionGroup()
- else:
- yield Option(value=key, display_text=value)
- count += 1
- if cutoff and count >= cutoff and cutoff_text:
- cutoff_text = cutoff_text.format(count=cutoff)
- yield Option(value='n/a', display_text=cutoff_text, disabled=True)
- def get_error_detail(exc_info):
- """
- Given a Django ValidationError, return a list of ErrorDetail,
- with the `code` populated.
- """
- code = getattr(exc_info, 'code', None) or 'invalid'
- try:
- error_dict = exc_info.error_dict
- except AttributeError:
- return [
- ErrorDetail((error.message % error.params) if error.params else error.message,
- code=error.code if error.code else code)
- for error in exc_info.error_list]
- return {
- k: [
- ErrorDetail((error.message % error.params) if error.params else error.message,
- code=error.code if error.code else code)
- for error in errors
- ] for k, errors in error_dict.items()
- }
- class CreateOnlyDefault:
- """
- This class may be used to provide default values that are only used
- for create operations, but that do not return any value for update
- operations.
- """
- requires_context = True
- def __init__(self, default):
- self.default = default
- def __call__(self, serializer_field):
- is_update = serializer_field.parent.instance is not None
- if is_update:
- raise SkipField()
- if callable(self.default):
- if hasattr(self.default, 'set_context'):
- warnings.warn(
- "Method `set_context` on defaults is deprecated and will "
- "no longer be called starting with 3.13. Instead set "
- "`requires_context = True` on the class, and accept the "
- "context as an additional argument.",
- RemovedInDRF313Warning, stacklevel=2
- )
- self.default.set_context(self)
- if getattr(self.default, 'requires_context', False):
- return self.default(serializer_field)
- else:
- return self.default()
- return self.default
- def __repr__(self):
- return '%s(%s)' % (self.__class__.__name__, repr(self.default))
- class CurrentUserDefault:
- requires_context = True
- def __call__(self, serializer_field):
- return serializer_field.context['request'].user
- def __repr__(self):
- return '%s()' % self.__class__.__name__
- class SkipField(Exception):
- pass
- REGEX_TYPE = type(re.compile(''))
- NOT_READ_ONLY_WRITE_ONLY = 'May not set both `read_only` and `write_only`'
- NOT_READ_ONLY_REQUIRED = 'May not set both `read_only` and `required`'
- NOT_REQUIRED_DEFAULT = 'May not set both `required` and `default`'
- USE_READONLYFIELD = 'Field(read_only=True) should be ReadOnlyField'
- MISSING_ERROR_MESSAGE = (
- 'ValidationError raised by `{class_name}`, but error key `{key}` does '
- 'not exist in the `error_messages` dictionary.'
- )
- class Field:
- _creation_counter = 0
- default_error_messages = {
- 'required': _('This field is required.'),
- 'null': _('This field may not be null.')
- }
- default_validators = []
- default_empty_html = empty
- initial = None
- def __init__(self, *, read_only=False, write_only=False,
- required=None, default=empty, initial=empty, source=None,
- label=None, help_text=None, style=None,
- error_messages=None, validators=None, allow_null=False):
- self._creation_counter = Field._creation_counter
- Field._creation_counter += 1
- # If `required` is unset, then use `True` unless a default is provided.
- if required is None:
- required = default is empty and not read_only
- # Some combinations of keyword arguments do not make sense.
- assert not (read_only and write_only), NOT_READ_ONLY_WRITE_ONLY
- assert not (read_only and required), NOT_READ_ONLY_REQUIRED
- assert not (required and default is not empty), NOT_REQUIRED_DEFAULT
- assert not (read_only and self.__class__ == Field), USE_READONLYFIELD
- self.read_only = read_only
- self.write_only = write_only
- self.required = required
- self.default = default
- self.source = source
- self.initial = self.initial if (initial is empty) else initial
- self.label = label
- self.help_text = help_text
- self.style = {} if style is None else style
- self.allow_null = allow_null
- if self.default_empty_html is not empty:
- if default is not empty:
- self.default_empty_html = default
- if validators is not None:
- self.validators = list(validators)
- # These are set up by `.bind()` when the field is added to a serializer.
- self.field_name = None
- self.parent = None
- # Collect default error message from self and parent classes
- messages = {}
- for cls in reversed(self.__class__.__mro__):
- messages.update(getattr(cls, 'default_error_messages', {}))
- messages.update(error_messages or {})
- self.error_messages = messages
- def bind(self, field_name, parent):
- """
- Initializes the field name and parent for the field instance.
- Called when a field is added to the parent serializer instance.
- """
- # In order to enforce a consistent style, we error if a redundant
- # 'source' argument has been used. For example:
- # my_field = serializer.CharField(source='my_field')
- assert self.source != field_name, (
- "It is redundant to specify `source='%s'` on field '%s' in "
- "serializer '%s', because it is the same as the field name. "
- "Remove the `source` keyword argument." %
- (field_name, self.__class__.__name__, parent.__class__.__name__)
- )
- self.field_name = field_name
- self.parent = parent
- # `self.label` should default to being based on the field name.
- if self.label is None:
- self.label = field_name.replace('_', ' ').capitalize()
- # self.source should default to being the same as the field name.
- if self.source is None:
- self.source = field_name
- # self.source_attrs is a list of attributes that need to be looked up
- # when serializing the instance, or populating the validated data.
- if self.source == '*':
- self.source_attrs = []
- else:
- self.source_attrs = self.source.split('.')
- # .validators is a lazily loaded property, that gets its default
- # value from `get_validators`.
- @property
- def validators(self):
- if not hasattr(self, '_validators'):
- self._validators = self.get_validators()
- return self._validators
- @validators.setter
- def validators(self, validators):
- self._validators = validators
- def get_validators(self):
- return list(self.default_validators)
- def get_initial(self):
- """
- Return a value to use when the field is being returned as a primitive
- value, without any object instance.
- """
- if callable(self.initial):
- return self.initial()
- return self.initial
- def get_value(self, dictionary):
- """
- Given the *incoming* primitive data, return the value for this field
- that should be validated and transformed to a native value.
- """
- if html.is_html_input(dictionary):
- # HTML forms will represent empty fields as '', and cannot
- # represent None or False values directly.
- if self.field_name not in dictionary:
- if getattr(self.root, 'partial', False):
- return empty
- return self.default_empty_html
- ret = dictionary[self.field_name]
- if ret == '' and self.allow_null:
- # If the field is blank, and null is a valid value then
- # determine if we should use null instead.
- return '' if getattr(self, 'allow_blank', False) else None
- elif ret == '' and not self.required:
- # If the field is blank, and emptiness is valid then
- # determine if we should use emptiness instead.
- return '' if getattr(self, 'allow_blank', False) else empty
- return ret
- return dictionary.get(self.field_name, empty)
- def get_attribute(self, instance):
- """
- Given the *outgoing* object instance, return the primitive value
- that should be used for this field.
- """
- try:
- return get_attribute(instance, self.source_attrs)
- except BuiltinSignatureError as exc:
- msg = (
- 'Field source for `{serializer}.{field}` maps to a built-in '
- 'function type and is invalid. Define a property or method on '
- 'the `{instance}` instance that wraps the call to the built-in '
- 'function.'.format(
- serializer=self.parent.__class__.__name__,
- field=self.field_name,
- instance=instance.__class__.__name__,
- )
- )
- raise type(exc)(msg)
- except (KeyError, AttributeError) as exc:
- if self.default is not empty:
- return self.get_default()
- if self.allow_null:
- return None
- if not self.required:
- raise SkipField()
- msg = (
- 'Got {exc_type} when attempting to get a value for field '
- '`{field}` on serializer `{serializer}`.\nThe serializer '
- 'field might be named incorrectly and not match '
- 'any attribute or key on the `{instance}` instance.\n'
- 'Original exception text was: {exc}.'.format(
- exc_type=type(exc).__name__,
- field=self.field_name,
- serializer=self.parent.__class__.__name__,
- instance=instance.__class__.__name__,
- exc=exc
- )
- )
- raise type(exc)(msg)
- def get_default(self):
- """
- Return the default value to use when validating data if no input
- is provided for this field.
- If a default has not been set for this field then this will simply
- raise `SkipField`, indicating that no value should be set in the
- validated data for this field.
- """
- if self.default is empty or getattr(self.root, 'partial', False):
- # No default, or this is a partial update.
- raise SkipField()
- if callable(self.default):
- if hasattr(self.default, 'set_context'):
- warnings.warn(
- "Method `set_context` on defaults is deprecated and will "
- "no longer be called starting with 3.13. Instead set "
- "`requires_context = True` on the class, and accept the "
- "context as an additional argument.",
- RemovedInDRF313Warning, stacklevel=2
- )
- self.default.set_context(self)
- if getattr(self.default, 'requires_context', False):
- return self.default(self)
- else:
- return self.default()
- return self.default
- def validate_empty_values(self, data):
- """
- Validate empty values, and either:
- * Raise `ValidationError`, indicating invalid data.
- * Raise `SkipField`, indicating that the field should be ignored.
- * Return (True, data), indicating an empty value that should be
- returned without any further validation being applied.
- * Return (False, data), indicating a non-empty value, that should
- have validation applied as normal.
- """
- if self.read_only:
- return (True, self.get_default())
- if data is empty:
- if getattr(self.root, 'partial', False):
- raise SkipField()
- if self.required:
- self.fail('required')
- return (True, self.get_default())
- if data is None:
- if not self.allow_null:
- self.fail('null')
- # Nullable `source='*'` fields should not be skipped when its named
- # field is given a null value. This is because `source='*'` means
- # the field is passed the entire object, which is not null.
- elif self.source == '*':
- return (False, None)
- return (True, None)
- return (False, data)
- def run_validation(self, data=empty):
- """
- Validate a simple representation and return the internal value.
- The provided data may be `empty` if no representation was included
- in the input.
- May raise `SkipField` if the field should not be included in the
- validated data.
- """
- (is_empty_value, data) = self.validate_empty_values(data)
- if is_empty_value:
- return data
- value = self.to_internal_value(data)
- self.run_validators(value)
- return value
- def run_validators(self, value):
- """
- Test the given value against all the validators on the field,
- and either raise a `ValidationError` or simply return.
- """
- errors = []
- for validator in self.validators:
- if hasattr(validator, 'set_context'):
- warnings.warn(
- "Method `set_context` on validators is deprecated and will "
- "no longer be called starting with 3.13. Instead set "
- "`requires_context = True` on the class, and accept the "
- "context as an additional argument.",
- RemovedInDRF313Warning, stacklevel=2
- )
- validator.set_context(self)
- try:
- if getattr(validator, 'requires_context', False):
- validator(value, self)
- else:
- validator(value)
- except ValidationError as exc:
- # If the validation error contains a mapping of fields to
- # errors then simply raise it immediately rather than
- # attempting to accumulate a list of errors.
- if isinstance(exc.detail, dict):
- raise
- errors.extend(exc.detail)
- except DjangoValidationError as exc:
- errors.extend(get_error_detail(exc))
- if errors:
- raise ValidationError(errors)
- def to_internal_value(self, data):
- """
- Transform the *incoming* primitive data into a native value.
- """
- raise NotImplementedError(
- '{cls}.to_internal_value() must be implemented for field '
- '{field_name}. If you do not need to support write operations '
- 'you probably want to subclass `ReadOnlyField` instead.'.format(
- cls=self.__class__.__name__,
- field_name=self.field_name,
- )
- )
- def to_representation(self, value):
- """
- Transform the *outgoing* native value into primitive data.
- """
- raise NotImplementedError(
- '{cls}.to_representation() must be implemented for field {field_name}.'.format(
- cls=self.__class__.__name__,
- field_name=self.field_name,
- )
- )
- def fail(self, key, **kwargs):
- """
- A helper method that simply raises a validation error.
- """
- try:
- msg = self.error_messages[key]
- except KeyError:
- class_name = self.__class__.__name__
- msg = MISSING_ERROR_MESSAGE.format(class_name=class_name, key=key)
- raise AssertionError(msg)
- message_string = msg.format(**kwargs)
- raise ValidationError(message_string, code=key)
- @property
- def root(self):
- """
- Returns the top-level serializer for this field.
- """
- root = self
- while root.parent is not None:
- root = root.parent
- return root
- @property
- def context(self):
- """
- Returns the context as passed to the root serializer on initialization.
- """
- return getattr(self.root, '_context', {})
- def __new__(cls, *args, **kwargs):
- """
- When a field is instantiated, we store the arguments that were used,
- so that we can present a helpful representation of the object.
- """
- instance = super().__new__(cls)
- instance._args = args
- instance._kwargs = kwargs
- return instance
- def __deepcopy__(self, memo):
- """
- When cloning fields we instantiate using the arguments it was
- originally created with, rather than copying the complete state.
- """
- # Treat regexes and validators as immutable.
- # See https://github.com/encode/django-rest-framework/issues/1954
- # and https://github.com/encode/django-rest-framework/pull/4489
- args = [
- copy.deepcopy(item) if not isinstance(item, REGEX_TYPE) else item
- for item in self._args
- ]
- kwargs = {
- key: (copy.deepcopy(value, memo) if (key not in ('validators', 'regex')) else value)
- for key, value in self._kwargs.items()
- }
- return self.__class__(*args, **kwargs)
- def __repr__(self):
- """
- Fields are represented using their initial calling arguments.
- This allows us to create descriptive representations for serializer
- instances that show all the declared fields on the serializer.
- """
- return representation.field_repr(self)
- # Boolean types...
- class BooleanField(Field):
- default_error_messages = {
- 'invalid': _('Must be a valid boolean.')
- }
- default_empty_html = False
- initial = False
- TRUE_VALUES = {
- 't', 'T',
- 'y', 'Y', 'yes', 'Yes', 'YES',
- 'true', 'True', 'TRUE',
- 'on', 'On', 'ON',
- '1', 1,
- True
- }
- FALSE_VALUES = {
- 'f', 'F',
- 'n', 'N', 'no', 'No', 'NO',
- 'false', 'False', 'FALSE',
- 'off', 'Off', 'OFF',
- '0', 0, 0.0,
- False
- }
- NULL_VALUES = {'null', 'Null', 'NULL', '', None}
- def to_internal_value(self, data):
- try:
- if data in self.TRUE_VALUES:
- return True
- elif data in self.FALSE_VALUES:
- return False
- elif data in self.NULL_VALUES and self.allow_null:
- return None
- except TypeError: # Input is an unhashable type
- pass
- self.fail('invalid', input=data)
- def to_representation(self, value):
- if value in self.TRUE_VALUES:
- return True
- elif value in self.FALSE_VALUES:
- return False
- if value in self.NULL_VALUES and self.allow_null:
- return None
- return bool(value)
- class NullBooleanField(BooleanField):
- initial = None
- def __init__(self, **kwargs):
- warnings.warn(
- "The `NullBooleanField` is deprecated and will be removed starting "
- "with 3.14. Instead use the `BooleanField` field and set "
- "`allow_null=True` which does the same thing.",
- RemovedInDRF314Warning, stacklevel=2
- )
- assert 'allow_null' not in kwargs, '`allow_null` is not a valid option.'
- kwargs['allow_null'] = True
- super().__init__(**kwargs)
- # String types...
- class CharField(Field):
- default_error_messages = {
- 'invalid': _('Not a valid string.'),
- 'blank': _('This field may not be blank.'),
- 'max_length': _('Ensure this field has no more than {max_length} characters.'),
- 'min_length': _('Ensure this field has at least {min_length} characters.'),
- }
- initial = ''
- def __init__(self, **kwargs):
- self.allow_blank = kwargs.pop('allow_blank', False)
- self.trim_whitespace = kwargs.pop('trim_whitespace', True)
- self.max_length = kwargs.pop('max_length', None)
- self.min_length = kwargs.pop('min_length', None)
- super().__init__(**kwargs)
- if self.max_length is not None:
- message = lazy_format(self.error_messages['max_length'], max_length=self.max_length)
- self.validators.append(
- MaxLengthValidator(self.max_length, message=message))
- if self.min_length is not None:
- message = lazy_format(self.error_messages['min_length'], min_length=self.min_length)
- self.validators.append(
- MinLengthValidator(self.min_length, message=message))
- self.validators.append(ProhibitNullCharactersValidator())
- self.validators.append(ProhibitSurrogateCharactersValidator())
- def run_validation(self, data=empty):
- # Test for the empty string here so that it does not get validated,
- # and so that subclasses do not need to handle it explicitly
- # inside the `to_internal_value()` method.
- if data == '' or (self.trim_whitespace and str(data).strip() == ''):
- if not self.allow_blank:
- self.fail('blank')
- return ''
- return super().run_validation(data)
- def to_internal_value(self, data):
- # We're lenient with allowing basic numerics to be coerced into strings,
- # but other types should fail. Eg. unclear if booleans should represent as `true` or `True`,
- # and composites such as lists are likely user error.
- if isinstance(data, bool) or not isinstance(data, (str, int, float,)):
- self.fail('invalid')
- value = str(data)
- return value.strip() if self.trim_whitespace else value
- def to_representation(self, value):
- return str(value)
- class EmailField(CharField):
- default_error_messages = {
- 'invalid': _('Enter a valid email address.')
- }
- def __init__(self, **kwargs):
- super().__init__(**kwargs)
- validator = EmailValidator(message=self.error_messages['invalid'])
- self.validators.append(validator)
- class RegexField(CharField):
- default_error_messages = {
- 'invalid': _('This value does not match the required pattern.')
- }
- def __init__(self, regex, **kwargs):
- super().__init__(**kwargs)
- validator = RegexValidator(regex, message=self.error_messages['invalid'])
- self.validators.append(validator)
- class SlugField(CharField):
- default_error_messages = {
- 'invalid': _('Enter a valid "slug" consisting of letters, numbers, underscores or hyphens.'),
- 'invalid_unicode': _('Enter a valid "slug" consisting of Unicode letters, numbers, underscores, or hyphens.')
- }
- def __init__(self, allow_unicode=False, **kwargs):
- super().__init__(**kwargs)
- self.allow_unicode = allow_unicode
- if self.allow_unicode:
- validator = RegexValidator(re.compile(r'^[-\w]+\Z', re.UNICODE), message=self.error_messages['invalid_unicode'])
- else:
- validator = RegexValidator(re.compile(r'^[-a-zA-Z0-9_]+$'), message=self.error_messages['invalid'])
- self.validators.append(validator)
- class URLField(CharField):
- default_error_messages = {
- 'invalid': _('Enter a valid URL.')
- }
- def __init__(self, **kwargs):
- super().__init__(**kwargs)
- validator = URLValidator(message=self.error_messages['invalid'])
- self.validators.append(validator)
- class UUIDField(Field):
- valid_formats = ('hex_verbose', 'hex', 'int', 'urn')
- default_error_messages = {
- 'invalid': _('Must be a valid UUID.'),
- }
- def __init__(self, **kwargs):
- self.uuid_format = kwargs.pop('format', 'hex_verbose')
- if self.uuid_format not in self.valid_formats:
- raise ValueError(
- 'Invalid format for uuid representation. '
- 'Must be one of "{}"'.format('", "'.join(self.valid_formats))
- )
- super().__init__(**kwargs)
- def to_internal_value(self, data):
- if not isinstance(data, uuid.UUID):
- try:
- if isinstance(data, int):
- return uuid.UUID(int=data)
- elif isinstance(data, str):
- return uuid.UUID(hex=data)
- else:
- self.fail('invalid', value=data)
- except (ValueError):
- self.fail('invalid', value=data)
- return data
- def to_representation(self, value):
- if self.uuid_format == 'hex_verbose':
- return str(value)
- else:
- return getattr(value, self.uuid_format)
- class IPAddressField(CharField):
- """Support both IPAddressField and GenericIPAddressField"""
- default_error_messages = {
- 'invalid': _('Enter a valid IPv4 or IPv6 address.'),
- }
- def __init__(self, protocol='both', **kwargs):
- self.protocol = protocol.lower()
- self.unpack_ipv4 = (self.protocol == 'both')
- super().__init__(**kwargs)
- validators, error_message = ip_address_validators(protocol, self.unpack_ipv4)
- self.validators.extend(validators)
- def to_internal_value(self, data):
- if not isinstance(data, str):
- self.fail('invalid', value=data)
- if ':' in data:
- try:
- if self.protocol in ('both', 'ipv6'):
- return clean_ipv6_address(data, self.unpack_ipv4)
- except DjangoValidationError:
- self.fail('invalid', value=data)
- return super().to_internal_value(data)
- # Number types...
- class IntegerField(Field):
- default_error_messages = {
- 'invalid': _('A valid integer is required.'),
- 'max_value': _('Ensure this value is less than or equal to {max_value}.'),
- 'min_value': _('Ensure this value is greater than or equal to {min_value}.'),
- 'max_string_length': _('String value too large.')
- }
- MAX_STRING_LENGTH = 1000 # Guard against malicious string inputs.
- re_decimal = re.compile(r'\.0*\s*$') # allow e.g. '1.0' as an int, but not '1.2'
- def __init__(self, **kwargs):
- self.max_value = kwargs.pop('max_value', None)
- self.min_value = kwargs.pop('min_value', None)
- super().__init__(**kwargs)
- if self.max_value is not None:
- message = lazy_format(self.error_messages['max_value'], max_value=self.max_value)
- self.validators.append(
- MaxValueValidator(self.max_value, message=message))
- if self.min_value is not None:
- message = lazy_format(self.error_messages['min_value'], min_value=self.min_value)
- self.validators.append(
- MinValueValidator(self.min_value, message=message))
- def to_internal_value(self, data):
- if isinstance(data, str) and len(data) > self.MAX_STRING_LENGTH:
- self.fail('max_string_length')
- try:
- data = int(self.re_decimal.sub('', str(data)))
- except (ValueError, TypeError):
- self.fail('invalid')
- return data
- def to_representation(self, value):
- return int(value)
- class FloatField(Field):
- default_error_messages = {
- 'invalid': _('A valid number is required.'),
- 'max_value': _('Ensure this value is less than or equal to {max_value}.'),
- 'min_value': _('Ensure this value is greater than or equal to {min_value}.'),
- 'max_string_length': _('String value too large.')
- }
- MAX_STRING_LENGTH = 1000 # Guard against malicious string inputs.
- def __init__(self, **kwargs):
- self.max_value = kwargs.pop('max_value', None)
- self.min_value = kwargs.pop('min_value', None)
- super().__init__(**kwargs)
- if self.max_value is not None:
- message = lazy_format(self.error_messages['max_value'], max_value=self.max_value)
- self.validators.append(
- MaxValueValidator(self.max_value, message=message))
- if self.min_value is not None:
- message = lazy_format(self.error_messages['min_value'], min_value=self.min_value)
- self.validators.append(
- MinValueValidator(self.min_value, message=message))
- def to_internal_value(self, data):
- if isinstance(data, str) and len(data) > self.MAX_STRING_LENGTH:
- self.fail('max_string_length')
- try:
- return float(data)
- except (TypeError, ValueError):
- self.fail('invalid')
- def to_representation(self, value):
- return float(value)
- class DecimalField(Field):
- default_error_messages = {
- 'invalid': _('A valid number is required.'),
- 'max_value': _('Ensure this value is less than or equal to {max_value}.'),
- 'min_value': _('Ensure this value is greater than or equal to {min_value}.'),
- 'max_digits': _('Ensure that there are no more than {max_digits} digits in total.'),
- 'max_decimal_places': _('Ensure that there are no more than {max_decimal_places} decimal places.'),
- 'max_whole_digits': _('Ensure that there are no more than {max_whole_digits} digits before the decimal point.'),
- 'max_string_length': _('String value too large.')
- }
- MAX_STRING_LENGTH = 1000 # Guard against malicious string inputs.
- def __init__(self, max_digits, decimal_places, coerce_to_string=None, max_value=None, min_value=None,
- localize=False, rounding=None, **kwargs):
- self.max_digits = max_digits
- self.decimal_places = decimal_places
- self.localize = localize
- if coerce_to_string is not None:
- self.coerce_to_string = coerce_to_string
- if self.localize:
- self.coerce_to_string = True
- self.max_value = max_value
- self.min_value = min_value
- if self.max_digits is not None and self.decimal_places is not None:
- self.max_whole_digits = self.max_digits - self.decimal_places
- else:
- self.max_whole_digits = None
- super().__init__(**kwargs)
- if self.max_value is not None:
- message = lazy_format(self.error_messages['max_value'], max_value=self.max_value)
- self.validators.append(
- MaxValueValidator(self.max_value, message=message))
- if self.min_value is not None:
- message = lazy_format(self.error_messages['min_value'], min_value=self.min_value)
- self.validators.append(
- MinValueValidator(self.min_value, message=message))
- if rounding is not None:
- valid_roundings = [v for k, v in vars(decimal).items() if k.startswith('ROUND_')]
- assert rounding in valid_roundings, (
- 'Invalid rounding option %s. Valid values for rounding are: %s' % (rounding, valid_roundings))
- self.rounding = rounding
- def validate_empty_values(self, data):
- if smart_str(data).strip() == '' and self.allow_null:
- return (True, None)
- return super().validate_empty_values(data)
- def to_internal_value(self, data):
- """
- Validate that the input is a decimal number and return a Decimal
- instance.
- """
- data = smart_str(data).strip()
- if self.localize:
- data = sanitize_separators(data)
- if len(data) > self.MAX_STRING_LENGTH:
- self.fail('max_string_length')
- try:
- value = decimal.Decimal(data)
- except decimal.DecimalException:
- self.fail('invalid')
- if value.is_nan():
- self.fail('invalid')
- # Check for infinity and negative infinity.
- if value in (decimal.Decimal('Inf'), decimal.Decimal('-Inf')):
- self.fail('invalid')
- return self.quantize(self.validate_precision(value))
- def validate_precision(self, value):
- """
- Ensure that there are no more than max_digits in the number, and no
- more than decimal_places digits after the decimal point.
- Override this method to disable the precision validation for input
- values or to enhance it in any way you need to.
- """
- sign, digittuple, exponent = value.as_tuple()
- if exponent >= 0:
- # 1234500.0
- total_digits = len(digittuple) + exponent
- whole_digits = total_digits
- decimal_places = 0
- elif len(digittuple) > abs(exponent):
- # 123.45
- total_digits = len(digittuple)
- whole_digits = total_digits - abs(exponent)
- decimal_places = abs(exponent)
- else:
- # 0.001234
- total_digits = abs(exponent)
- whole_digits = 0
- decimal_places = total_digits
- if self.max_digits is not None and total_digits > self.max_digits:
- self.fail('max_digits', max_digits=self.max_digits)
- if self.decimal_places is not None and decimal_places > self.decimal_places:
- self.fail('max_decimal_places', max_decimal_places=self.decimal_places)
- if self.max_whole_digits is not None and whole_digits > self.max_whole_digits:
- self.fail('max_whole_digits', max_whole_digits=self.max_whole_digits)
- return value
- def to_representation(self, value):
- coerce_to_string = getattr(self, 'coerce_to_string', api_settings.COERCE_DECIMAL_TO_STRING)
- if value is None:
- if coerce_to_string:
- return ''
- else:
- return None
- if not isinstance(value, decimal.Decimal):
- value = decimal.Decimal(str(value).strip())
- quantized = self.quantize(value)
- if not coerce_to_string:
- return quantized
- if self.localize:
- return localize_input(quantized)
- return '{:f}'.format(quantized)
- def quantize(self, value):
- """
- Quantize the decimal value to the configured precision.
- """
- if self.decimal_places is None:
- return value
- context = decimal.getcontext().copy()
- if self.max_digits is not None:
- context.prec = self.max_digits
- return value.quantize(
- decimal.Decimal('.1') ** self.decimal_places,
- rounding=self.rounding,
- context=context
- )
- # Date & time fields...
- class DateTimeField(Field):
- default_error_messages = {
- 'invalid': _('Datetime has wrong format. Use one of these formats instead: {format}.'),
- 'date': _('Expected a datetime but got a date.'),
- 'make_aware': _('Invalid datetime for the timezone "{timezone}".'),
- 'overflow': _('Datetime value out of range.')
- }
- datetime_parser = datetime.datetime.strptime
- def __init__(self, format=empty, input_formats=None, default_timezone=None, **kwargs):
- if format is not empty:
- self.format = format
- if input_formats is not None:
- self.input_formats = input_formats
- if default_timezone is not None:
- self.timezone = default_timezone
- super().__init__(**kwargs)
- def enforce_timezone(self, value):
- """
- When `self.default_timezone` is `None`, always return naive datetimes.
- When `self.default_timezone` is not `None`, always return aware datetimes.
- """
- field_timezone = getattr(self, 'timezone', self.default_timezone())
- if field_timezone is not None:
- if timezone.is_aware(value):
- try:
- return value.astimezone(field_timezone)
- except OverflowError:
- self.fail('overflow')
- try:
- return timezone.make_aware(value, field_timezone)
- except InvalidTimeError:
- self.fail('make_aware', timezone=field_timezone)
- elif (field_timezone is None) and timezone.is_aware(value):
- return timezone.make_naive(value, utc)
- return value
- def default_timezone(self):
- return timezone.get_current_timezone() if settings.USE_TZ else None
- def to_internal_value(self, value):
- input_formats = getattr(self, 'input_formats', api_settings.DATETIME_INPUT_FORMATS)
- if isinstance(value, datetime.date) and not isinstance(value, datetime.datetime):
- self.fail('date')
- if isinstance(value, datetime.datetime):
- return self.enforce_timezone(value)
- for input_format in input_formats:
- if input_format.lower() == ISO_8601:
- try:
- parsed = parse_datetime(value)
- if parsed is not None:
- return self.enforce_timezone(parsed)
- except (ValueError, TypeError):
- pass
- else:
- try:
- parsed = self.datetime_parser(value, input_format)
- return self.enforce_timezone(parsed)
- except (ValueError, TypeError):
- pass
- humanized_format = humanize_datetime.datetime_formats(input_formats)
- self.fail('invalid', format=humanized_format)
- def to_representation(self, value):
- if not value:
- return None
- output_format = getattr(self, 'format', api_settings.DATETIME_FORMAT)
- if output_format is None or isinstance(value, str):
- return value
- value = self.enforce_timezone(value)
- if output_format.lower() == ISO_8601:
- value = value.isoformat()
- if value.endswith('+00:00'):
- value = value[:-6] + 'Z'
- return value
- return value.strftime(output_format)
- class DateField(Field):
- default_error_messages = {
- 'invalid': _('Date has wrong format. Use one of these formats instead: {format}.'),
- 'datetime': _('Expected a date but got a datetime.'),
- }
- datetime_parser = datetime.datetime.strptime
- def __init__(self, format=empty, input_formats=None, **kwargs):
- if format is not empty:
- self.format = format
- if input_formats is not None:
- self.input_formats = input_formats
- super().__init__(**kwargs)
- def to_internal_value(self, value):
- input_formats = getattr(self, 'input_formats', api_settings.DATE_INPUT_FORMATS)
- if isinstance(value, datetime.datetime):
- self.fail('datetime')
- if isinstance(value, datetime.date):
- return value
- for input_format in input_formats:
- if input_format.lower() == ISO_8601:
- try:
- parsed = parse_date(value)
- except (ValueError, TypeError):
- pass
- else:
- if parsed is not None:
- return parsed
- else:
- try:
- parsed = self.datetime_parser(value, input_format)
- except (ValueError, TypeError):
- pass
- else:
- return parsed.date()
- humanized_format = humanize_datetime.date_formats(input_formats)
- self.fail('invalid', format=humanized_format)
- def to_representation(self, value):
- if not value:
- return None
- output_format = getattr(self, 'format', api_settings.DATE_FORMAT)
- if output_format is None or isinstance(value, str):
- return value
- # Applying a `DateField` to a datetime value is almost always
- # not a sensible thing to do, as it means naively dropping
- # any explicit or implicit timezone info.
- assert not isinstance(value, datetime.datetime), (
- 'Expected a `date`, but got a `datetime`. Refusing to coerce, '
- 'as this may mean losing timezone information. Use a custom '
- 'read-only field and deal with timezone issues explicitly.'
- )
- if output_format.lower() == ISO_8601:
- return value.isoformat()
- return value.strftime(output_format)
- class TimeField(Field):
- default_error_messages = {
- 'invalid': _('Time has wrong format. Use one of these formats instead: {format}.'),
- }
- datetime_parser = datetime.datetime.strptime
- def __init__(self, format=empty, input_formats=None, **kwargs):
- if format is not empty:
- self.format = format
- if input_formats is not None:
- self.input_formats = input_formats
- super().__init__(**kwargs)
- def to_internal_value(self, value):
- input_formats = getattr(self, 'input_formats', api_settings.TIME_INPUT_FORMATS)
- if isinstance(value, datetime.time):
- return value
- for input_format in input_formats:
- if input_format.lower() == ISO_8601:
- try:
- parsed = parse_time(value)
- except (ValueError, TypeError):
- pass
- else:
- if parsed is not None:
- return parsed
- else:
- try:
- parsed = self.datetime_parser(value, input_format)
- except (ValueError, TypeError):
- pass
- else:
- return parsed.time()
- humanized_format = humanize_datetime.time_formats(input_formats)
- self.fail('invalid', format=humanized_format)
- def to_representation(self, value):
- if value in (None, ''):
- return None
- output_format = getattr(self, 'format', api_settings.TIME_FORMAT)
- if output_format is None or isinstance(value, str):
- return value
- # Applying a `TimeField` to a datetime value is almost always
- # not a sensible thing to do, as it means naively dropping
- # any explicit or implicit timezone info.
- assert not isinstance(value, datetime.datetime), (
- 'Expected a `time`, but got a `datetime`. Refusing to coerce, '
- 'as this may mean losing timezone information. Use a custom '
- 'read-only field and deal with timezone issues explicitly.'
- )
- if output_format.lower() == ISO_8601:
- return value.isoformat()
- return value.strftime(output_format)
- class DurationField(Field):
- default_error_messages = {
- 'invalid': _('Duration has wrong format. Use one of these formats instead: {format}.'),
- 'max_value': _('Ensure this value is less than or equal to {max_value}.'),
- 'min_value': _('Ensure this value is greater than or equal to {min_value}.'),
- }
- def __init__(self, **kwargs):
- self.max_value = kwargs.pop('max_value', None)
- self.min_value = kwargs.pop('min_value', None)
- super().__init__(**kwargs)
- if self.max_value is not None:
- message = lazy_format(self.error_messages['max_value'], max_value=self.max_value)
- self.validators.append(
- MaxValueValidator(self.max_value, message=message))
- if self.min_value is not None:
- message = lazy_format(self.error_messages['min_value'], min_value=self.min_value)
- self.validators.append(
- MinValueValidator(self.min_value, message=message))
- def to_internal_value(self, value):
- if isinstance(value, datetime.timedelta):
- return value
- parsed = parse_duration(str(value))
- if parsed is not None:
- return parsed
- self.fail('invalid', format='[DD] [HH:[MM:]]ss[.uuuuuu]')
- def to_representation(self, value):
- return duration_string(value)
- # Choice types...
- class ChoiceField(Field):
- default_error_messages = {
- 'invalid_choice': _('"{input}" is not a valid choice.')
- }
- html_cutoff = None
- html_cutoff_text = _('More than {count} items...')
- def __init__(self, choices, **kwargs):
- self.choices = choices
- self.html_cutoff = kwargs.pop('html_cutoff', self.html_cutoff)
- self.html_cutoff_text = kwargs.pop('html_cutoff_text', self.html_cutoff_text)
- self.allow_blank = kwargs.pop('allow_blank', False)
- super().__init__(**kwargs)
- def to_internal_value(self, data):
- if data == '' and self.allow_blank:
- return ''
- try:
- return self.choice_strings_to_values[str(data)]
- except KeyError:
- self.fail('invalid_choice', input=data)
- def to_representation(self, value):
- if value in ('', None):
- return value
- return self.choice_strings_to_values.get(str(value), value)
- def iter_options(self):
- """
- Helper method for use with templates rendering select widgets.
- """
- return iter_options(
- self.grouped_choices,
- cutoff=self.html_cutoff,
- cutoff_text=self.html_cutoff_text
- )
- def _get_choices(self):
- return self._choices
- def _set_choices(self, choices):
- self.grouped_choices = to_choices_dict(choices)
- self._choices = flatten_choices_dict(self.grouped_choices)
- # Map the string representation of choices to the underlying value.
- # Allows us to deal with eg. integer choices while supporting either
- # integer or string input, but still get the correct datatype out.
- self.choice_strings_to_values = {
- str(key): key for key in self.choices
- }
- choices = property(_get_choices, _set_choices)
- 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}".'),
- 'empty': _('This selection may not be empty.')
- }
- default_empty_html = []
- def __init__(self, **kwargs):
- self.allow_empty = kwargs.pop('allow_empty', True)
- super().__init__(**kwargs)
- def get_value(self, dictionary):
- if self.field_name not in dictionary:
- if getattr(self.root, 'partial', False):
- return empty
- # We override the default field access in order to support
- # lists in HTML forms.
- if html.is_html_input(dictionary):
- return dictionary.getlist(self.field_name)
- return dictionary.get(self.field_name, empty)
- def to_internal_value(self, data):
- if isinstance(data, str) or not hasattr(data, '__iter__'):
- self.fail('not_a_list', input_type=type(data).__name__)
- if not self.allow_empty and len(data) == 0:
- self.fail('empty')
- return {
- # Arguments for super() are needed because of scoping inside
- # comprehensions.
- super(MultipleChoiceField, self).to_internal_value(item)
- for item in data
- }
- def to_representation(self, value):
- return {
- self.choice_strings_to_values.get(str(item), item) for item in value
- }
- class FilePathField(ChoiceField):
- default_error_messages = {
- 'invalid_choice': _('"{input}" is not a valid path choice.')
- }
- def __init__(self, path, match=None, recursive=False, allow_files=True,
- allow_folders=False, required=None, **kwargs):
- # Defer to Django's FilePathField implementation to get the
- # valid set of choices.
- field = DjangoFilePathField(
- path, match=match, recursive=recursive, allow_files=allow_files,
- allow_folders=allow_folders, required=required
- )
- kwargs['choices'] = field.choices
- super().__init__(**kwargs)
- # File types...
- class FileField(Field):
- default_error_messages = {
- 'required': _('No file was submitted.'),
- 'invalid': _('The submitted data was not a file. Check the encoding type on the form.'),
- 'no_name': _('No filename could be determined.'),
- 'empty': _('The submitted file is empty.'),
- 'max_length': _('Ensure this filename has at most {max_length} characters (it has {length}).'),
- }
- def __init__(self, **kwargs):
- self.max_length = kwargs.pop('max_length', None)
- self.allow_empty_file = kwargs.pop('allow_empty_file', False)
- if 'use_url' in kwargs:
- self.use_url = kwargs.pop('use_url')
- super().__init__(**kwargs)
- def to_internal_value(self, data):
- try:
- # `UploadedFile` objects should have name and size attributes.
- file_name = data.name
- file_size = data.size
- except AttributeError:
- self.fail('invalid')
- if not file_name:
- self.fail('no_name')
- if not self.allow_empty_file and not file_size:
- self.fail('empty')
- if self.max_length and len(file_name) > self.max_length:
- self.fail('max_length', max_length=self.max_length, length=len(file_name))
- return data
- def to_representation(self, value):
- if not value:
- return None
- use_url = getattr(self, 'use_url', api_settings.UPLOADED_FILES_USE_URL)
- if use_url:
- try:
- url = value.url
- except AttributeError:
- return None
- request = self.context.get('request', None)
- if request is not None:
- return request.build_absolute_uri(url)
- return url
- return value.name
- class ImageField(FileField):
- default_error_messages = {
- 'invalid_image': _(
- 'Upload a valid image. The file you uploaded was either not an image or a corrupted image.'
- ),
- }
- def __init__(self, **kwargs):
- self._DjangoImageField = kwargs.pop('_DjangoImageField', DjangoImageField)
- super().__init__(**kwargs)
- def to_internal_value(self, data):
- # Image validation is a bit grungy, so we'll just outright
- # defer to Django's implementation so we don't need to
- # consider it, or treat PIL as a test dependency.
- file_object = super().to_internal_value(data)
- django_field = self._DjangoImageField()
- django_field.error_messages = self.error_messages
- return django_field.clean(file_object)
- # Composite field types...
- class _UnvalidatedField(Field):
- def __init__(self, **kwargs):
- super().__init__(**kwargs)
- self.allow_blank = True
- self.allow_null = True
- def to_internal_value(self, data):
- return data
- def to_representation(self, value):
- return value
- class ListField(Field):
- child = _UnvalidatedField()
- initial = []
- default_error_messages = {
- 'not_a_list': _('Expected a list of items but got type "{input_type}".'),
- 'empty': _('This list may not be empty.'),
- 'min_length': _('Ensure this field has at least {min_length} elements.'),
- 'max_length': _('Ensure this field has no more than {max_length} elements.')
- }
- def __init__(self, **kwargs):
- self.child = kwargs.pop('child', copy.deepcopy(self.child))
- self.allow_empty = kwargs.pop('allow_empty', True)
- self.max_length = kwargs.pop('max_length', None)
- self.min_length = kwargs.pop('min_length', None)
- assert not inspect.isclass(self.child), '`child` has not been instantiated.'
- assert self.child.source is None, (
- "The `source` argument is not meaningful when applied to a `child=` field. "
- "Remove `source=` from the field declaration."
- )
- super().__init__(**kwargs)
- self.child.bind(field_name='', parent=self)
- if self.max_length is not None:
- message = lazy_format(self.error_messages['max_length'], max_length=self.max_length)
- self.validators.append(MaxLengthValidator(self.max_length, message=message))
- if self.min_length is not None:
- message = lazy_format(self.error_messages['min_length'], min_length=self.min_length)
- self.validators.append(MinLengthValidator(self.min_length, message=message))
- def get_value(self, dictionary):
- if self.field_name not in dictionary:
- if getattr(self.root, 'partial', False):
- return empty
- # We override the default field access in order to support
- # lists in HTML forms.
- if html.is_html_input(dictionary):
- val = dictionary.getlist(self.field_name, [])
- if len(val) > 0:
- # Support QueryDict lists in HTML input.
- return val
- return html.parse_html_list(dictionary, prefix=self.field_name, default=empty)
- return dictionary.get(self.field_name, empty)
- def to_internal_value(self, data):
- """
- List of dicts of native values <- List of dicts of primitive datatypes.
- """
- if html.is_html_input(data):
- data = html.parse_html_list(data, default=[])
- if isinstance(data, (str, Mapping)) or not hasattr(data, '__iter__'):
- self.fail('not_a_list', input_type=type(data).__name__)
- if not self.allow_empty and len(data) == 0:
- self.fail('empty')
- return self.run_child_validation(data)
- def to_representation(self, data):
- """
- List of object instances -> List of dicts of primitive datatypes.
- """
- return [self.child.to_representation(item) if item is not None else None for item in data]
- def run_child_validation(self, data):
- result = []
- errors = OrderedDict()
- for idx, item in enumerate(data):
- try:
- result.append(self.child.run_validation(item))
- except ValidationError as e:
- errors[idx] = e.detail
- if not errors:
- return result
- raise ValidationError(errors)
- class DictField(Field):
- child = _UnvalidatedField()
- initial = {}
- default_error_messages = {
- 'not_a_dict': _('Expected a dictionary of items but got type "{input_type}".'),
- 'empty': _('This dictionary may not be empty.'),
- }
- def __init__(self, **kwargs):
- self.child = kwargs.pop('child', copy.deepcopy(self.child))
- self.allow_empty = kwargs.pop('allow_empty', True)
- assert not inspect.isclass(self.child), '`child` has not been instantiated.'
- assert self.child.source is None, (
- "The `source` argument is not meaningful when applied to a `child=` field. "
- "Remove `source=` from the field declaration."
- )
- super().__init__(**kwargs)
- self.child.bind(field_name='', parent=self)
- def get_value(self, dictionary):
- # We override the default field access in order to support
- # dictionaries in HTML forms.
- if html.is_html_input(dictionary):
- return html.parse_html_dict(dictionary, prefix=self.field_name)
- return dictionary.get(self.field_name, empty)
- def to_internal_value(self, data):
- """
- Dicts of native values <- Dicts of primitive datatypes.
- """
- if html.is_html_input(data):
- data = html.parse_html_dict(data)
- if not isinstance(data, dict):
- self.fail('not_a_dict', input_type=type(data).__name__)
- if not self.allow_empty and len(data) == 0:
- self.fail('empty')
- return self.run_child_validation(data)
- def to_representation(self, value):
- return {
- str(key): self.child.to_representation(val) if val is not None else None
- for key, val in value.items()
- }
- def run_child_validation(self, data):
- result = {}
- errors = OrderedDict()
- for key, value in data.items():
- key = str(key)
- try:
- result[key] = self.child.run_validation(value)
- except ValidationError as e:
- errors[key] = e.detail
- if not errors:
- return result
- raise ValidationError(errors)
- class HStoreField(DictField):
- child = CharField(allow_blank=True, allow_null=True)
- def __init__(self, **kwargs):
- super().__init__(**kwargs)
- assert isinstance(self.child, CharField), (
- "The `child` argument must be an instance of `CharField`, "
- "as the hstore extension stores values as strings."
- )
- class JSONField(Field):
- default_error_messages = {
- 'invalid': _('Value must be valid JSON.')
- }
- # Workaround for isinstance calls when importing the field isn't possible
- _is_jsonfield = True
- def __init__(self, **kwargs):
- self.binary = kwargs.pop('binary', False)
- self.encoder = kwargs.pop('encoder', None)
- self.decoder = kwargs.pop('decoder', None)
- super().__init__(**kwargs)
- def get_value(self, dictionary):
- if html.is_html_input(dictionary) and self.field_name in dictionary:
- # When HTML form input is used, mark up the input
- # as being a JSON string, rather than a JSON primitive.
- class JSONString(str):
- def __new__(cls, value):
- ret = str.__new__(cls, value)
- ret.is_json_string = True
- return ret
- return JSONString(dictionary[self.field_name])
- return dictionary.get(self.field_name, empty)
- def to_internal_value(self, data):
- try:
- if self.binary or getattr(data, 'is_json_string', False):
- if isinstance(data, bytes):
- data = data.decode()
- return json.loads(data, cls=self.decoder)
- else:
- json.dumps(data, cls=self.encoder)
- except (TypeError, ValueError):
- self.fail('invalid')
- return data
- def to_representation(self, value):
- if self.binary:
- value = json.dumps(value, cls=self.encoder)
- value = value.encode()
- return value
- # Miscellaneous field types...
- class ReadOnlyField(Field):
- """
- A read-only field that simply returns the field value.
- If the field is a method with no parameters, the method will be called
- and its return value used as the representation.
- For example, the following would call `get_expiry_date()` on the object:
- class ExampleSerializer(Serializer):
- expiry_date = ReadOnlyField(source='get_expiry_date')
- """
- def __init__(self, **kwargs):
- kwargs['read_only'] = True
- super().__init__(**kwargs)
- def to_representation(self, value):
- return value
- class HiddenField(Field):
- """
- A hidden field does not take input from the user, or present any output,
- but it does populate a field in `validated_data`, based on its default
- value. This is particularly useful when we have a `unique_for_date`
- constraint on a pair of fields, as we need some way to include the date in
- the validated data.
- """
- def __init__(self, **kwargs):
- assert 'default' in kwargs, 'default is a required argument.'
- kwargs['write_only'] = True
- super().__init__(**kwargs)
- def get_value(self, dictionary):
- # We always use the default value for `HiddenField`.
- # User input is never provided or accepted.
- return empty
- def to_internal_value(self, data):
- return data
- class SerializerMethodField(Field):
- """
- A read-only field that get its representation from calling a method on the
- parent serializer class. The method called will be of the form
- "get_{field_name}", and should take a single argument, which is the
- object being serialized.
- For example:
- class ExampleSerializer(self):
- extra_info = SerializerMethodField()
- def get_extra_info(self, obj):
- return ... # Calculate some data to return.
- """
- def __init__(self, method_name=None, **kwargs):
- self.method_name = method_name
- kwargs['source'] = '*'
- kwargs['read_only'] = True
- super().__init__(**kwargs)
- def bind(self, field_name, parent):
- # The method name defaults to `get_{field_name}`.
- if self.method_name is None:
- self.method_name = 'get_{field_name}'.format(field_name=field_name)
- super().bind(field_name, parent)
- def to_representation(self, value):
- method = getattr(self.parent, self.method_name)
- return method(value)
- class ModelField(Field):
- """
- A generic field that can be used against an arbitrary model field.
- This is used by `ModelSerializer` when dealing with custom model fields,
- that do not have a serializer field to be mapped to.
- """
- default_error_messages = {
- 'max_length': _('Ensure this field has no more than {max_length} characters.'),
- }
- def __init__(self, model_field, **kwargs):
- self.model_field = model_field
- # The `max_length` option is supported by Django's base `Field` class,
- # so we'd better support it here.
- self.max_length = kwargs.pop('max_length', None)
- super().__init__(**kwargs)
- if self.max_length is not None:
- message = lazy_format(self.error_messages['max_length'], max_length=self.max_length)
- self.validators.append(
- MaxLengthValidator(self.max_length, message=message))
- def to_internal_value(self, data):
- rel = self.model_field.remote_field
- if rel is not None:
- return rel.model._meta.get_field(rel.field_name).to_python(data)
- return self.model_field.to_python(data)
- def get_attribute(self, obj):
- # We pass the object instance onto `to_representation`,
- # not just the field attribute.
- return obj
- def to_representation(self, obj):
- value = self.model_field.value_from_object(obj)
- if is_protected_type(value):
- return value
- return self.model_field.value_to_string(obj)
|