/
fields.py
127 lines (105 loc) · 4.17 KB
/
fields.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
import inspect
import importlib
from rest_framework.fields import Field
from rest_framework.serializers import BaseSerializer
def _signature_parameters(func):
try:
inspect.signature
except AttributeError:
# Python 2.x
return inspect.getargspec(func).args
else:
# Python 3.x
return inspect.signature(func).parameters.keys()
class RecursiveField(Field):
"""
A field that gets its representation from its parent.
This method could be used to serialize a tree structure, a linked list, or
even a directed acyclic graph. As with all recursive things, it is
important to keep the base case in mind. In the case of the tree serializer
example below, the base case is a node with an empty list of children. In
the case of the list serializer below, the base case is when `next==None`.
Above all, beware of cyclical references.
Examples:
class TreeSerializer(self):
children = ListField(child=RecursiveField())
class ListSerializer(self):
next = RecursiveField(allow_null=True)
"""
# This list of attributes determined by the attributes that
# `rest_framework.serializers` calls to on a field object
PROXIED_ATTRS = (
# methods
'get_value',
'get_initial',
'run_validation',
'get_attribute',
'to_representation',
# attributes
'field_name',
'source',
'read_only',
'default',
'source_attrs',
'write_only',
)
def __init__(self, to=None, **kwargs):
"""
arguments:
to - `None`, the name of another serializer defined in the same module
as this serializer, or the fully qualified import path to another
serializer. e.g. `ExampleSerializer` or
`path.to.module.ExampleSerializer`
"""
self.to = to
self.init_kwargs = kwargs
self._proxied = None
# need to call super-constructor to support ModelSerializer
super_kwargs = dict(
(key, kwargs[key])
for key in kwargs
if key in _signature_parameters(Field.__init__)
)
super(RecursiveField, self).__init__(**super_kwargs)
def bind(self, field_name, parent):
# Extra-lazy binding, because when we are nested in a ListField, the
# RecursiveField will be bound before the ListField is bound
self.bind_args = (field_name, parent)
@property
def proxied(self):
if not self._proxied:
if self.bind_args:
field_name, parent = self.bind_args
if hasattr(parent, 'child') and parent.child is self:
# RecursiveField nested inside of a ListField
parent_class = parent.parent.__class__
else:
# RecursiveField directly inside a Serializer
parent_class = parent.__class__
assert issubclass(parent_class, BaseSerializer)
if self.to is None:
proxied_class = parent_class
else:
try:
module_name, class_name = self.to.rsplit('.', 1)
except ValueError:
module_name, class_name = parent_class.__module__, self.to
try:
proxied_class = getattr(
importlib.import_module(module_name), class_name)
except Exception as e:
raise ImportError(
'could not locate serializer %s' % self.to, e)
# Create a new serializer instance and proxy it
proxied = proxied_class(**self.init_kwargs)
proxied.bind(field_name, parent)
self._proxied = proxied
return self._proxied
def __getattribute__(self, name):
if name in RecursiveField.PROXIED_ATTRS:
try:
proxied = object.__getattribute__(self, 'proxied')
return getattr(proxied, name)
except AttributeError:
pass
return object.__getattribute__(self, name)