/usr/lib/python3/dist-packages/restless/preparers.py is in python3-restless 2.1.1-1.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
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 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 | class Preparer(object):
"""
A plain preparation object which just passes through data.
It also is relevant as the protocol subclasses should implement to work with
Restless.
"""
def __init__(self):
super(Preparer, self).__init__()
def prepare(self, data):
"""
Handles actually transforming the data.
By default, this does nothing & simply returns the data passed to it.
"""
return data
class FieldsPreparer(Preparer):
"""
A more complex preparation object, this will return a given set of fields.
This takes a ``fields`` parameter, which should be a dictionary of
keys (fieldnames to expose to the user) & values (a dotted lookup path to
the desired attribute/key on the object).
Example::
preparer = FieldsPreparer(fields={
# ``user`` is the key the client will see.
# ``author.pk`` is the dotted path lookup ``FieldsPreparer``
# will traverse on the data to return a value.
'user': 'author.pk',
})
"""
def __init__(self, fields):
super(FieldsPreparer, self).__init__()
self.fields = fields
def prepare(self, data):
"""
Handles transforming the provided data into the fielded data that should
be exposed to the end user.
Uses the ``lookup_data`` method to traverse dotted paths.
Returns a dictionary of data as the response.
"""
result = {}
if not self.fields:
# No fields specified. Serialize everything.
return data
for fieldname, lookup in self.fields.items():
if isinstance(lookup, SubPreparer):
result[fieldname] = lookup.prepare(data)
else:
result[fieldname] = self.lookup_data(lookup, data)
return result
def lookup_data(self, lookup, data):
"""
Given a lookup string, attempts to descend through nested data looking for
the value.
Can work with either dictionary-alikes or objects (or any combination of
those).
Lookups should be a string. If it is a dotted path, it will be split on
``.`` & it will traverse through to find the final value. If not, it will
simply attempt to find either a key or attribute of that name & return it.
Example::
>>> data = {
... 'type': 'message',
... 'greeting': {
... 'en': 'hello',
... 'fr': 'bonjour',
... 'es': 'hola',
... },
... 'person': Person(
... name='daniel'
... )
... }
>>> lookup_data('type', data)
'message'
>>> lookup_data('greeting.en', data)
'hello'
>>> lookup_data('person.name', data)
'daniel'
"""
value = data
parts = lookup.split('.')
if not parts or not parts[0]:
return value
part = parts[0]
remaining_lookup = '.'.join(parts[1:])
if callable(getattr(data, 'keys', None)) and hasattr(data, '__getitem__'):
# Dictionary enough for us.
value = data[part]
elif data is not None:
# Assume it's an object.
value = getattr(data, part)
# Call if it's callable except if it's a Django DB manager instance
# We check if is a manager by checking the db_manager (duck typing)
if callable(value) and not hasattr(value, 'db_manager'):
value = value()
if not remaining_lookup:
return value
# There's more to lookup, so dive in recursively.
return self.lookup_data(remaining_lookup, value)
class SubPreparer(FieldsPreparer):
"""
A preparation class designed to be used within other preparers.
This is primary to enable deeply-nested structures, allowing you
to compose/share definitions as well. Typical usage consists of creating
a configured instance of a FieldsPreparer, then use a `SubPreparer` to
pull it in.
Example::
# First, define the nested fields you'd like to expose.
author_preparer = FieldsPreparer(fields={
'id': 'pk',
'username': 'username',
'name': 'get_full_name',
})
# Then, in the main preparer, pull them in using `SubPreparer`.
preparer = FieldsPreparer(fields={
'author': SubPreparer('user', author_preparer),
# Other fields can come before/follow as normal.
'content': 'post',
'created': 'created_at',
})
"""
def __init__(self, lookup, preparer):
self.lookup = lookup
self.preparer = preparer
def get_inner_data(self, data):
"""
Used internally so that the correct data is extracted out of the
broader dataset, allowing the preparer being called to deal with just
the expected subset.
"""
return self.lookup_data(self.lookup, data)
def prepare(self, data):
"""
Handles passing the data to the configured preparer.
Uses the ``get_inner_data`` method to provide the correct subset of
the data.
Returns a dictionary of data as the response.
"""
return self.preparer.prepare(self.get_inner_data(data))
class CollectionSubPreparer(SubPreparer):
"""
A preparation class designed to handle collections of data.
This is useful in the case where you have a 1-to-many or many-to-many
relationship of data to expose as part of the parent data.
Example::
# First, set up a preparer that handles the data for each thing in
# the broader collection.
comment_preparer = FieldsPreparer(fields={
'comment': 'comment_text',
'created': 'created',
})
# Then use it with the ``CollectionSubPreparer`` to create a list
# of prepared sub items.
preparer = FieldsPreparer(fields={
# A normal blog post field.
'post': 'post_text',
# All the comments on the post.
'comments': CollectionSubPreparer('comments.all', comment_preparer),
})
"""
def prepare(self, data):
"""
Handles passing each item in the collection data to the configured
subpreparer.
Uses a loop and the ``get_inner_data`` method to provide the correct
item of the data.
Returns a list of data as the response.
"""
result = []
for item in self.get_inner_data(data):
result.append(self.preparer.prepare(item))
return result
|