2014-09-09 20:46:28 +04:00
|
|
|
from django.conf import settings
|
2014-09-08 17:24:05 +04:00
|
|
|
from django.core import validators
|
|
|
|
from django.core.exceptions import ValidationError
|
2014-09-09 20:46:28 +04:00
|
|
|
from django.utils import timezone
|
2014-09-25 13:49:25 +04:00
|
|
|
from django.utils.datastructures import SortedDict
|
2014-09-09 20:46:28 +04:00
|
|
|
from django.utils.dateparse import parse_date, parse_datetime, parse_time
|
2014-09-08 17:24:05 +04:00
|
|
|
from django.utils.encoding import is_protected_type
|
2014-09-09 20:46:28 +04:00
|
|
|
from django.utils.translation import ugettext_lazy as _
|
|
|
|
from rest_framework import ISO_8601
|
2014-09-22 19:45:06 +04:00
|
|
|
from rest_framework.compat import smart_text, EmailValidator, MinValueValidator, MaxValueValidator, URLValidator
|
2014-09-09 20:46:28 +04:00
|
|
|
from rest_framework.settings import api_settings
|
|
|
|
from rest_framework.utils import html, representation, humanize_datetime
|
2014-09-24 23:25:59 +04:00
|
|
|
import copy
|
2014-09-09 20:46:28 +04:00
|
|
|
import datetime
|
|
|
|
import decimal
|
2014-09-02 20:41:23 +04:00
|
|
|
import inspect
|
2014-09-22 16:26:47 +04:00
|
|
|
import re
|
2013-04-25 15:47:34 +04:00
|
|
|
|
2012-11-22 03:20:49 +04:00
|
|
|
|
2014-08-29 19:46:26 +04:00
|
|
|
class empty:
|
2012-09-20 16:06:27 +04:00
|
|
|
"""
|
2014-08-29 19:46:26 +04:00
|
|
|
This class is used to represent no data being provided for a given input
|
|
|
|
or output value.
|
2013-03-13 15:42:12 +04:00
|
|
|
|
2014-08-29 19:46:26 +04:00
|
|
|
It is required because `None` may be a valid input or output value.
|
|
|
|
"""
|
|
|
|
pass
|
2012-09-20 16:06:27 +04:00
|
|
|
|
2013-05-25 02:44:23 +04:00
|
|
|
|
2014-09-02 20:41:23 +04:00
|
|
|
def is_simple_callable(obj):
|
|
|
|
"""
|
|
|
|
True if the object is a callable that takes no arguments.
|
|
|
|
"""
|
|
|
|
function = inspect.isfunction(obj)
|
|
|
|
method = inspect.ismethod(obj)
|
|
|
|
|
|
|
|
if not (function or method):
|
|
|
|
return False
|
|
|
|
|
|
|
|
args, _, _, defaults = inspect.getargspec(obj)
|
|
|
|
len_args = len(args) if function else len(args) - 1
|
|
|
|
len_defaults = len(defaults) if defaults else 0
|
|
|
|
return len_args <= len_defaults
|
|
|
|
|
|
|
|
|
2014-08-29 19:46:26 +04:00
|
|
|
def get_attribute(instance, attrs):
|
2013-02-07 16:57:40 +04:00
|
|
|
"""
|
2014-08-29 19:46:26 +04:00
|
|
|
Similar to Python's built in `getattr(instance, attr)`,
|
|
|
|
but takes a list of nested attributes, instead of a single attribute.
|
2014-09-08 17:24:05 +04:00
|
|
|
|
|
|
|
Also accepts either attribute lookup on objects or dictionary lookups.
|
2013-02-07 16:57:40 +04:00
|
|
|
"""
|
2014-08-29 19:46:26 +04:00
|
|
|
for attr in attrs:
|
2014-09-08 17:24:05 +04:00
|
|
|
try:
|
|
|
|
instance = getattr(instance, attr)
|
2014-09-11 16:20:44 +04:00
|
|
|
except AttributeError as exc:
|
|
|
|
try:
|
|
|
|
return instance[attr]
|
2014-09-26 13:46:52 +04:00
|
|
|
except (KeyError, TypeError, AttributeError):
|
2014-09-11 16:20:44 +04:00
|
|
|
raise exc
|
2014-08-29 19:46:26 +04:00
|
|
|
return instance
|
2013-03-06 16:19:39 +04:00
|
|
|
|
|
|
|
|
2014-08-29 19:46:26 +04:00
|
|
|
def set_value(dictionary, keys, value):
|
2013-07-04 09:51:24 +04:00
|
|
|
"""
|
2014-08-29 19:46:26 +04:00
|
|
|
Similar to Python's built in `dictionary[key] = value`,
|
|
|
|
but takes a list of nested keys instead of a single key.
|
2013-07-04 09:51:24 +04:00
|
|
|
|
2014-08-29 19:46:26 +04:00
|
|
|
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}}
|
2013-07-04 09:51:24 +04:00
|
|
|
"""
|
2014-08-29 19:46:26 +04:00
|
|
|
if not keys:
|
|
|
|
dictionary.update(value)
|
|
|
|
return
|
2013-07-04 09:51:24 +04:00
|
|
|
|
2014-08-29 19:46:26 +04:00
|
|
|
for key in keys[:-1]:
|
|
|
|
if key not in dictionary:
|
|
|
|
dictionary[key] = {}
|
|
|
|
dictionary = dictionary[key]
|
2013-07-04 09:51:24 +04:00
|
|
|
|
2014-08-29 19:46:26 +04:00
|
|
|
dictionary[keys[-1]] = value
|
2013-07-04 09:51:24 +04:00
|
|
|
|
2012-09-20 16:06:27 +04:00
|
|
|
|
2014-08-29 19:46:26 +04:00
|
|
|
class SkipField(Exception):
|
|
|
|
pass
|
2012-10-04 16:28:14 +04:00
|
|
|
|
2012-12-04 17:16:45 +04:00
|
|
|
|
2014-09-09 20:46:28 +04:00
|
|
|
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_READ_ONLY_DEFAULT = 'May not set both `read_only` and `default`'
|
|
|
|
NOT_REQUIRED_DEFAULT = 'May not set both `required` and `default`'
|
2014-09-26 13:46:52 +04:00
|
|
|
USE_READONLYFIELD = 'Field(read_only=True) should be ReadOnlyField'
|
2014-09-09 20:46:28 +04:00
|
|
|
MISSING_ERROR_MESSAGE = (
|
|
|
|
'ValidationError raised by `{class_name}`, but error key `{key}` does '
|
|
|
|
'not exist in the `error_messages` dictionary.'
|
|
|
|
)
|
|
|
|
|
|
|
|
|
2014-08-29 19:46:26 +04:00
|
|
|
class Field(object):
|
|
|
|
_creation_counter = 0
|
2013-10-02 16:45:35 +04:00
|
|
|
|
2014-09-09 20:46:28 +04:00
|
|
|
default_error_messages = {
|
2014-09-22 20:46:02 +04:00
|
|
|
'required': _('This field is required.'),
|
|
|
|
'null': _('This field may not be null.')
|
2014-08-29 19:46:26 +04:00
|
|
|
}
|
2014-09-08 17:24:05 +04:00
|
|
|
default_validators = []
|
2014-09-23 17:15:00 +04:00
|
|
|
default_empty_html = None
|
2014-09-26 13:46:52 +04:00
|
|
|
initial = None
|
2014-09-08 17:24:05 +04:00
|
|
|
|
2014-08-29 19:46:26 +04:00
|
|
|
def __init__(self, read_only=False, write_only=False,
|
2014-09-26 13:46:52 +04:00
|
|
|
required=None, default=empty, initial=empty, source=None,
|
2014-09-10 16:52:16 +04:00
|
|
|
label=None, help_text=None, style=None,
|
2014-09-25 16:37:26 +04:00
|
|
|
error_messages=None, validators=[], allow_null=False):
|
2014-08-29 19:46:26 +04:00
|
|
|
self._creation_counter = Field._creation_counter
|
|
|
|
Field._creation_counter += 1
|
2014-05-04 02:12:08 +04:00
|
|
|
|
2014-08-29 19:46:26 +04:00
|
|
|
# If `required` is unset, then use `True` unless a default is provided.
|
|
|
|
if required is None:
|
|
|
|
required = default is empty and not read_only
|
2014-05-04 02:12:08 +04:00
|
|
|
|
2014-08-29 19:46:26 +04:00
|
|
|
# Some combinations of keyword arguments do not make sense.
|
2014-09-09 20:46:28 +04:00
|
|
|
assert not (read_only and write_only), NOT_READ_ONLY_WRITE_ONLY
|
|
|
|
assert not (read_only and required), NOT_READ_ONLY_REQUIRED
|
|
|
|
assert not (read_only and default is not empty), NOT_READ_ONLY_DEFAULT
|
|
|
|
assert not (required and default is not empty), NOT_REQUIRED_DEFAULT
|
2014-09-26 13:46:52 +04:00
|
|
|
assert not (read_only and self.__class__ == Field), USE_READONLYFIELD
|
2013-10-02 16:45:35 +04:00
|
|
|
|
2014-08-29 19:46:26 +04:00
|
|
|
self.read_only = read_only
|
|
|
|
self.write_only = write_only
|
|
|
|
self.required = required
|
|
|
|
self.default = default
|
|
|
|
self.source = source
|
2014-09-26 13:46:52 +04:00
|
|
|
self.initial = self.initial if (initial is empty) else initial
|
2014-08-29 19:46:26 +04:00
|
|
|
self.label = label
|
2014-09-10 16:52:16 +04:00
|
|
|
self.help_text = help_text
|
2014-08-29 19:46:26 +04:00
|
|
|
self.style = {} if style is None else style
|
2014-09-09 20:46:28 +04:00
|
|
|
self.validators = validators or self.default_validators[:]
|
2014-09-22 20:46:02 +04:00
|
|
|
self.allow_null = allow_null
|
2014-09-09 20:46:28 +04:00
|
|
|
|
2014-09-25 14:40:32 +04:00
|
|
|
# These are set up by `.bind()` when the field is added to a serializer.
|
|
|
|
self.field_name = None
|
|
|
|
self.parent = None
|
|
|
|
|
2014-09-09 20:46:28 +04:00
|
|
|
# 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
|
2012-10-04 16:28:14 +04:00
|
|
|
|
2014-09-25 14:40:32 +04:00
|
|
|
def bind(self, field_name, parent):
|
2012-10-04 16:28:14 +04:00
|
|
|
"""
|
2014-09-26 13:46:52 +04:00
|
|
|
Initializes the field name and parent for the field instance.
|
|
|
|
Called when a field is added to the parent serializer instance.
|
2012-10-04 16:28:14 +04:00
|
|
|
"""
|
2014-09-24 23:53:37 +04:00
|
|
|
|
|
|
|
# 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')
|
2014-09-25 15:09:12 +04:00
|
|
|
assert self.source != field_name, (
|
2014-09-24 23:53:37 +04:00
|
|
|
"It is redundant to specify `source='%s'` on field '%s' in "
|
2014-09-25 13:49:25 +04:00
|
|
|
"serializer '%s', because it is the same as the field name. "
|
2014-09-24 23:53:37 +04:00
|
|
|
"Remove the `source` keyword argument." %
|
|
|
|
(field_name, self.__class__.__name__, parent.__class__.__name__)
|
|
|
|
)
|
|
|
|
|
2014-08-29 19:46:26 +04:00
|
|
|
self.field_name = field_name
|
2012-10-04 16:28:14 +04:00
|
|
|
self.parent = parent
|
|
|
|
|
2014-09-25 14:40:32 +04:00
|
|
|
# `self.label` should default to being based on the field name.
|
2014-08-29 19:46:26 +04:00
|
|
|
if self.label is None:
|
2014-09-18 14:20:56 +04:00
|
|
|
self.label = field_name.replace('_', ' ').capitalize()
|
2012-10-04 16:28:14 +04:00
|
|
|
|
2014-08-29 19:46:26 +04:00
|
|
|
# self.source should default to being the same as the field name.
|
|
|
|
if self.source is None:
|
|
|
|
self.source = field_name
|
2012-10-04 16:28:14 +04:00
|
|
|
|
2014-08-29 19:46:26 +04:00
|
|
|
# self.source_attrs is a list of attributes that need to be looked up
|
|
|
|
# when serializing the instance, or populating the validated data.
|
2012-10-04 16:28:14 +04:00
|
|
|
if self.source == '*':
|
2014-08-29 19:46:26 +04:00
|
|
|
self.source_attrs = []
|
|
|
|
else:
|
|
|
|
self.source_attrs = self.source.split('.')
|
2012-10-04 16:28:14 +04:00
|
|
|
|
2014-08-29 19:46:26 +04:00
|
|
|
def get_initial(self):
|
2012-10-04 16:28:14 +04:00
|
|
|
"""
|
2014-08-29 19:46:26 +04:00
|
|
|
Return a value to use when the field is being returned as a primative
|
|
|
|
value, without any object instance.
|
2012-10-04 16:28:14 +04:00
|
|
|
"""
|
2014-08-29 19:46:26 +04:00
|
|
|
return self.initial
|
2012-10-04 16:28:14 +04:00
|
|
|
|
2014-08-29 19:46:26 +04:00
|
|
|
def get_value(self, dictionary):
|
2012-10-04 16:28:14 +04:00
|
|
|
"""
|
2014-08-29 19:46:26 +04:00
|
|
|
Given the *incoming* primative data, return the value for this field
|
|
|
|
that should be validated and transformed to a native value.
|
2012-10-04 16:28:14 +04:00
|
|
|
"""
|
2014-09-23 17:15:00 +04:00
|
|
|
if html.is_html_input(dictionary):
|
|
|
|
# HTML forms will represent empty fields as '', and cannot
|
|
|
|
# represent None or False values directly.
|
|
|
|
ret = dictionary.get(self.field_name, '')
|
|
|
|
return self.default_empty_html if (ret == '') else ret
|
2014-08-29 19:46:26 +04:00
|
|
|
return dictionary.get(self.field_name, empty)
|
2012-09-20 16:06:27 +04:00
|
|
|
|
2014-08-29 19:46:26 +04:00
|
|
|
def get_attribute(self, instance):
|
|
|
|
"""
|
|
|
|
Given the *outgoing* object instance, return the value for this field
|
|
|
|
that should be returned as a primative value.
|
|
|
|
"""
|
|
|
|
return get_attribute(instance, self.source_attrs)
|
2012-09-20 16:06:27 +04:00
|
|
|
|
2014-08-29 19:46:26 +04:00
|
|
|
def get_default(self):
|
|
|
|
"""
|
|
|
|
Return the default value to use when validating data if no input
|
|
|
|
is provided for this field.
|
2013-06-06 11:56:39 +04:00
|
|
|
|
2014-08-29 19:46:26 +04:00
|
|
|
If a default has not been set for this field then this will simply
|
|
|
|
return `empty`, indicating that no value should be set in the
|
|
|
|
validated data for this field.
|
|
|
|
"""
|
|
|
|
if self.default is empty:
|
|
|
|
raise SkipField()
|
2014-02-27 19:34:36 +04:00
|
|
|
return self.default
|
|
|
|
|
2014-09-12 12:49:35 +04:00
|
|
|
def run_validation(self, data=empty):
|
2014-08-29 19:46:26 +04:00
|
|
|
"""
|
|
|
|
Validate a simple representation and return the internal value.
|
2012-09-20 16:06:27 +04:00
|
|
|
|
2014-09-22 17:54:33 +04:00
|
|
|
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
|
2014-08-29 19:46:26 +04:00
|
|
|
validated data.
|
|
|
|
"""
|
|
|
|
if data is empty:
|
2014-09-26 13:46:52 +04:00
|
|
|
if getattr(self.root, 'partial', False):
|
|
|
|
raise SkipField()
|
2014-08-29 19:46:26 +04:00
|
|
|
if self.required:
|
|
|
|
self.fail('required')
|
|
|
|
return self.get_default()
|
2012-09-20 16:06:27 +04:00
|
|
|
|
2014-09-22 20:46:02 +04:00
|
|
|
if data is None:
|
|
|
|
if not self.allow_null:
|
|
|
|
self.fail('null')
|
|
|
|
return None
|
|
|
|
|
2014-09-12 12:49:35 +04:00
|
|
|
value = self.to_internal_value(data)
|
2014-09-09 20:46:28 +04:00
|
|
|
self.run_validators(value)
|
|
|
|
return value
|
2014-01-14 15:25:44 +04:00
|
|
|
|
2014-09-08 17:24:05 +04:00
|
|
|
def run_validators(self, value):
|
2014-09-22 17:54:33 +04:00
|
|
|
"""
|
|
|
|
Test the given value against all the validators on the field,
|
|
|
|
and either raise a `ValidationError` or simply return.
|
|
|
|
"""
|
2014-09-08 17:24:05 +04:00
|
|
|
errors = []
|
|
|
|
for validator in self.validators:
|
|
|
|
try:
|
|
|
|
validator(value)
|
|
|
|
except ValidationError as exc:
|
|
|
|
errors.extend(exc.messages)
|
|
|
|
if errors:
|
|
|
|
raise ValidationError(errors)
|
|
|
|
|
2014-09-12 12:49:35 +04:00
|
|
|
def to_internal_value(self, data):
|
2012-09-20 16:06:27 +04:00
|
|
|
"""
|
2014-08-29 19:46:26 +04:00
|
|
|
Transform the *incoming* primative data into a native value.
|
2012-09-20 16:06:27 +04:00
|
|
|
"""
|
2014-09-12 12:49:35 +04:00
|
|
|
raise NotImplementedError('to_internal_value() must be implemented.')
|
2012-09-20 16:06:27 +04:00
|
|
|
|
2014-09-12 12:49:35 +04:00
|
|
|
def to_representation(self, value):
|
2012-09-20 16:06:27 +04:00
|
|
|
"""
|
2014-08-29 19:46:26 +04:00
|
|
|
Transform the *outgoing* native value into primative data.
|
2012-09-20 16:06:27 +04:00
|
|
|
"""
|
2014-09-12 12:49:35 +04:00
|
|
|
raise NotImplementedError('to_representation() must be implemented.')
|
2012-09-20 16:06:27 +04:00
|
|
|
|
2014-08-29 19:46:26 +04:00
|
|
|
def fail(self, key, **kwargs):
|
|
|
|
"""
|
|
|
|
A helper method that simply raises a validation error.
|
|
|
|
"""
|
2012-10-04 16:28:14 +04:00
|
|
|
try:
|
2014-09-09 20:46:28 +04:00
|
|
|
msg = self.error_messages[key]
|
2013-02-06 17:05:17 +04:00
|
|
|
except KeyError:
|
2014-08-29 19:46:26 +04:00
|
|
|
class_name = self.__class__.__name__
|
2014-09-09 20:46:28 +04:00
|
|
|
msg = MISSING_ERROR_MESSAGE.format(class_name=class_name, key=key)
|
2014-08-29 19:46:26 +04:00
|
|
|
raise AssertionError(msg)
|
2014-09-09 20:46:28 +04:00
|
|
|
raise ValidationError(msg.format(**kwargs))
|
2012-09-20 16:06:27 +04:00
|
|
|
|
2014-09-25 16:37:26 +04:00
|
|
|
@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', {})
|
|
|
|
|
2014-09-26 13:46:52 +04:00
|
|
|
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(Field, cls).__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.
|
|
|
|
"""
|
|
|
|
args = copy.deepcopy(self._args)
|
|
|
|
kwargs = copy.deepcopy(self._kwargs)
|
|
|
|
return self.__class__(*args, **kwargs)
|
|
|
|
|
2014-09-05 19:29:46 +04:00
|
|
|
def __repr__(self):
|
2014-09-25 16:37:26 +04:00
|
|
|
"""
|
|
|
|
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.
|
|
|
|
"""
|
2014-09-09 20:46:28 +04:00
|
|
|
return representation.field_repr(self)
|
2014-09-05 19:29:46 +04:00
|
|
|
|
2012-09-20 16:06:27 +04:00
|
|
|
|
2014-09-09 20:46:28 +04:00
|
|
|
# Boolean types...
|
|
|
|
|
2014-08-29 19:46:26 +04:00
|
|
|
class BooleanField(Field):
|
2014-09-09 20:46:28 +04:00
|
|
|
default_error_messages = {
|
|
|
|
'invalid': _('`{input}` is not a valid boolean.')
|
2012-09-20 16:06:27 +04:00
|
|
|
}
|
2014-09-23 17:15:00 +04:00
|
|
|
default_empty_html = False
|
2014-09-25 13:49:25 +04:00
|
|
|
initial = False
|
2014-09-10 19:57:22 +04:00
|
|
|
TRUE_VALUES = set(('t', 'T', 'true', 'True', 'TRUE', '1', 1, True))
|
|
|
|
FALSE_VALUES = set(('f', 'F', 'false', 'False', 'FALSE', '0', 0, 0.0, False))
|
2014-08-29 19:46:26 +04:00
|
|
|
|
2014-09-23 17:30:17 +04:00
|
|
|
def __init__(self, **kwargs):
|
|
|
|
assert 'allow_null' not in kwargs, '`allow_null` is not a valid option. Use `NullBooleanField` instead.'
|
|
|
|
super(BooleanField, self).__init__(**kwargs)
|
|
|
|
|
2014-09-12 12:49:35 +04:00
|
|
|
def to_internal_value(self, data):
|
2014-08-29 19:46:26 +04:00
|
|
|
if data in self.TRUE_VALUES:
|
2012-09-20 16:06:27 +04:00
|
|
|
return True
|
2014-08-29 19:46:26 +04:00
|
|
|
elif data in self.FALSE_VALUES:
|
2012-09-20 16:06:27 +04:00
|
|
|
return False
|
2014-09-09 20:46:28 +04:00
|
|
|
self.fail('invalid', input=data)
|
|
|
|
|
2014-09-12 12:49:35 +04:00
|
|
|
def to_representation(self, value):
|
2014-09-23 17:30:17 +04:00
|
|
|
if value in self.TRUE_VALUES:
|
|
|
|
return True
|
|
|
|
elif value in self.FALSE_VALUES:
|
|
|
|
return False
|
|
|
|
return bool(value)
|
|
|
|
|
|
|
|
|
|
|
|
class NullBooleanField(Field):
|
|
|
|
default_error_messages = {
|
|
|
|
'invalid': _('`{input}` is not a valid boolean.')
|
|
|
|
}
|
|
|
|
default_empty_html = None
|
2014-09-26 13:46:52 +04:00
|
|
|
initial = None
|
2014-09-23 17:30:17 +04:00
|
|
|
TRUE_VALUES = set(('t', 'T', 'true', 'True', 'TRUE', '1', 1, True))
|
|
|
|
FALSE_VALUES = set(('f', 'F', 'false', 'False', 'FALSE', '0', 0, 0.0, False))
|
|
|
|
NULL_VALUES = set(('n', 'N', 'null', 'Null', 'NULL', '', None))
|
|
|
|
|
|
|
|
def __init__(self, **kwargs):
|
|
|
|
assert 'allow_null' not in kwargs, '`allow_null` is not a valid option.'
|
|
|
|
kwargs['allow_null'] = True
|
|
|
|
super(NullBooleanField, self).__init__(**kwargs)
|
|
|
|
|
|
|
|
def to_internal_value(self, data):
|
|
|
|
if data in self.TRUE_VALUES:
|
|
|
|
return True
|
|
|
|
elif data in self.FALSE_VALUES:
|
|
|
|
return False
|
|
|
|
elif data in self.NULL_VALUES:
|
|
|
|
return None
|
|
|
|
self.fail('invalid', input=data)
|
|
|
|
|
|
|
|
def to_representation(self, value):
|
|
|
|
if value in self.NULL_VALUES:
|
2014-09-09 20:46:28 +04:00
|
|
|
return None
|
|
|
|
if value in self.TRUE_VALUES:
|
|
|
|
return True
|
|
|
|
elif value in self.FALSE_VALUES:
|
|
|
|
return False
|
|
|
|
return bool(value)
|
2012-09-20 16:06:27 +04:00
|
|
|
|
|
|
|
|
2014-09-09 20:46:28 +04:00
|
|
|
# String types...
|
|
|
|
|
2014-08-29 19:46:26 +04:00
|
|
|
class CharField(Field):
|
2014-09-09 20:46:28 +04:00
|
|
|
default_error_messages = {
|
|
|
|
'blank': _('This field may not be blank.')
|
2014-08-29 19:46:26 +04:00
|
|
|
}
|
2014-09-23 17:15:00 +04:00
|
|
|
default_empty_html = ''
|
2014-09-25 13:49:25 +04:00
|
|
|
initial = ''
|
2012-10-04 16:28:14 +04:00
|
|
|
|
2014-09-02 18:07:56 +04:00
|
|
|
def __init__(self, **kwargs):
|
2014-08-29 19:46:26 +04:00
|
|
|
self.allow_blank = kwargs.pop('allow_blank', False)
|
2014-09-02 18:07:56 +04:00
|
|
|
self.max_length = kwargs.pop('max_length', None)
|
|
|
|
self.min_length = kwargs.pop('min_length', None)
|
|
|
|
super(CharField, self).__init__(**kwargs)
|
2012-09-20 16:06:27 +04:00
|
|
|
|
2014-09-22 20:46:02 +04:00
|
|
|
def run_validation(self, data=empty):
|
2014-09-23 17:15:00 +04:00
|
|
|
# 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.
|
2014-09-22 20:46:02 +04:00
|
|
|
if data == '':
|
|
|
|
if not self.allow_blank:
|
|
|
|
self.fail('blank')
|
|
|
|
return ''
|
|
|
|
return super(CharField, self).run_validation(data)
|
|
|
|
|
2014-09-12 12:49:35 +04:00
|
|
|
def to_internal_value(self, data):
|
2014-08-29 19:46:26 +04:00
|
|
|
return str(data)
|
2012-11-17 01:43:16 +04:00
|
|
|
|
2014-09-12 12:49:35 +04:00
|
|
|
def to_representation(self, value):
|
2014-09-09 20:46:28 +04:00
|
|
|
if value is None:
|
|
|
|
return None
|
|
|
|
return str(value)
|
2012-11-17 01:43:16 +04:00
|
|
|
|
2014-09-09 20:46:28 +04:00
|
|
|
|
|
|
|
class EmailField(CharField):
|
|
|
|
default_error_messages = {
|
|
|
|
'invalid': _('Enter a valid email address.')
|
|
|
|
}
|
2014-09-22 16:26:47 +04:00
|
|
|
|
|
|
|
def __init__(self, **kwargs):
|
|
|
|
super(EmailField, self).__init__(**kwargs)
|
2014-09-22 19:45:06 +04:00
|
|
|
validator = EmailValidator(message=self.error_messages['invalid'])
|
2014-09-22 16:57:45 +04:00
|
|
|
self.validators.append(validator)
|
2014-09-09 20:46:28 +04:00
|
|
|
|
2014-09-12 12:49:35 +04:00
|
|
|
def to_internal_value(self, data):
|
|
|
|
return str(data).strip()
|
2014-09-09 20:46:28 +04:00
|
|
|
|
2014-09-12 12:49:35 +04:00
|
|
|
def to_representation(self, value):
|
|
|
|
if value is None:
|
2014-09-09 20:46:28 +04:00
|
|
|
return None
|
2014-09-12 12:49:35 +04:00
|
|
|
return str(value).strip()
|
2014-09-09 20:46:28 +04:00
|
|
|
|
|
|
|
|
|
|
|
class RegexField(CharField):
|
2014-09-22 16:26:47 +04:00
|
|
|
default_error_messages = {
|
|
|
|
'invalid': _('This value does not match the required pattern.')
|
|
|
|
}
|
|
|
|
|
2014-09-09 20:46:28 +04:00
|
|
|
def __init__(self, regex, **kwargs):
|
|
|
|
super(RegexField, self).__init__(**kwargs)
|
2014-09-22 16:26:47 +04:00
|
|
|
validator = validators.RegexValidator(regex, message=self.error_messages['invalid'])
|
2014-09-22 16:57:45 +04:00
|
|
|
self.validators.append(validator)
|
2014-09-09 20:46:28 +04:00
|
|
|
|
|
|
|
|
|
|
|
class SlugField(CharField):
|
|
|
|
default_error_messages = {
|
|
|
|
'invalid': _("Enter a valid 'slug' consisting of letters, numbers, underscores or hyphens.")
|
|
|
|
}
|
2014-09-22 16:26:47 +04:00
|
|
|
|
|
|
|
def __init__(self, **kwargs):
|
|
|
|
super(SlugField, self).__init__(**kwargs)
|
|
|
|
slug_regex = re.compile(r'^[-a-zA-Z0-9_]+$')
|
|
|
|
validator = validators.RegexValidator(slug_regex, message=self.error_messages['invalid'])
|
2014-09-22 16:57:45 +04:00
|
|
|
self.validators.append(validator)
|
2014-09-09 20:46:28 +04:00
|
|
|
|
|
|
|
|
|
|
|
class URLField(CharField):
|
|
|
|
default_error_messages = {
|
|
|
|
'invalid': _("Enter a valid URL.")
|
|
|
|
}
|
2014-09-22 16:26:47 +04:00
|
|
|
|
|
|
|
def __init__(self, **kwargs):
|
|
|
|
super(URLField, self).__init__(**kwargs)
|
2014-09-22 19:45:06 +04:00
|
|
|
validator = URLValidator(message=self.error_messages['invalid'])
|
2014-09-22 16:57:45 +04:00
|
|
|
self.validators.append(validator)
|
2014-09-09 20:46:28 +04:00
|
|
|
|
|
|
|
|
|
|
|
# Number types...
|
|
|
|
|
|
|
|
class IntegerField(Field):
|
|
|
|
default_error_messages = {
|
2014-09-22 16:57:45 +04:00
|
|
|
'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}.'),
|
2014-09-09 20:46:28 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
def __init__(self, **kwargs):
|
|
|
|
max_value = kwargs.pop('max_value', None)
|
|
|
|
min_value = kwargs.pop('min_value', None)
|
|
|
|
super(IntegerField, self).__init__(**kwargs)
|
|
|
|
if max_value is not None:
|
2014-09-22 16:57:45 +04:00
|
|
|
message = self.error_messages['max_value'].format(max_value=max_value)
|
|
|
|
self.validators.append(MaxValueValidator(max_value, message=message))
|
2014-09-09 20:46:28 +04:00
|
|
|
if min_value is not None:
|
2014-09-22 16:57:45 +04:00
|
|
|
message = self.error_messages['min_value'].format(min_value=min_value)
|
|
|
|
self.validators.append(MinValueValidator(min_value, message=message))
|
2014-09-09 20:46:28 +04:00
|
|
|
|
2014-09-12 12:49:35 +04:00
|
|
|
def to_internal_value(self, data):
|
2014-09-09 20:46:28 +04:00
|
|
|
try:
|
|
|
|
data = int(str(data))
|
|
|
|
except (ValueError, TypeError):
|
|
|
|
self.fail('invalid')
|
|
|
|
return data
|
|
|
|
|
2014-09-12 12:49:35 +04:00
|
|
|
def to_representation(self, value):
|
2014-09-09 20:46:28 +04:00
|
|
|
if value is None:
|
|
|
|
return None
|
|
|
|
return int(value)
|
|
|
|
|
|
|
|
|
|
|
|
class FloatField(Field):
|
|
|
|
default_error_messages = {
|
2014-09-22 15:25:57 +04:00
|
|
|
'invalid': _("A valid number is required."),
|
2014-09-22 16:57:45 +04:00
|
|
|
'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}.'),
|
2013-05-20 16:04:38 +04:00
|
|
|
}
|
2013-05-25 02:44:23 +04:00
|
|
|
|
2014-09-02 18:07:56 +04:00
|
|
|
def __init__(self, **kwargs):
|
2014-09-09 20:46:28 +04:00
|
|
|
max_value = kwargs.pop('max_value', None)
|
|
|
|
min_value = kwargs.pop('min_value', None)
|
|
|
|
super(FloatField, self).__init__(**kwargs)
|
|
|
|
if max_value is not None:
|
2014-09-22 16:57:45 +04:00
|
|
|
message = self.error_messages['max_value'].format(max_value=max_value)
|
|
|
|
self.validators.append(MaxValueValidator(max_value, message=message))
|
2014-09-09 20:46:28 +04:00
|
|
|
if min_value is not None:
|
2014-09-22 16:57:45 +04:00
|
|
|
message = self.error_messages['min_value'].format(min_value=min_value)
|
|
|
|
self.validators.append(MinValueValidator(min_value, message=message))
|
2014-09-09 20:46:28 +04:00
|
|
|
|
2014-09-12 12:49:35 +04:00
|
|
|
def to_internal_value(self, value):
|
2014-09-22 15:25:57 +04:00
|
|
|
try:
|
|
|
|
return float(value)
|
|
|
|
except (TypeError, ValueError):
|
|
|
|
self.fail('invalid')
|
2014-09-12 12:49:35 +04:00
|
|
|
|
|
|
|
def to_representation(self, value):
|
2014-09-09 20:46:28 +04:00
|
|
|
if value is None:
|
|
|
|
return None
|
2014-09-22 15:25:57 +04:00
|
|
|
return float(value)
|
2014-09-09 20:46:28 +04:00
|
|
|
|
|
|
|
|
|
|
|
class DecimalField(Field):
|
|
|
|
default_error_messages = {
|
2014-09-22 15:25:57 +04:00
|
|
|
'invalid': _('A valid number is required.'),
|
2014-09-09 20:46:28 +04:00
|
|
|
'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.')
|
|
|
|
}
|
|
|
|
|
2014-09-12 15:10:22 +04:00
|
|
|
coerce_to_string = api_settings.COERCE_DECIMAL_TO_STRING
|
|
|
|
|
|
|
|
def __init__(self, max_digits, decimal_places, coerce_to_string=None, max_value=None, min_value=None, **kwargs):
|
2014-09-12 00:48:54 +04:00
|
|
|
self.max_digits = max_digits
|
|
|
|
self.decimal_places = decimal_places
|
2014-09-12 15:10:22 +04:00
|
|
|
self.coerce_to_string = coerce_to_string if (coerce_to_string is not None) else self.coerce_to_string
|
2014-09-09 20:46:28 +04:00
|
|
|
super(DecimalField, self).__init__(**kwargs)
|
|
|
|
if max_value is not None:
|
2014-09-22 16:57:45 +04:00
|
|
|
message = self.error_messages['max_value'].format(max_value=max_value)
|
|
|
|
self.validators.append(MaxValueValidator(max_value, message=message))
|
2014-09-09 20:46:28 +04:00
|
|
|
if min_value is not None:
|
2014-09-22 16:57:45 +04:00
|
|
|
message = self.error_messages['min_value'].format(min_value=min_value)
|
|
|
|
self.validators.append(MinValueValidator(min_value, message=message))
|
2014-09-09 20:46:28 +04:00
|
|
|
|
2014-09-12 12:49:35 +04:00
|
|
|
def to_internal_value(self, value):
|
2014-09-09 20:46:28 +04:00
|
|
|
"""
|
|
|
|
Validates that the input is a decimal number. Returns a Decimal
|
|
|
|
instance. Returns None for empty values. Ensures that there are no more
|
|
|
|
than max_digits in the number, and no more than decimal_places digits
|
|
|
|
after the decimal point.
|
|
|
|
"""
|
|
|
|
value = smart_text(value).strip()
|
|
|
|
try:
|
|
|
|
value = decimal.Decimal(value)
|
|
|
|
except decimal.DecimalException:
|
|
|
|
self.fail('invalid')
|
|
|
|
|
|
|
|
# Check for NaN. It is the only value that isn't equal to itself,
|
|
|
|
# so we can use this to identify NaN values.
|
|
|
|
if value != value:
|
|
|
|
self.fail('invalid')
|
|
|
|
|
|
|
|
# Check for infinity and negative infinity.
|
|
|
|
if value in (decimal.Decimal('Inf'), decimal.Decimal('-Inf')):
|
|
|
|
self.fail('invalid')
|
|
|
|
|
|
|
|
sign, digittuple, exponent = value.as_tuple()
|
|
|
|
decimals = abs(exponent)
|
|
|
|
# digittuple doesn't include any leading zeros.
|
|
|
|
digits = len(digittuple)
|
|
|
|
if decimals > digits:
|
|
|
|
# We have leading zeros up to or past the decimal point. Count
|
|
|
|
# everything past the decimal point as a digit. We do not count
|
|
|
|
# 0 before the decimal point as a digit since that would mean
|
|
|
|
# we would not allow max_digits = decimal_places.
|
|
|
|
digits = decimals
|
|
|
|
whole_digits = digits - decimals
|
|
|
|
|
|
|
|
if self.max_digits is not None and digits > self.max_digits:
|
|
|
|
self.fail('max_digits', max_digits=self.max_digits)
|
|
|
|
if self.decimal_places is not None and decimals > self.decimal_places:
|
2014-09-12 00:48:54 +04:00
|
|
|
self.fail('max_decimal_places', max_decimal_places=self.decimal_places)
|
2014-09-09 20:46:28 +04:00
|
|
|
if self.max_digits is not None and self.decimal_places is not None and whole_digits > (self.max_digits - self.decimal_places):
|
2014-09-22 15:25:57 +04:00
|
|
|
self.fail('max_whole_digits', max_whole_digits=self.max_digits - self.decimal_places)
|
2014-09-09 20:46:28 +04:00
|
|
|
|
|
|
|
return value
|
|
|
|
|
2014-09-12 12:49:35 +04:00
|
|
|
def to_representation(self, value):
|
2014-09-22 18:34:06 +04:00
|
|
|
if value in (None, ''):
|
|
|
|
return None
|
|
|
|
|
|
|
|
if not isinstance(value, decimal.Decimal):
|
2014-09-22 19:45:06 +04:00
|
|
|
value = decimal.Decimal(str(value).strip())
|
2014-09-12 00:57:32 +04:00
|
|
|
|
2014-09-22 18:34:06 +04:00
|
|
|
context = decimal.getcontext().copy()
|
|
|
|
context.prec = self.max_digits
|
|
|
|
quantized = value.quantize(
|
|
|
|
decimal.Decimal('.1') ** self.decimal_places,
|
|
|
|
context=context
|
|
|
|
)
|
2014-09-12 00:57:32 +04:00
|
|
|
if not self.coerce_to_string:
|
2014-09-22 18:34:06 +04:00
|
|
|
return quantized
|
|
|
|
return '{0:f}'.format(quantized)
|
2014-09-12 00:48:54 +04:00
|
|
|
|
2014-09-09 20:46:28 +04:00
|
|
|
|
|
|
|
# Date & time fields...
|
|
|
|
|
2014-09-25 16:10:33 +04:00
|
|
|
class DateTimeField(Field):
|
2014-09-09 20:46:28 +04:00
|
|
|
default_error_messages = {
|
2014-09-25 16:10:33 +04:00
|
|
|
'invalid': _('Datetime has wrong format. Use one of these formats instead: {format}'),
|
|
|
|
'date': _('Expected a datetime but got a date.'),
|
2014-09-09 20:46:28 +04:00
|
|
|
}
|
2014-09-25 16:10:33 +04:00
|
|
|
format = api_settings.DATETIME_FORMAT
|
|
|
|
input_formats = api_settings.DATETIME_INPUT_FORMATS
|
|
|
|
default_timezone = timezone.get_default_timezone() if settings.USE_TZ else None
|
2014-09-09 20:46:28 +04:00
|
|
|
|
2014-09-25 16:10:33 +04:00
|
|
|
def __init__(self, format=empty, input_formats=None, default_timezone=None, *args, **kwargs):
|
2014-09-22 19:02:59 +04:00
|
|
|
self.format = format if format is not empty else self.format
|
2014-09-12 15:10:22 +04:00
|
|
|
self.input_formats = input_formats if input_formats is not None else self.input_formats
|
2014-09-25 16:10:33 +04:00
|
|
|
self.default_timezone = default_timezone if default_timezone is not None else self.default_timezone
|
|
|
|
super(DateTimeField, self).__init__(*args, **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.
|
|
|
|
"""
|
|
|
|
if (self.default_timezone is not None) and not timezone.is_aware(value):
|
|
|
|
return timezone.make_aware(value, self.default_timezone)
|
|
|
|
elif (self.default_timezone is None) and timezone.is_aware(value):
|
|
|
|
return timezone.make_naive(value, timezone.UTC())
|
|
|
|
return value
|
2014-09-09 20:46:28 +04:00
|
|
|
|
2014-09-12 12:49:35 +04:00
|
|
|
def to_internal_value(self, value):
|
2014-09-25 16:12:02 +04:00
|
|
|
if isinstance(value, datetime.date) and not isinstance(value, datetime.datetime):
|
2014-09-25 16:10:33 +04:00
|
|
|
self.fail('date')
|
2014-09-10 11:53:33 +04:00
|
|
|
|
2014-09-25 16:10:33 +04:00
|
|
|
if isinstance(value, datetime.datetime):
|
|
|
|
return self.enforce_timezone(value)
|
2014-09-09 20:46:28 +04:00
|
|
|
|
|
|
|
for format in self.input_formats:
|
|
|
|
if format.lower() == ISO_8601:
|
|
|
|
try:
|
2014-09-25 16:10:33 +04:00
|
|
|
parsed = parse_datetime(value)
|
2014-09-09 20:46:28 +04:00
|
|
|
except (ValueError, TypeError):
|
|
|
|
pass
|
|
|
|
else:
|
|
|
|
if parsed is not None:
|
2014-09-25 16:10:33 +04:00
|
|
|
return self.enforce_timezone(parsed)
|
2014-09-09 20:46:28 +04:00
|
|
|
else:
|
|
|
|
try:
|
|
|
|
parsed = datetime.datetime.strptime(value, format)
|
|
|
|
except (ValueError, TypeError):
|
|
|
|
pass
|
|
|
|
else:
|
2014-09-25 16:10:33 +04:00
|
|
|
return self.enforce_timezone(parsed)
|
2014-09-09 20:46:28 +04:00
|
|
|
|
2014-09-25 16:10:33 +04:00
|
|
|
humanized_format = humanize_datetime.datetime_formats(self.input_formats)
|
2014-09-12 13:59:51 +04:00
|
|
|
self.fail('invalid', format=humanized_format)
|
2014-09-09 20:46:28 +04:00
|
|
|
|
2014-09-12 12:49:35 +04:00
|
|
|
def to_representation(self, value):
|
2014-09-09 20:46:28 +04:00
|
|
|
if value is None or self.format is None:
|
|
|
|
return value
|
|
|
|
|
|
|
|
if self.format.lower() == ISO_8601:
|
2014-09-25 16:10:33 +04:00
|
|
|
ret = value.isoformat()
|
|
|
|
if ret.endswith('+00:00'):
|
|
|
|
ret = ret[:-6] + 'Z'
|
|
|
|
return ret
|
2014-09-09 20:46:28 +04:00
|
|
|
return value.strftime(self.format)
|
|
|
|
|
|
|
|
|
2014-09-25 16:10:33 +04:00
|
|
|
class DateField(Field):
|
2014-09-09 20:46:28 +04:00
|
|
|
default_error_messages = {
|
2014-09-25 16:10:33 +04:00
|
|
|
'invalid': _('Date has wrong format. Use one of these formats instead: {format}'),
|
|
|
|
'datetime': _('Expected a date but got a datetime.'),
|
2014-09-09 20:46:28 +04:00
|
|
|
}
|
2014-09-25 16:10:33 +04:00
|
|
|
format = api_settings.DATE_FORMAT
|
|
|
|
input_formats = api_settings.DATE_INPUT_FORMATS
|
2014-09-09 20:46:28 +04:00
|
|
|
|
2014-09-25 16:10:33 +04:00
|
|
|
def __init__(self, format=empty, input_formats=None, *args, **kwargs):
|
2014-09-22 19:02:59 +04:00
|
|
|
self.format = format if format is not empty else self.format
|
2014-09-12 15:10:22 +04:00
|
|
|
self.input_formats = input_formats if input_formats is not None else self.input_formats
|
2014-09-25 16:10:33 +04:00
|
|
|
super(DateField, self).__init__(*args, **kwargs)
|
2014-09-22 15:25:57 +04:00
|
|
|
|
2014-09-12 12:49:35 +04:00
|
|
|
def to_internal_value(self, value):
|
2014-09-22 15:25:57 +04:00
|
|
|
if isinstance(value, datetime.datetime):
|
2014-09-25 16:10:33 +04:00
|
|
|
self.fail('datetime')
|
|
|
|
|
|
|
|
if isinstance(value, datetime.date):
|
|
|
|
return value
|
2014-09-09 20:46:28 +04:00
|
|
|
|
|
|
|
for format in self.input_formats:
|
|
|
|
if format.lower() == ISO_8601:
|
|
|
|
try:
|
2014-09-25 16:10:33 +04:00
|
|
|
parsed = parse_date(value)
|
2014-09-09 20:46:28 +04:00
|
|
|
except (ValueError, TypeError):
|
|
|
|
pass
|
|
|
|
else:
|
|
|
|
if parsed is not None:
|
2014-09-25 16:10:33 +04:00
|
|
|
return parsed
|
2014-09-09 20:46:28 +04:00
|
|
|
else:
|
|
|
|
try:
|
|
|
|
parsed = datetime.datetime.strptime(value, format)
|
|
|
|
except (ValueError, TypeError):
|
|
|
|
pass
|
|
|
|
else:
|
2014-09-25 16:10:33 +04:00
|
|
|
return parsed.date()
|
2014-09-09 20:46:28 +04:00
|
|
|
|
2014-09-25 16:10:33 +04:00
|
|
|
humanized_format = humanize_datetime.date_formats(self.input_formats)
|
2014-09-12 13:59:51 +04:00
|
|
|
self.fail('invalid', format=humanized_format)
|
2014-09-09 20:46:28 +04:00
|
|
|
|
2014-09-12 12:49:35 +04:00
|
|
|
def to_representation(self, value):
|
2014-09-09 20:46:28 +04:00
|
|
|
if value is None or self.format is None:
|
|
|
|
return value
|
|
|
|
|
2014-09-25 16:10:33 +04:00
|
|
|
# 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.'
|
|
|
|
)
|
|
|
|
|
2014-09-09 20:46:28 +04:00
|
|
|
if self.format.lower() == ISO_8601:
|
2014-09-25 16:10:33 +04:00
|
|
|
return value.isoformat()
|
2014-09-09 20:46:28 +04:00
|
|
|
return value.strftime(self.format)
|
2014-08-29 19:46:26 +04:00
|
|
|
|
|
|
|
|
2014-09-09 20:46:28 +04:00
|
|
|
class TimeField(Field):
|
|
|
|
default_error_messages = {
|
2014-09-12 13:59:51 +04:00
|
|
|
'invalid': _('Time has wrong format. Use one of these formats instead: {format}'),
|
2014-09-09 20:46:28 +04:00
|
|
|
}
|
|
|
|
format = api_settings.TIME_FORMAT
|
2014-09-12 15:10:22 +04:00
|
|
|
input_formats = api_settings.TIME_INPUT_FORMATS
|
2014-09-09 20:46:28 +04:00
|
|
|
|
2014-09-22 19:02:59 +04:00
|
|
|
def __init__(self, format=empty, input_formats=None, *args, **kwargs):
|
|
|
|
self.format = format if format is not empty else self.format
|
2014-09-12 15:10:22 +04:00
|
|
|
self.input_formats = input_formats if input_formats is not None else self.input_formats
|
2014-09-09 20:46:28 +04:00
|
|
|
super(TimeField, self).__init__(*args, **kwargs)
|
|
|
|
|
2014-09-22 16:26:47 +04:00
|
|
|
def to_internal_value(self, value):
|
2014-09-09 20:46:28 +04:00
|
|
|
if isinstance(value, datetime.time):
|
|
|
|
return value
|
|
|
|
|
|
|
|
for format in self.input_formats:
|
|
|
|
if format.lower() == ISO_8601:
|
|
|
|
try:
|
|
|
|
parsed = parse_time(value)
|
|
|
|
except (ValueError, TypeError):
|
|
|
|
pass
|
|
|
|
else:
|
|
|
|
if parsed is not None:
|
|
|
|
return parsed
|
|
|
|
else:
|
|
|
|
try:
|
|
|
|
parsed = datetime.datetime.strptime(value, format)
|
|
|
|
except (ValueError, TypeError):
|
|
|
|
pass
|
|
|
|
else:
|
|
|
|
return parsed.time()
|
|
|
|
|
|
|
|
humanized_format = humanize_datetime.time_formats(self.input_formats)
|
2014-09-12 13:59:51 +04:00
|
|
|
self.fail('invalid', format=humanized_format)
|
2014-09-09 20:46:28 +04:00
|
|
|
|
2014-09-12 12:49:35 +04:00
|
|
|
def to_representation(self, value):
|
2014-09-09 20:46:28 +04:00
|
|
|
if value is None or self.format is None:
|
|
|
|
return value
|
|
|
|
|
2014-09-25 16:10:33 +04:00
|
|
|
# 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.'
|
|
|
|
)
|
2014-09-09 20:46:28 +04:00
|
|
|
|
|
|
|
if self.format.lower() == ISO_8601:
|
|
|
|
return value.isoformat()
|
|
|
|
return value.strftime(self.format)
|
|
|
|
|
|
|
|
|
|
|
|
# Choice types...
|
|
|
|
|
|
|
|
class ChoiceField(Field):
|
|
|
|
default_error_messages = {
|
|
|
|
'invalid_choice': _('`{input}` is not a valid choice.')
|
|
|
|
}
|
|
|
|
|
|
|
|
def __init__(self, choices, **kwargs):
|
2014-08-29 19:46:26 +04:00
|
|
|
# Allow either single or paired choices style:
|
|
|
|
# choices = [1, 2, 3]
|
|
|
|
# choices = [(1, 'First'), (2, 'Second'), (3, 'Third')]
|
|
|
|
pairs = [
|
|
|
|
isinstance(item, (list, tuple)) and len(item) == 2
|
|
|
|
for item in choices
|
|
|
|
]
|
|
|
|
if all(pairs):
|
2014-09-25 13:49:25 +04:00
|
|
|
self.choices = SortedDict([(key, display_value) for key, display_value in choices])
|
2014-08-29 19:46:26 +04:00
|
|
|
else:
|
2014-09-25 13:49:25 +04:00
|
|
|
self.choices = SortedDict([(item, item) for item in choices])
|
2013-05-25 02:44:23 +04:00
|
|
|
|
2014-08-29 19:46:26 +04:00
|
|
|
# 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.
|
2014-09-10 19:57:22 +04:00
|
|
|
self.choice_strings_to_values = dict([
|
|
|
|
(str(key), key) for key in self.choices.keys()
|
|
|
|
])
|
2012-10-19 02:48:52 +04:00
|
|
|
|
2014-09-02 18:07:56 +04:00
|
|
|
super(ChoiceField, self).__init__(**kwargs)
|
2012-10-29 18:10:38 +04:00
|
|
|
|
2014-09-12 12:49:35 +04:00
|
|
|
def to_internal_value(self, data):
|
2012-09-20 16:06:27 +04:00
|
|
|
try:
|
2014-08-29 19:46:26 +04:00
|
|
|
return self.choice_strings_to_values[str(data)]
|
|
|
|
except KeyError:
|
|
|
|
self.fail('invalid_choice', input=data)
|
2012-11-14 02:26:17 +04:00
|
|
|
|
2014-09-12 12:49:35 +04:00
|
|
|
def to_representation(self, value):
|
2014-09-22 19:50:04 +04:00
|
|
|
return self.choice_strings_to_values[str(value)]
|
2014-09-09 20:46:28 +04:00
|
|
|
|
2013-04-15 14:40:18 +04:00
|
|
|
|
2014-08-29 19:46:26 +04:00
|
|
|
class MultipleChoiceField(ChoiceField):
|
2014-09-09 20:46:28 +04:00
|
|
|
default_error_messages = {
|
|
|
|
'invalid_choice': _('`{input}` is not a valid choice.'),
|
|
|
|
'not_a_list': _('Expected a list of items but got type `{input_type}`')
|
2013-04-15 14:40:18 +04:00
|
|
|
}
|
|
|
|
|
2014-09-12 12:49:35 +04:00
|
|
|
def to_internal_value(self, data):
|
2014-09-22 17:54:33 +04:00
|
|
|
if isinstance(data, type('')) or not hasattr(data, '__iter__'):
|
2014-08-29 19:46:26 +04:00
|
|
|
self.fail('not_a_list', input_type=type(data).__name__)
|
2014-09-22 17:54:33 +04:00
|
|
|
|
2014-08-29 19:46:26 +04:00
|
|
|
return set([
|
2014-09-12 12:49:35 +04:00
|
|
|
super(MultipleChoiceField, self).to_internal_value(item)
|
2014-08-29 19:46:26 +04:00
|
|
|
for item in data
|
|
|
|
])
|
2013-04-15 14:40:18 +04:00
|
|
|
|
2014-09-12 12:49:35 +04:00
|
|
|
def to_representation(self, value):
|
2014-09-22 19:52:57 +04:00
|
|
|
return set([
|
|
|
|
self.choice_strings_to_values[str(item)] for item in value
|
|
|
|
])
|
2014-09-02 20:41:23 +04:00
|
|
|
|
2012-11-19 21:22:17 +04:00
|
|
|
|
2014-09-09 20:46:28 +04:00
|
|
|
# File types...
|
|
|
|
|
|
|
|
class FileField(Field):
|
2014-09-02 18:07:56 +04:00
|
|
|
pass # TODO
|
|
|
|
|
|
|
|
|
2014-09-09 20:46:28 +04:00
|
|
|
class ImageField(Field):
|
2014-09-02 20:41:23 +04:00
|
|
|
pass # TODO
|
|
|
|
|
|
|
|
|
2014-09-09 20:46:28 +04:00
|
|
|
# Advanced field types...
|
2014-09-02 18:07:56 +04:00
|
|
|
|
2014-09-09 20:46:28 +04:00
|
|
|
class ReadOnlyField(Field):
|
|
|
|
"""
|
|
|
|
A read-only field that simply returns the field value.
|
2014-09-02 20:41:23 +04:00
|
|
|
|
2014-09-09 20:46:28 +04:00
|
|
|
If the field is a method with no parameters, the method will be called
|
|
|
|
and it's return value used as the representation.
|
2014-09-02 20:41:23 +04:00
|
|
|
|
2014-09-09 20:46:28 +04:00
|
|
|
For example, the following would call `get_expiry_date()` on the object:
|
2014-09-02 20:41:23 +04:00
|
|
|
|
2014-09-09 20:46:28 +04:00
|
|
|
class ExampleSerializer(self):
|
|
|
|
expiry_date = ReadOnlyField(source='get_expiry_date')
|
|
|
|
"""
|
2014-09-02 20:41:23 +04:00
|
|
|
|
|
|
|
def __init__(self, **kwargs):
|
2014-09-09 20:46:28 +04:00
|
|
|
kwargs['read_only'] = True
|
|
|
|
super(ReadOnlyField, self).__init__(**kwargs)
|
2014-09-02 18:07:56 +04:00
|
|
|
|
2014-09-12 12:49:35 +04:00
|
|
|
def to_representation(self, value):
|
2014-09-02 20:41:23 +04:00
|
|
|
if is_simple_callable(value):
|
|
|
|
return value()
|
|
|
|
return value
|
|
|
|
|
|
|
|
|
2014-09-12 12:12:56 +04:00
|
|
|
class SerializerMethodField(Field):
|
2014-09-09 20:46:28 +04:00
|
|
|
"""
|
|
|
|
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):
|
2014-09-12 12:12:56 +04:00
|
|
|
extra_info = SerializerMethodField()
|
2014-09-09 20:46:28 +04:00
|
|
|
|
|
|
|
def get_extra_info(self, obj):
|
|
|
|
return ... # Calculate some data to return.
|
|
|
|
"""
|
2014-09-25 15:09:12 +04:00
|
|
|
def __init__(self, method_name=None, **kwargs):
|
|
|
|
self.method_name = method_name
|
2014-08-29 19:46:26 +04:00
|
|
|
kwargs['source'] = '*'
|
|
|
|
kwargs['read_only'] = True
|
2014-09-12 12:12:56 +04:00
|
|
|
super(SerializerMethodField, self).__init__(**kwargs)
|
2014-09-09 20:46:28 +04:00
|
|
|
|
2014-09-25 15:09:12 +04:00
|
|
|
def bind(self, field_name, parent):
|
|
|
|
# In order to enforce a consistent style, we error if a redundant
|
|
|
|
# 'method_name' argument has been used. For example:
|
|
|
|
# my_field = serializer.CharField(source='my_field')
|
|
|
|
default_method_name = 'get_{field_name}'.format(field_name=field_name)
|
|
|
|
assert self.method_name != default_method_name, (
|
|
|
|
"It is redundant to specify `%s` on SerializerMethodField '%s' in "
|
|
|
|
"serializer '%s', because it is the same as the default method name. "
|
|
|
|
"Remove the `method_name` argument." %
|
|
|
|
(self.method_name, field_name, parent.__class__.__name__)
|
|
|
|
)
|
|
|
|
|
|
|
|
# The method name should default to `get_{field_name}`.
|
|
|
|
if self.method_name is None:
|
|
|
|
self.method_name = default_method_name
|
|
|
|
|
|
|
|
super(SerializerMethodField, self).bind(field_name, parent)
|
|
|
|
|
2014-09-12 12:49:35 +04:00
|
|
|
def to_representation(self, value):
|
2014-09-25 15:09:12 +04:00
|
|
|
method = getattr(self.parent, self.method_name)
|
2014-08-29 19:46:26 +04:00
|
|
|
return method(value)
|
2014-09-08 17:24:05 +04:00
|
|
|
|
|
|
|
|
|
|
|
class ModelField(Field):
|
|
|
|
"""
|
|
|
|
A generic field that can be used against an arbitrary model field.
|
|
|
|
|
2014-09-09 20:46:28 +04:00
|
|
|
This is used by `ModelSerializer` when dealing with custom model fields,
|
|
|
|
that do not have a serializer field to be mapped to.
|
|
|
|
"""
|
|
|
|
def __init__(self, model_field, **kwargs):
|
|
|
|
self.model_field = model_field
|
|
|
|
kwargs['source'] = '*'
|
|
|
|
super(ModelField, self).__init__(**kwargs)
|
2014-09-08 17:24:05 +04:00
|
|
|
|
2014-09-12 12:49:35 +04:00
|
|
|
def to_internal_value(self, data):
|
2014-09-08 17:24:05 +04:00
|
|
|
rel = getattr(self.model_field, 'rel', None)
|
|
|
|
if rel is not None:
|
|
|
|
return rel.to._meta.get_field(rel.field_name).to_python(data)
|
|
|
|
return self.model_field.to_python(data)
|
|
|
|
|
2014-09-12 12:49:35 +04:00
|
|
|
def to_representation(self, obj):
|
2014-09-08 17:24:05 +04:00
|
|
|
value = self.model_field._get_val_from_obj(obj)
|
|
|
|
if is_protected_type(value):
|
|
|
|
return value
|
|
|
|
return self.model_field.value_to_string(obj)
|