appier 1.31.4__py2.py3-none-any.whl → 1.32.0__py2.py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- appier/__init__.py +333 -52
- appier/amqp.py +29 -30
- appier/api.py +214 -212
- appier/asgi.py +54 -55
- appier/async_neo.py +46 -35
- appier/async_old.py +55 -42
- appier/asynchronous.py +7 -13
- appier/base.py +1762 -1429
- appier/bus.py +51 -52
- appier/cache.py +99 -84
- appier/common.py +9 -11
- appier/component.py +17 -19
- appier/compress.py +25 -28
- appier/config.py +96 -73
- appier/controller.py +9 -15
- appier/crypt.py +25 -21
- appier/data.py +73 -57
- appier/defines.py +191 -226
- appier/exceptions.py +103 -63
- appier/execution.py +94 -88
- appier/export.py +90 -88
- appier/extra.py +6 -13
- appier/extra_neo.py +8 -11
- appier/extra_old.py +18 -16
- appier/geo.py +57 -47
- appier/git.py +101 -90
- appier/graph.py +23 -24
- appier/http.py +520 -398
- appier/legacy.py +373 -180
- appier/log.py +90 -97
- appier/meta.py +42 -42
- appier/mock.py +32 -34
- appier/model.py +793 -681
- appier/model_a.py +208 -183
- appier/mongo.py +183 -107
- appier/observer.py +39 -31
- appier/part.py +23 -24
- appier/preferences.py +44 -47
- appier/queuing.py +78 -96
- appier/redisdb.py +40 -35
- appier/request.py +227 -175
- appier/scheduler.py +13 -18
- appier/serialize.py +37 -31
- appier/session.py +161 -147
- appier/settings.py +2 -11
- appier/smtp.py +53 -49
- appier/storage.py +39 -33
- appier/structures.py +50 -45
- appier/test/__init__.py +2 -11
- appier/test/base.py +111 -108
- appier/test/cache.py +28 -35
- appier/test/config.py +10 -19
- appier/test/crypt.py +3 -12
- appier/test/data.py +3 -12
- appier/test/exceptions.py +8 -17
- appier/test/export.py +16 -33
- appier/test/graph.py +27 -60
- appier/test/http.py +42 -54
- appier/test/legacy.py +20 -30
- appier/test/log.py +14 -35
- appier/test/mock.py +27 -123
- appier/test/model.py +79 -91
- appier/test/part.py +5 -14
- appier/test/preferences.py +5 -13
- appier/test/queuing.py +29 -37
- appier/test/request.py +61 -73
- appier/test/serialize.py +12 -23
- appier/test/session.py +10 -19
- appier/test/smtp.py +8 -14
- appier/test/structures.py +20 -24
- appier/test/typesf.py +14 -28
- appier/test/util.py +480 -438
- appier/typesf.py +251 -171
- appier/util.py +578 -407
- appier/validation.py +280 -143
- {appier-1.31.4.dist-info → appier-1.32.0.dist-info}/METADATA +6 -1
- appier-1.32.0.dist-info/RECORD +86 -0
- appier-1.31.4.dist-info/RECORD +0 -86
- {appier-1.31.4.dist-info → appier-1.32.0.dist-info}/LICENSE +0 -0
- {appier-1.31.4.dist-info → appier-1.32.0.dist-info}/WHEEL +0 -0
- {appier-1.31.4.dist-info → appier-1.32.0.dist-info}/top_level.txt +0 -0
appier/model.py
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
# -*- coding: utf-8 -*-
|
|
3
3
|
|
|
4
4
|
# Hive Appier Framework
|
|
5
|
-
# Copyright (c) 2008-
|
|
5
|
+
# Copyright (c) 2008-2024 Hive Solutions Lda.
|
|
6
6
|
#
|
|
7
7
|
# This file is part of Hive Appier Framework.
|
|
8
8
|
#
|
|
@@ -22,16 +22,7 @@
|
|
|
22
22
|
__author__ = "João Magalhães <joamag@hive.pt>"
|
|
23
23
|
""" The author(s) of the module """
|
|
24
24
|
|
|
25
|
-
|
|
26
|
-
""" The version of the module """
|
|
27
|
-
|
|
28
|
-
__revision__ = "$LastChangedRevision$"
|
|
29
|
-
""" The revision number of the module """
|
|
30
|
-
|
|
31
|
-
__date__ = "$LastChangedDate$"
|
|
32
|
-
""" The last change date of the module """
|
|
33
|
-
|
|
34
|
-
__copyright__ = "Copyright (c) 2008-2022 Hive Solutions Lda."
|
|
25
|
+
__copyright__ = "Copyright (c) 2008-2024 Hive Solutions Lda."
|
|
35
26
|
""" The copyright for the module """
|
|
36
27
|
|
|
37
28
|
__license__ = "Apache License, Version 2.0"
|
|
@@ -65,13 +56,14 @@ RE = lambda v: [i for i in v if not i == ""]
|
|
|
65
56
|
empty element from the provided list values """
|
|
66
57
|
|
|
67
58
|
BUILDERS = {
|
|
68
|
-
legacy.UNICODE
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
59
|
+
legacy.UNICODE: lambda v: v.decode("utf-8")
|
|
60
|
+
if isinstance(v, legacy.BYTES)
|
|
61
|
+
else legacy.UNICODE(v),
|
|
62
|
+
list: lambda v: RE(v)
|
|
63
|
+
if isinstance(v, list)
|
|
64
|
+
else (json.loads(v) if isinstance(v, legacy.UNICODE) else RE([v])),
|
|
65
|
+
dict: lambda v: json.loads(v) if isinstance(v, legacy.UNICODE) else dict(v),
|
|
66
|
+
bool: lambda v: v if isinstance(v, bool) else not v in ("", "0", "false", "False"),
|
|
75
67
|
}
|
|
76
68
|
""" The map associating the various types with the
|
|
77
69
|
custom builder functions to be used when applying
|
|
@@ -79,32 +71,37 @@ the types function, this is relevant for the built-in
|
|
|
79
71
|
types that are meant to avoid using the default constructor """
|
|
80
72
|
|
|
81
73
|
BUILDERS_META = dict(
|
|
82
|
-
text
|
|
83
|
-
country
|
|
84
|
-
longtext
|
|
85
|
-
map
|
|
86
|
-
longmap
|
|
87
|
-
date
|
|
88
|
-
datetetime
|
|
89
|
-
file
|
|
74
|
+
text=BUILDERS[legacy.UNICODE],
|
|
75
|
+
country=BUILDERS[legacy.UNICODE],
|
|
76
|
+
longtext=BUILDERS[legacy.UNICODE],
|
|
77
|
+
map=BUILDERS[dict],
|
|
78
|
+
longmap=BUILDERS[dict],
|
|
79
|
+
date=lambda v: float(v),
|
|
80
|
+
datetetime=lambda v: float(v),
|
|
81
|
+
file=None,
|
|
90
82
|
)
|
|
91
83
|
""" Map equivalent to the builders map but appliable
|
|
92
84
|
for meta based type naming, this map should be used as
|
|
93
85
|
an extension to the base builder map """
|
|
94
86
|
|
|
95
87
|
METAS = dict(
|
|
96
|
-
text
|
|
97
|
-
enum
|
|
98
|
-
list
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
88
|
+
text=lambda v, d, c=None: v,
|
|
89
|
+
enum=lambda v, d, c=None: d["enum"].get(v, None),
|
|
90
|
+
list=lambda v, d, c=None: json.dumps(
|
|
91
|
+
v, ensure_ascii=False, cls=c._encoder() if c else None
|
|
92
|
+
),
|
|
93
|
+
map=lambda v, d, c=None: json.dumps(
|
|
94
|
+
v, ensure_ascii=False, cls=c._encoder() if c else None
|
|
95
|
+
),
|
|
96
|
+
longmap=lambda v, d, c=None: json.dumps(
|
|
97
|
+
v, ensure_ascii=False, cls=c._encoder() if c else None
|
|
98
|
+
),
|
|
99
|
+
date=lambda v, d, c=None: datetime.datetime.utcfromtimestamp(float(v)).strftime(
|
|
100
|
+
"%d %b %Y"
|
|
101
|
+
),
|
|
102
|
+
datetime=lambda v, d, c=None: datetime.datetime.utcfromtimestamp(float(v)).strftime(
|
|
103
|
+
"%d %b %Y %H:%M:%S"
|
|
104
|
+
),
|
|
108
105
|
)
|
|
109
106
|
""" The map that contains the various mapping functions
|
|
110
107
|
for the meta types that may be described for a field under
|
|
@@ -112,13 +109,13 @@ the current model specification, the resulting value for
|
|
|
112
109
|
each of these functions should preferably be a string """
|
|
113
110
|
|
|
114
111
|
TYPE_DEFAULTS = {
|
|
115
|
-
legacy.BYTES
|
|
116
|
-
legacy.UNICODE
|
|
117
|
-
int
|
|
118
|
-
float
|
|
119
|
-
bool
|
|
120
|
-
list
|
|
121
|
-
dict
|
|
112
|
+
legacy.BYTES: None,
|
|
113
|
+
legacy.UNICODE: None,
|
|
114
|
+
int: None,
|
|
115
|
+
float: None,
|
|
116
|
+
bool: False,
|
|
117
|
+
list: lambda: [],
|
|
118
|
+
dict: lambda: {},
|
|
122
119
|
}
|
|
123
120
|
""" The default values to be set when a type
|
|
124
121
|
conversion fails for the provided string value
|
|
@@ -126,82 +123,70 @@ the resulting value may be returned when a validation
|
|
|
126
123
|
fails an so it must be used carefully """
|
|
127
124
|
|
|
128
125
|
TYPE_META = {
|
|
129
|
-
typesf.File
|
|
130
|
-
typesf.Files
|
|
131
|
-
typesf.Reference
|
|
132
|
-
typesf.References
|
|
133
|
-
legacy.BYTES
|
|
134
|
-
legacy.UNICODE
|
|
135
|
-
int
|
|
136
|
-
float
|
|
126
|
+
typesf.File: "file",
|
|
127
|
+
typesf.Files: "files",
|
|
128
|
+
typesf.Reference: "reference",
|
|
129
|
+
typesf.References: "references",
|
|
130
|
+
legacy.BYTES: "string",
|
|
131
|
+
legacy.UNICODE: "string",
|
|
132
|
+
int: "number",
|
|
133
|
+
float: "float",
|
|
137
134
|
bool: "bool",
|
|
138
|
-
list
|
|
139
|
-
dict
|
|
135
|
+
list: "list",
|
|
136
|
+
dict: "map",
|
|
140
137
|
}
|
|
141
138
|
""" Dictionary that defines the default mapping for each
|
|
142
139
|
of the base data types against the associated default meta
|
|
143
140
|
values for each for them, these meta type values are going
|
|
144
141
|
to be used mostly for presentation purposes """
|
|
145
142
|
|
|
146
|
-
TYPE_REFERENCES = (
|
|
147
|
-
typesf.Reference,
|
|
148
|
-
typesf.References
|
|
149
|
-
)
|
|
143
|
+
TYPE_REFERENCES = (typesf.Reference, typesf.References)
|
|
150
144
|
""" The various data types that are considered to be references
|
|
151
145
|
so that they are lazy loaded from the data source, these kind
|
|
152
146
|
of types should be compliant to a common interface so that they
|
|
153
147
|
may be used "blindly" from an external entity """
|
|
154
148
|
|
|
155
149
|
REVERSE = dict(
|
|
156
|
-
descending
|
|
157
|
-
ascending
|
|
150
|
+
descending="ascending",
|
|
151
|
+
ascending="descending",
|
|
158
152
|
)
|
|
159
153
|
""" The reverse order dictionary that maps a certain
|
|
160
154
|
order direction (as a string) with the opposite one
|
|
161
155
|
this may be used to "calculate" the reverse value """
|
|
162
156
|
|
|
163
|
-
DIRTY_PARAMS = (
|
|
164
|
-
"map",
|
|
165
|
-
"rules",
|
|
166
|
-
"meta",
|
|
167
|
-
"build",
|
|
168
|
-
"skip",
|
|
169
|
-
"limit",
|
|
170
|
-
"sort",
|
|
171
|
-
"raise_e"
|
|
172
|
-
)
|
|
157
|
+
DIRTY_PARAMS = ("map", "rules", "meta", "build", "skip", "limit", "sort", "raise_e")
|
|
173
158
|
""" The set containing the complete set of parameter names for
|
|
174
159
|
the parameters that are considered to be dirty and that should
|
|
175
160
|
be cleaned from any query operation on the data source, otherwise
|
|
176
161
|
serious consequences may occur """
|
|
177
162
|
|
|
178
163
|
OPERATORS = {
|
|
179
|
-
"eq"
|
|
180
|
-
"equals"
|
|
181
|
-
"ne"
|
|
182
|
-
"not_equals"
|
|
183
|
-
"in"
|
|
184
|
-
"nin"
|
|
185
|
-
"not_in"
|
|
186
|
-
"like"
|
|
187
|
-
"likei"
|
|
188
|
-
"llike"
|
|
189
|
-
"llikei"
|
|
190
|
-
"rlike"
|
|
191
|
-
"rlikei"
|
|
192
|
-
"gt"
|
|
193
|
-
"greater"
|
|
194
|
-
"gte"
|
|
195
|
-
"greater_equal"
|
|
196
|
-
"lt"
|
|
197
|
-
"lesser"
|
|
198
|
-
"lte"
|
|
199
|
-
"lesser_equal"
|
|
200
|
-
"null"
|
|
201
|
-
"is_null"
|
|
202
|
-
"not_null"
|
|
203
|
-
"is_not_null"
|
|
204
|
-
"contains"
|
|
164
|
+
"eq": None,
|
|
165
|
+
"equals": None,
|
|
166
|
+
"ne": "$ne",
|
|
167
|
+
"not_equals": "$ne",
|
|
168
|
+
"in": "$in",
|
|
169
|
+
"nin": "$nin",
|
|
170
|
+
"not_in": "$nin",
|
|
171
|
+
"like": "$regex",
|
|
172
|
+
"likei": "$regex",
|
|
173
|
+
"llike": "$regex",
|
|
174
|
+
"llikei": "$regex",
|
|
175
|
+
"rlike": "$regex",
|
|
176
|
+
"rlikei": "$regex",
|
|
177
|
+
"gt": "$gt",
|
|
178
|
+
"greater": "$gt",
|
|
179
|
+
"gte": "$gte",
|
|
180
|
+
"greater_equal": "$gte",
|
|
181
|
+
"lt": "$lt",
|
|
182
|
+
"lesser": "$lt",
|
|
183
|
+
"lte": "$lte",
|
|
184
|
+
"lesser_equal": "$lte",
|
|
185
|
+
"null": None,
|
|
186
|
+
"is_null": None,
|
|
187
|
+
"not_null": "$ne",
|
|
188
|
+
"is_not_null": "$ne",
|
|
189
|
+
"contains": "$all",
|
|
205
190
|
}
|
|
206
191
|
""" The map containing the mapping association between the
|
|
207
192
|
normalized version of the operators and the infra-structure
|
|
@@ -210,29 +195,25 @@ of the values don't have a valid mapping for this operations
|
|
|
210
195
|
the operator must be ignored and not used explicitly """
|
|
211
196
|
|
|
212
197
|
VALUE_METHODS = {
|
|
213
|
-
"in"
|
|
214
|
-
"not_in"
|
|
215
|
-
"like"
|
|
216
|
-
"likei"
|
|
217
|
-
"llike"
|
|
218
|
-
"llikei"
|
|
219
|
-
"rlike"
|
|
220
|
-
"rlikei"
|
|
221
|
-
"null"
|
|
222
|
-
"is_null"
|
|
223
|
-
"not_null"
|
|
224
|
-
"is_not_null"
|
|
225
|
-
"contains"
|
|
198
|
+
"in": lambda v, t: [t(v) for v in v.split(";")],
|
|
199
|
+
"not_in": lambda v, t: [t(v) for v in v.split(";")],
|
|
200
|
+
"like": lambda v, t: "^.*" + legacy.UNICODE(re.escape(v)) + ".*$",
|
|
201
|
+
"likei": lambda v, t: "^.*" + legacy.UNICODE(re.escape(v)) + ".*$",
|
|
202
|
+
"llike": lambda v, t: "^.*" + legacy.UNICODE(re.escape(v)) + "$",
|
|
203
|
+
"llikei": lambda v, t: "^.*" + legacy.UNICODE(re.escape(v)) + "$",
|
|
204
|
+
"rlike": lambda v, t: "^" + legacy.UNICODE(re.escape(v)) + ".*$",
|
|
205
|
+
"rlikei": lambda v, t: "^" + legacy.UNICODE(re.escape(v)) + ".*$",
|
|
206
|
+
"null": lambda v, t: None,
|
|
207
|
+
"is_null": lambda v, t: None,
|
|
208
|
+
"not_null": lambda v, t: None,
|
|
209
|
+
"is_not_null": lambda v, t: None,
|
|
210
|
+
"contains": lambda v, t: [t(v) for v in v.split(";")],
|
|
226
211
|
}
|
|
227
212
|
""" Map that associates each of the normalized operations with
|
|
228
213
|
an inline function that together with the data type maps the
|
|
229
214
|
the base string based value into the target normalized value """
|
|
230
215
|
|
|
231
|
-
INSENSITIVE = {
|
|
232
|
-
"likei" : True,
|
|
233
|
-
"llikei" : True,
|
|
234
|
-
"rlikei" : True
|
|
235
|
-
}
|
|
216
|
+
INSENSITIVE = {"likei": True, "llikei": True, "rlikei": True}
|
|
236
217
|
""" The map that associates the various operators with the boolean
|
|
237
218
|
values that define if an insensitive base search should be used
|
|
238
219
|
instead of the "typical" sensitive search """
|
|
@@ -243,10 +224,12 @@ EXTRA_CLS = []
|
|
|
243
224
|
|
|
244
225
|
if legacy.PYTHON_ASYNC_GEN:
|
|
245
226
|
from . import model_a
|
|
227
|
+
|
|
246
228
|
EXTRA_CLS.append(model_a.ModelAsync)
|
|
247
229
|
|
|
248
230
|
BUILDERS.update(BUILDERS_META)
|
|
249
231
|
|
|
232
|
+
|
|
250
233
|
class Model(legacy.with_meta(meta.Ordered, observer.Observable, *EXTRA_CLS)):
|
|
251
234
|
"""
|
|
252
235
|
Abstract model class from which all the models should
|
|
@@ -273,61 +256,75 @@ class Model(legacy.with_meta(meta.Ordered, observer.Observable, *EXTRA_CLS)):
|
|
|
273
256
|
instance.__dict__["ref"] = None
|
|
274
257
|
return instance
|
|
275
258
|
|
|
276
|
-
def __init__(self, model
|
|
259
|
+
def __init__(self, model=None, **kwargs):
|
|
277
260
|
fill = kwargs.pop("fill", True)
|
|
278
261
|
model = model or {}
|
|
279
|
-
if fill:
|
|
262
|
+
if fill:
|
|
263
|
+
model = self.__class__.fill(model)
|
|
280
264
|
self.__dict__["model"] = model
|
|
281
265
|
self.__dict__["owner"] = common.base().APP or None
|
|
282
266
|
self.__dict__["ref"] = kwargs.pop("ref", None)
|
|
283
|
-
for name, value in kwargs.items():
|
|
267
|
+
for name, value in kwargs.items():
|
|
268
|
+
setattr(self, name, value)
|
|
284
269
|
for name, method in self.__class__._extra_methods:
|
|
285
|
-
if legacy.PYTHON_3:
|
|
286
|
-
|
|
270
|
+
if legacy.PYTHON_3:
|
|
271
|
+
bound_method = types.MethodType(method, self)
|
|
272
|
+
else:
|
|
273
|
+
bound_method = types.MethodType(method, self, self.__class__)
|
|
287
274
|
setattr(self, name, bound_method)
|
|
288
275
|
observer.Observable.__init__(self)
|
|
289
276
|
|
|
290
277
|
def __str__(self):
|
|
291
278
|
cls = self.__class__
|
|
292
279
|
default = cls.default()
|
|
293
|
-
if not default:
|
|
294
|
-
|
|
280
|
+
if not default:
|
|
281
|
+
return cls._name()
|
|
282
|
+
if not default in self.model:
|
|
283
|
+
return cls._name()
|
|
295
284
|
value = self.model[default]
|
|
296
|
-
if value == None:
|
|
285
|
+
if value == None:
|
|
286
|
+
value = ""
|
|
297
287
|
is_string = legacy.is_str(value)
|
|
298
288
|
return value if is_string else str(value)
|
|
299
289
|
|
|
300
290
|
def __unicode__(self):
|
|
301
291
|
cls = self.__class__
|
|
302
292
|
default = cls.default()
|
|
303
|
-
if not default:
|
|
293
|
+
if not default:
|
|
294
|
+
return cls._name()
|
|
304
295
|
value = self.model[default]
|
|
305
|
-
if value == None:
|
|
296
|
+
if value == None:
|
|
297
|
+
value = ""
|
|
306
298
|
is_unicode = legacy.is_unicode(value)
|
|
307
299
|
return value if is_unicode else legacy.UNICODE(value)
|
|
308
300
|
|
|
309
301
|
def __getattribute__(self, name):
|
|
310
302
|
try:
|
|
311
303
|
model = object.__getattribute__(self, "model")
|
|
312
|
-
if name in model:
|
|
313
|
-
|
|
304
|
+
if name in model:
|
|
305
|
+
return model[name]
|
|
306
|
+
except AttributeError:
|
|
307
|
+
pass
|
|
314
308
|
cls = object.__getattribute__(self, "__class__")
|
|
315
309
|
definition = cls.definition()
|
|
316
|
-
if name in definition:
|
|
317
|
-
"attribute '%s' is not set" % name
|
|
318
|
-
)
|
|
310
|
+
if name in definition:
|
|
311
|
+
raise AttributeError("attribute '%s' is not set" % name)
|
|
319
312
|
return object.__getattribute__(self, name)
|
|
320
313
|
|
|
321
314
|
def __setattr__(self, name, value):
|
|
322
315
|
is_base = name in self.__dict__
|
|
323
|
-
if is_base:
|
|
324
|
-
|
|
316
|
+
if is_base:
|
|
317
|
+
self.__dict__[name] = value
|
|
318
|
+
else:
|
|
319
|
+
self.model[name] = value
|
|
325
320
|
|
|
326
321
|
def __delattr__(self, name):
|
|
327
322
|
try:
|
|
328
323
|
model = object.__getattribute__(self, "model")
|
|
329
|
-
if name in model:
|
|
330
|
-
|
|
324
|
+
if name in model:
|
|
325
|
+
del model[name]
|
|
326
|
+
except AttributeError:
|
|
327
|
+
pass
|
|
331
328
|
|
|
332
329
|
def __len__(self):
|
|
333
330
|
return self.model.__len__()
|
|
@@ -353,12 +350,12 @@ class Model(legacy.with_meta(meta.Ordered, observer.Observable, *EXTRA_CLS)):
|
|
|
353
350
|
@classmethod
|
|
354
351
|
def new(
|
|
355
352
|
cls,
|
|
356
|
-
model
|
|
357
|
-
form
|
|
358
|
-
safe
|
|
359
|
-
build
|
|
360
|
-
fill
|
|
361
|
-
new
|
|
353
|
+
model=None,
|
|
354
|
+
form=True,
|
|
355
|
+
safe=True,
|
|
356
|
+
build=False,
|
|
357
|
+
fill=True,
|
|
358
|
+
new=True,
|
|
362
359
|
**kwargs
|
|
363
360
|
):
|
|
364
361
|
"""
|
|
@@ -408,26 +405,24 @@ class Model(legacy.with_meta(meta.Ordered, observer.Observable, *EXTRA_CLS)):
|
|
|
408
405
|
model and after the proper validations are performed on it.
|
|
409
406
|
"""
|
|
410
407
|
|
|
411
|
-
if model == None:
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
408
|
+
if model == None:
|
|
409
|
+
model = util.get_object() if form else dict(kwargs)
|
|
410
|
+
if fill:
|
|
411
|
+
model = cls.fill(model, safe=not new)
|
|
412
|
+
instance = cls(fill=False)
|
|
413
|
+
instance.apply(model, form=form, safe_a=safe)
|
|
414
|
+
if build:
|
|
415
|
+
cls.build(instance.model, map=False)
|
|
416
|
+
if new:
|
|
417
|
+
instance.assert_is_new()
|
|
417
418
|
return instance
|
|
418
419
|
|
|
419
420
|
@classmethod
|
|
420
|
-
def old(cls, model
|
|
421
|
-
return cls.new(
|
|
422
|
-
model = model,
|
|
423
|
-
form = form,
|
|
424
|
-
safe = safe,
|
|
425
|
-
build = build,
|
|
426
|
-
new = False
|
|
427
|
-
)
|
|
421
|
+
def old(cls, model=None, form=True, safe=True, build=False):
|
|
422
|
+
return cls.new(model=model, form=form, safe=safe, build=build, new=False)
|
|
428
423
|
|
|
429
424
|
@classmethod
|
|
430
|
-
def wrap(cls, models, build
|
|
425
|
+
def wrap(cls, models, build=True, handler=None, **kwargs):
|
|
431
426
|
"""
|
|
432
427
|
"Wraps" the provided sequence (or single set) of model based data into a
|
|
433
428
|
sequence of models (or a single model) so that proper business logic may
|
|
@@ -460,166 +455,177 @@ class Model(legacy.with_meta(meta.Ordered, observer.Observable, *EXTRA_CLS)):
|
|
|
460
455
|
"""
|
|
461
456
|
|
|
462
457
|
is_sequence = isinstance(models, (list, tuple))
|
|
463
|
-
if not is_sequence:
|
|
458
|
+
if not is_sequence:
|
|
459
|
+
models = [models]
|
|
464
460
|
wrapping = []
|
|
465
461
|
for model in models:
|
|
466
|
-
if not isinstance(model, dict):
|
|
467
|
-
|
|
462
|
+
if not isinstance(model, dict):
|
|
463
|
+
continue
|
|
464
|
+
_model = cls(model=model, **kwargs)
|
|
468
465
|
handler and handler(_model.model)
|
|
469
|
-
build and cls.build(_model.model, map
|
|
466
|
+
build and cls.build(_model.model, map=False)
|
|
470
467
|
wrapping.append(_model)
|
|
471
|
-
if is_sequence:
|
|
472
|
-
|
|
468
|
+
if is_sequence:
|
|
469
|
+
return wrapping
|
|
470
|
+
else:
|
|
471
|
+
return wrapping[0] if wrapping else None
|
|
473
472
|
|
|
474
473
|
@classmethod
|
|
475
|
-
def singleton(
|
|
476
|
-
cls,
|
|
477
|
-
model = None,
|
|
478
|
-
form = True,
|
|
479
|
-
safe = True,
|
|
480
|
-
build = False,
|
|
481
|
-
*args,
|
|
482
|
-
**kwargs
|
|
483
|
-
):
|
|
484
|
-
instance = cls.get(raise_e = False, *args, **kwargs)
|
|
474
|
+
def singleton(cls, model=None, form=True, safe=True, build=False, *args, **kwargs):
|
|
475
|
+
instance = cls.get(raise_e=False, *args, **kwargs)
|
|
485
476
|
if instance:
|
|
486
|
-
instance.apply(model, form
|
|
477
|
+
instance.apply(model, form=form, safe_a=safe)
|
|
487
478
|
else:
|
|
488
|
-
instance = cls.old(
|
|
489
|
-
model = model,
|
|
490
|
-
form = form,
|
|
491
|
-
safe = safe,
|
|
492
|
-
build = build
|
|
493
|
-
)
|
|
479
|
+
instance = cls.old(model=model, form=form, safe=safe, build=build)
|
|
494
480
|
return instance
|
|
495
481
|
|
|
496
482
|
@classmethod
|
|
497
483
|
def get(cls, *args, **kwargs):
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
(
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
484
|
+
(
|
|
485
|
+
fields,
|
|
486
|
+
eager,
|
|
487
|
+
eager_l,
|
|
488
|
+
map,
|
|
489
|
+
rules,
|
|
490
|
+
meta,
|
|
491
|
+
build,
|
|
492
|
+
fill,
|
|
493
|
+
resolve_a,
|
|
494
|
+
skip,
|
|
495
|
+
limit,
|
|
496
|
+
sort,
|
|
497
|
+
raise_e,
|
|
498
|
+
) = cls._get_attrs(
|
|
499
|
+
kwargs,
|
|
500
|
+
(
|
|
501
|
+
("fields", None),
|
|
502
|
+
("eager", None),
|
|
503
|
+
("eager_l", None),
|
|
504
|
+
("map", False),
|
|
505
|
+
("rules", True),
|
|
506
|
+
("meta", False),
|
|
507
|
+
("build", True),
|
|
508
|
+
("fill", True),
|
|
509
|
+
("resolve_a", None),
|
|
510
|
+
("skip", 0),
|
|
511
|
+
("limit", 0),
|
|
512
|
+
("sort", None),
|
|
513
|
+
("raise_e", True),
|
|
514
|
+
),
|
|
515
|
+
)
|
|
525
516
|
|
|
526
517
|
# in case there's a sort field and the safe search mode is enabled
|
|
527
518
|
# we must add sorting by the `_id` field so that the retrieval is
|
|
528
519
|
# considered to be deterministic, otherwise some DB implementations
|
|
529
520
|
# will not respect the same sorting sequence across different calls
|
|
530
521
|
if sort and (skip or limit):
|
|
531
|
-
if not isinstance(sort, list):
|
|
522
|
+
if not isinstance(sort, list):
|
|
523
|
+
sort = list(sort)
|
|
532
524
|
sort.append(["_id", 1])
|
|
533
525
|
|
|
534
|
-
if eager_l == None:
|
|
535
|
-
|
|
536
|
-
if
|
|
537
|
-
|
|
526
|
+
if eager_l == None:
|
|
527
|
+
eager_l = map
|
|
528
|
+
if resolve_a == None:
|
|
529
|
+
resolve_a = map
|
|
530
|
+
if eager_l:
|
|
531
|
+
eager = cls._eager_b(eager)
|
|
532
|
+
fields = cls._sniff(fields, rules=rules)
|
|
538
533
|
collection = cls._collection()
|
|
539
|
-
model = collection.find_one(
|
|
540
|
-
kwargs,
|
|
541
|
-
fields,
|
|
542
|
-
skip = skip,
|
|
543
|
-
limit = limit,
|
|
544
|
-
sort = sort
|
|
545
|
-
)
|
|
534
|
+
model = collection.find_one(kwargs, fields, skip=skip, limit=limit, sort=sort)
|
|
546
535
|
if not model and raise_e:
|
|
547
536
|
is_devel = common.is_devel()
|
|
548
|
-
if is_devel:
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
537
|
+
if is_devel:
|
|
538
|
+
message = "%s not found for %s" % (cls.__name__, str(kwargs))
|
|
539
|
+
else:
|
|
540
|
+
message = "%s not found" % cls.__name__
|
|
541
|
+
raise exceptions.NotFoundError(message=message)
|
|
542
|
+
if not model and not raise_e:
|
|
543
|
+
return model
|
|
552
544
|
cls.types(model)
|
|
553
|
-
if fill:
|
|
554
|
-
|
|
555
|
-
if
|
|
556
|
-
|
|
557
|
-
|
|
545
|
+
if fill:
|
|
546
|
+
cls.fill(model, safe=rules)
|
|
547
|
+
if build:
|
|
548
|
+
cls.build(model, map=map, rules=rules, meta=meta)
|
|
549
|
+
if eager:
|
|
550
|
+
model = cls._eager(model, eager, map=map)
|
|
551
|
+
if resolve_a:
|
|
552
|
+
model = cls._resolve_all(model, resolve=False)
|
|
553
|
+
return model if map else cls.old(model=model, safe=False)
|
|
558
554
|
|
|
559
555
|
@classmethod
|
|
560
556
|
def find(cls, *args, **kwargs):
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
(
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
557
|
+
(
|
|
558
|
+
fields,
|
|
559
|
+
eager,
|
|
560
|
+
eager_l,
|
|
561
|
+
map,
|
|
562
|
+
rules,
|
|
563
|
+
meta,
|
|
564
|
+
build,
|
|
565
|
+
fill,
|
|
566
|
+
resolve_a,
|
|
567
|
+
skip,
|
|
568
|
+
limit,
|
|
569
|
+
sort,
|
|
570
|
+
raise_e,
|
|
571
|
+
) = cls._get_attrs(
|
|
572
|
+
kwargs,
|
|
573
|
+
(
|
|
574
|
+
("fields", None),
|
|
575
|
+
("eager", None),
|
|
576
|
+
("eager_l", False),
|
|
577
|
+
("map", False),
|
|
578
|
+
("rules", True),
|
|
579
|
+
("meta", False),
|
|
580
|
+
("build", True),
|
|
581
|
+
("fill", True),
|
|
582
|
+
("resolve_a", None),
|
|
583
|
+
("skip", 0),
|
|
584
|
+
("limit", 0),
|
|
585
|
+
("sort", None),
|
|
586
|
+
("raise_e", False),
|
|
587
|
+
),
|
|
588
|
+
)
|
|
588
589
|
|
|
589
590
|
# in case there's a sort field and the safe search mode is enabled
|
|
590
591
|
# we must add sorting by the `_id` field so that the search is
|
|
591
592
|
# considered to be deterministic, otherwise some DB implementations
|
|
592
593
|
# will not respect the same sorting sequence across different calls
|
|
593
594
|
if sort and (skip or limit):
|
|
594
|
-
if not isinstance(sort, list):
|
|
595
|
+
if not isinstance(sort, list):
|
|
596
|
+
sort = list(sort)
|
|
595
597
|
sort.append(["_id", 1])
|
|
596
598
|
|
|
597
|
-
if resolve_a == None:
|
|
598
|
-
|
|
599
|
+
if resolve_a == None:
|
|
600
|
+
resolve_a = map
|
|
601
|
+
if eager_l:
|
|
602
|
+
eager = cls._eager_b(eager)
|
|
599
603
|
|
|
600
604
|
cls._find_s(kwargs)
|
|
601
605
|
cls._find_d(kwargs)
|
|
602
606
|
|
|
603
|
-
fields = cls._sniff(fields, rules
|
|
607
|
+
fields = cls._sniff(fields, rules=rules)
|
|
604
608
|
collection = cls._collection()
|
|
605
|
-
models = collection.find(
|
|
606
|
-
kwargs,
|
|
607
|
-
fields,
|
|
608
|
-
skip = skip,
|
|
609
|
-
limit = limit,
|
|
610
|
-
sort = sort
|
|
611
|
-
)
|
|
609
|
+
models = collection.find(kwargs, fields, skip=skip, limit=limit, sort=sort)
|
|
612
610
|
if not models and raise_e:
|
|
613
611
|
is_devel = common.is_devel()
|
|
614
|
-
if is_devel:
|
|
615
|
-
|
|
616
|
-
|
|
612
|
+
if is_devel:
|
|
613
|
+
message = "%s not found for %s" % (cls.__name__, str(kwargs))
|
|
614
|
+
else:
|
|
615
|
+
message = "%s not found" % cls.__name__
|
|
616
|
+
raise exceptions.NotFoundError(message=message)
|
|
617
617
|
models = [cls.types(model) for model in models]
|
|
618
|
-
if fill:
|
|
619
|
-
|
|
620
|
-
if
|
|
621
|
-
|
|
622
|
-
|
|
618
|
+
if fill:
|
|
619
|
+
models = [cls.fill(model, safe=rules) for model in models]
|
|
620
|
+
if build:
|
|
621
|
+
[cls.build(model, map=map, rules=rules, meta=meta) for model in models]
|
|
622
|
+
if eager:
|
|
623
|
+
models = cls._eager(models, eager, map=map)
|
|
624
|
+
if resolve_a:
|
|
625
|
+
models = [cls._resolve_all(model, resolve=False) for model in models]
|
|
626
|
+
models = (
|
|
627
|
+
models if map else [cls.old(model=model, safe=False) for model in models]
|
|
628
|
+
)
|
|
623
629
|
return models
|
|
624
630
|
|
|
625
631
|
@classmethod
|
|
@@ -644,7 +650,7 @@ class Model(legacy.with_meta(meta.Ordered, observer.Observable, *EXTRA_CLS)):
|
|
|
644
650
|
return result
|
|
645
651
|
|
|
646
652
|
@classmethod
|
|
647
|
-
def paginate(cls, skip
|
|
653
|
+
def paginate(cls, skip=0, limit=1, *args, **kwargs):
|
|
648
654
|
# retrieves the reference to the current global request in handling
|
|
649
655
|
# this is going to be used for operations on the parameters
|
|
650
656
|
request = common.base().get_request()
|
|
@@ -665,20 +671,22 @@ class Model(legacy.with_meta(meta.Ordered, observer.Observable, *EXTRA_CLS)):
|
|
|
665
671
|
# calculates the proper size of the current page being requested
|
|
666
672
|
# taking into account the total number of values and the limit
|
|
667
673
|
size = total % limit if index == count else limit
|
|
668
|
-
if size == 0 and total > 0:
|
|
669
|
-
|
|
674
|
+
if size == 0 and total > 0:
|
|
675
|
+
size = limit
|
|
676
|
+
if total == 0:
|
|
677
|
+
size = 0
|
|
670
678
|
|
|
671
679
|
# creates the base structure for the page populating with the
|
|
672
680
|
# base values that may be used for display of the page
|
|
673
681
|
page = dict(
|
|
674
|
-
count
|
|
675
|
-
index
|
|
676
|
-
start
|
|
677
|
-
end
|
|
678
|
-
size
|
|
679
|
-
total
|
|
680
|
-
sorter
|
|
681
|
-
direction
|
|
682
|
+
count=count,
|
|
683
|
+
index=index,
|
|
684
|
+
start=skip + 1,
|
|
685
|
+
end=skip + limit,
|
|
686
|
+
size=size,
|
|
687
|
+
total=total,
|
|
688
|
+
sorter=request.params_f.get("sorter", None),
|
|
689
|
+
direction=request.params_f.get("direction", "descending"),
|
|
682
690
|
)
|
|
683
691
|
|
|
684
692
|
def generate(**kwargs):
|
|
@@ -689,7 +697,8 @@ class Model(legacy.with_meta(meta.Ordered, observer.Observable, *EXTRA_CLS)):
|
|
|
689
697
|
# creates a copy of the current definition of the parameters and for each
|
|
690
698
|
# of the exclusion parameters removes it from the current structure
|
|
691
699
|
params = dict(request.params_f)
|
|
692
|
-
if "async" in params:
|
|
700
|
+
if "async" in params:
|
|
701
|
+
del params["async"]
|
|
693
702
|
|
|
694
703
|
# retrieves the "special" sorter keyword based argument and the equivalent
|
|
695
704
|
# values for the current page in handling, this values are going to be used
|
|
@@ -702,24 +711,31 @@ class Model(legacy.with_meta(meta.Ordered, observer.Observable, *EXTRA_CLS)):
|
|
|
702
711
|
# verifies if the sorter value is defined in the arguments and if that's
|
|
703
712
|
# the case verifies if it's the same as the current one if that the case
|
|
704
713
|
# the direction must be reversed otherwise the default direction is set
|
|
705
|
-
if sorter and sorter == _sorter:
|
|
706
|
-
|
|
714
|
+
if sorter and sorter == _sorter:
|
|
715
|
+
params["direction"] = reverse
|
|
716
|
+
elif sorter:
|
|
717
|
+
params["direction"] = "descending"
|
|
707
718
|
|
|
708
719
|
# "copies" the complete set of values from the provided keyword
|
|
709
720
|
# based arguments into the parameters map, properly converting them
|
|
710
721
|
# into the proper string value (avoiding possible problems)
|
|
711
|
-
for key, value in kwargs.items():
|
|
722
|
+
for key, value in kwargs.items():
|
|
723
|
+
params[key] = str(value)
|
|
712
724
|
|
|
713
725
|
# iterates over the complete set of parameters to be sent to linearize
|
|
714
726
|
# them into a sequence of tuples ready to be converted into quoted string
|
|
715
727
|
for key, value in params.items():
|
|
716
728
|
is_list = isinstance(value, (list, tuple))
|
|
717
|
-
if not is_list:
|
|
718
|
-
|
|
729
|
+
if not is_list:
|
|
730
|
+
value = [value]
|
|
731
|
+
for _value in value:
|
|
732
|
+
params_l.append((key, _value))
|
|
719
733
|
|
|
720
734
|
# converts the multiple parameters to be used into a linear
|
|
721
735
|
# quoted manner so they they may be used as query string values
|
|
722
|
-
query = [
|
|
736
|
+
query = [
|
|
737
|
+
util.quote(key) + "=" + util.quote(value) for key, value in params_l
|
|
738
|
+
]
|
|
723
739
|
query = "&".join(query)
|
|
724
740
|
return "?" + query if query else query
|
|
725
741
|
|
|
@@ -734,17 +750,22 @@ class Model(legacy.with_meta(meta.Ordered, observer.Observable, *EXTRA_CLS)):
|
|
|
734
750
|
collection.remove(kwargs)
|
|
735
751
|
|
|
736
752
|
@classmethod
|
|
737
|
-
def ordered(cls, filter
|
|
753
|
+
def ordered(cls, filter=dict):
|
|
738
754
|
is_sequence = isinstance(filter, (list, tuple))
|
|
739
|
-
if not is_sequence:
|
|
755
|
+
if not is_sequence:
|
|
756
|
+
filter = (filter,)
|
|
740
757
|
|
|
741
758
|
ordered = list(cls._ordered)
|
|
742
759
|
|
|
743
760
|
for name, value in cls.__dict__.items():
|
|
744
|
-
if name.startswith("_"):
|
|
745
|
-
|
|
746
|
-
if not
|
|
747
|
-
|
|
761
|
+
if name.startswith("_"):
|
|
762
|
+
continue
|
|
763
|
+
if not name == name.lower():
|
|
764
|
+
continue
|
|
765
|
+
if not isinstance(value, filter):
|
|
766
|
+
continue
|
|
767
|
+
if name in ordered:
|
|
768
|
+
continue
|
|
748
769
|
ordered.append(name)
|
|
749
770
|
|
|
750
771
|
return ordered
|
|
@@ -753,7 +774,8 @@ class Model(legacy.with_meta(meta.Ordered, observer.Observable, *EXTRA_CLS)):
|
|
|
753
774
|
def methods(cls):
|
|
754
775
|
# in case the methods are already "cached" in the current
|
|
755
776
|
# class (fast retrieval) returns immediately
|
|
756
|
-
if "_methods" in cls.__dict__:
|
|
777
|
+
if "_methods" in cls.__dict__:
|
|
778
|
+
return cls._methods
|
|
757
779
|
|
|
758
780
|
# starts the list that will hold the various method names
|
|
759
781
|
# for the class, note that this value will be ordered
|
|
@@ -769,12 +791,7 @@ class Model(legacy.with_meta(meta.Ordered, observer.Observable, *EXTRA_CLS)):
|
|
|
769
791
|
# ordered set of attributes from it extending the retrieved methods
|
|
770
792
|
# list with the value for each of the model levels
|
|
771
793
|
for _cls in hierarchy:
|
|
772
|
-
ordered = _cls.ordered(
|
|
773
|
-
filter = (
|
|
774
|
-
types.FunctionType,
|
|
775
|
-
classmethod
|
|
776
|
-
)
|
|
777
|
-
)
|
|
794
|
+
ordered = _cls.ordered(filter=(types.FunctionType, classmethod))
|
|
778
795
|
methods.extend(ordered)
|
|
779
796
|
|
|
780
797
|
# saves the retrieved set of methods in the current model definition
|
|
@@ -786,7 +803,8 @@ class Model(legacy.with_meta(meta.Ordered, observer.Observable, *EXTRA_CLS)):
|
|
|
786
803
|
def fields(cls):
|
|
787
804
|
# in case the fields are already "cached" in the current
|
|
788
805
|
# class (fast retrieval) returns immediately
|
|
789
|
-
if "_fields" in cls.__dict__:
|
|
806
|
+
if "_fields" in cls.__dict__:
|
|
807
|
+
return cls._fields
|
|
790
808
|
|
|
791
809
|
# starts the list that will hold the various field names
|
|
792
810
|
# for the class, note that this value will be ordered
|
|
@@ -814,7 +832,8 @@ class Model(legacy.with_meta(meta.Ordered, observer.Observable, *EXTRA_CLS)):
|
|
|
814
832
|
def definition(cls):
|
|
815
833
|
# in case the definition is already "cached" in the current
|
|
816
834
|
# class (fast retrieval) returns immediately
|
|
817
|
-
if "_definition" in cls.__dict__:
|
|
835
|
+
if "_definition" in cls.__dict__:
|
|
836
|
+
return cls._definition
|
|
818
837
|
|
|
819
838
|
# creates the map that will hold the complete definition of
|
|
820
839
|
# the current model
|
|
@@ -830,14 +849,18 @@ class Model(legacy.with_meta(meta.Ordered, observer.Observable, *EXTRA_CLS)):
|
|
|
830
849
|
# associated with its definition map
|
|
831
850
|
for _cls in hierarchy:
|
|
832
851
|
for name, value in _cls.__dict__.items():
|
|
833
|
-
if name.startswith("_"):
|
|
834
|
-
|
|
835
|
-
if not
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
852
|
+
if name.startswith("_"):
|
|
853
|
+
continue
|
|
854
|
+
if not name == name.lower():
|
|
855
|
+
continue
|
|
856
|
+
if not isinstance(value, dict):
|
|
857
|
+
continue
|
|
858
|
+
if name in definition:
|
|
859
|
+
raise exceptions.OperationalError(
|
|
860
|
+
message="Duplicated attribute '%s' in '%s' hierarchy"
|
|
861
|
+
% (name, _cls.__name__),
|
|
862
|
+
code=412,
|
|
863
|
+
)
|
|
841
864
|
definition[name] = value
|
|
842
865
|
|
|
843
866
|
# sets the "default" definition for the based identifier
|
|
@@ -853,7 +876,8 @@ class Model(legacy.with_meta(meta.Ordered, observer.Observable, *EXTRA_CLS)):
|
|
|
853
876
|
def definition_extended(cls):
|
|
854
877
|
# in case the definition extended is already "cached" in the current
|
|
855
878
|
# class (fast retrieval) returns immediately
|
|
856
|
-
if "_definition_extended" in cls.__dict__:
|
|
879
|
+
if "_definition_extended" in cls.__dict__:
|
|
880
|
+
return cls._definition_extended
|
|
857
881
|
|
|
858
882
|
# retrieves the base definition dictionary and duplicated it adding
|
|
859
883
|
# the element present in the set of extra definition (overridable on
|
|
@@ -870,7 +894,8 @@ class Model(legacy.with_meta(meta.Ordered, observer.Observable, *EXTRA_CLS)):
|
|
|
870
894
|
def links(cls):
|
|
871
895
|
# in case the links are already "cached" in the current
|
|
872
896
|
# class (fast retrieval) returns immediately
|
|
873
|
-
if "_links" in cls.__dict__:
|
|
897
|
+
if "_links" in cls.__dict__:
|
|
898
|
+
return cls._links
|
|
874
899
|
|
|
875
900
|
# creates the list that will hold the complete set of method
|
|
876
901
|
# names for links type methods
|
|
@@ -885,7 +910,8 @@ class Model(legacy.with_meta(meta.Ordered, observer.Observable, *EXTRA_CLS)):
|
|
|
885
910
|
# class hierarchy to determine the ones that are links
|
|
886
911
|
for name in methods:
|
|
887
912
|
method = getattr(cls, name)
|
|
888
|
-
if not hasattr(method, "_link"):
|
|
913
|
+
if not hasattr(method, "_link"):
|
|
914
|
+
continue
|
|
889
915
|
reference = hasattr(method, "__self__") and method.__self__
|
|
890
916
|
is_instance = False if reference else True
|
|
891
917
|
method._link["instance"] = is_instance
|
|
@@ -893,7 +919,7 @@ class Model(legacy.with_meta(meta.Ordered, observer.Observable, *EXTRA_CLS)):
|
|
|
893
919
|
|
|
894
920
|
# sorts the various links taking into account the name of
|
|
895
921
|
# the link, this is considered the pre-defined order
|
|
896
|
-
links.sort(key
|
|
922
|
+
links.sort(key=lambda item: item["name"])
|
|
897
923
|
|
|
898
924
|
# saves the list of link method names defined under the current
|
|
899
925
|
# class and then returns the contents of it to the caller method
|
|
@@ -904,7 +930,8 @@ class Model(legacy.with_meta(meta.Ordered, observer.Observable, *EXTRA_CLS)):
|
|
|
904
930
|
def links_m(cls):
|
|
905
931
|
# in case the links are already "cached" in the current
|
|
906
932
|
# class (fast retrieval) returns immediately
|
|
907
|
-
if "_links_m" in cls.__dict__:
|
|
933
|
+
if "_links_m" in cls.__dict__:
|
|
934
|
+
return cls._links_m
|
|
908
935
|
|
|
909
936
|
# creates the map that will hold the complete set of method
|
|
910
937
|
# names for links type methods
|
|
@@ -919,7 +946,8 @@ class Model(legacy.with_meta(meta.Ordered, observer.Observable, *EXTRA_CLS)):
|
|
|
919
946
|
# class hierarchy to determine the ones that are links
|
|
920
947
|
for name in methods:
|
|
921
948
|
method = getattr(cls, name)
|
|
922
|
-
if not hasattr(method, "_link"):
|
|
949
|
+
if not hasattr(method, "_link"):
|
|
950
|
+
continue
|
|
923
951
|
links_m[method.__name__] = method._link
|
|
924
952
|
|
|
925
953
|
# saves the map of link method names defined under the current
|
|
@@ -936,7 +964,8 @@ class Model(legacy.with_meta(meta.Ordered, observer.Observable, *EXTRA_CLS)):
|
|
|
936
964
|
def operations(cls):
|
|
937
965
|
# in case the operations are already "cached" in the current
|
|
938
966
|
# class (fast retrieval) returns immediately
|
|
939
|
-
if "_operations" in cls.__dict__:
|
|
967
|
+
if "_operations" in cls.__dict__:
|
|
968
|
+
return cls._operations
|
|
940
969
|
|
|
941
970
|
# creates the list that will hold the complete set of method
|
|
942
971
|
# names for operations type methods
|
|
@@ -951,7 +980,8 @@ class Model(legacy.with_meta(meta.Ordered, observer.Observable, *EXTRA_CLS)):
|
|
|
951
980
|
# class hierarchy to determine the ones that are operations
|
|
952
981
|
for name in methods:
|
|
953
982
|
method = getattr(cls, name)
|
|
954
|
-
if not hasattr(method, "_operation"):
|
|
983
|
+
if not hasattr(method, "_operation"):
|
|
984
|
+
continue
|
|
955
985
|
reference = hasattr(method, "__self__") and method.__self__
|
|
956
986
|
is_instance = False if reference else True
|
|
957
987
|
method._operation["instance"] = is_instance
|
|
@@ -959,7 +989,7 @@ class Model(legacy.with_meta(meta.Ordered, observer.Observable, *EXTRA_CLS)):
|
|
|
959
989
|
|
|
960
990
|
# sorts the various operations taking into account the name of
|
|
961
991
|
# the operation, this is considered the pre-defined order
|
|
962
|
-
operations.sort(key
|
|
992
|
+
operations.sort(key=lambda item: item["name"])
|
|
963
993
|
|
|
964
994
|
# saves the list of operation method names defined under the current
|
|
965
995
|
# class and then returns the contents of it to the caller method
|
|
@@ -970,7 +1000,8 @@ class Model(legacy.with_meta(meta.Ordered, observer.Observable, *EXTRA_CLS)):
|
|
|
970
1000
|
def operations_m(cls):
|
|
971
1001
|
# in case the operations are already "cached" in the current
|
|
972
1002
|
# class (fast retrieval) returns immediately
|
|
973
|
-
if "_operations_m" in cls.__dict__:
|
|
1003
|
+
if "_operations_m" in cls.__dict__:
|
|
1004
|
+
return cls._operations_m
|
|
974
1005
|
|
|
975
1006
|
# creates the map that will hold the complete set of method
|
|
976
1007
|
# names for operations type methods
|
|
@@ -985,7 +1016,8 @@ class Model(legacy.with_meta(meta.Ordered, observer.Observable, *EXTRA_CLS)):
|
|
|
985
1016
|
# class hierarchy to determine the ones that are operations
|
|
986
1017
|
for name in methods:
|
|
987
1018
|
method = getattr(cls, name)
|
|
988
|
-
if not hasattr(method, "_operation"):
|
|
1019
|
+
if not hasattr(method, "_operation"):
|
|
1020
|
+
continue
|
|
989
1021
|
operations_m[method.__name__] = method._operation
|
|
990
1022
|
|
|
991
1023
|
# saves the map of operation method names defined under the current
|
|
@@ -1002,7 +1034,8 @@ class Model(legacy.with_meta(meta.Ordered, observer.Observable, *EXTRA_CLS)):
|
|
|
1002
1034
|
def views(cls):
|
|
1003
1035
|
# in case the views are already "cached" in the current
|
|
1004
1036
|
# class (fast retrieval) returns immediately
|
|
1005
|
-
if "_views" in cls.__dict__:
|
|
1037
|
+
if "_views" in cls.__dict__:
|
|
1038
|
+
return cls._views
|
|
1006
1039
|
|
|
1007
1040
|
# creates the list that will hold the complete set of method
|
|
1008
1041
|
# names for views type methods
|
|
@@ -1017,7 +1050,8 @@ class Model(legacy.with_meta(meta.Ordered, observer.Observable, *EXTRA_CLS)):
|
|
|
1017
1050
|
# class hierarchy to determine the ones that are views
|
|
1018
1051
|
for name in methods:
|
|
1019
1052
|
method = getattr(cls, name)
|
|
1020
|
-
if not hasattr(method, "_view"):
|
|
1053
|
+
if not hasattr(method, "_view"):
|
|
1054
|
+
continue
|
|
1021
1055
|
reference = hasattr(method, "__self__") and method.__self__
|
|
1022
1056
|
is_instance = False if reference else True
|
|
1023
1057
|
method._view["instance"] = is_instance
|
|
@@ -1025,7 +1059,7 @@ class Model(legacy.with_meta(meta.Ordered, observer.Observable, *EXTRA_CLS)):
|
|
|
1025
1059
|
|
|
1026
1060
|
# sorts the various views taking into account the name of
|
|
1027
1061
|
# the view, this is considered the pre-defined order
|
|
1028
|
-
views.sort(key
|
|
1062
|
+
views.sort(key=lambda item: item["name"])
|
|
1029
1063
|
|
|
1030
1064
|
# saves the list of view method names defined under the current
|
|
1031
1065
|
# class and then returns the contents of it to the caller method
|
|
@@ -1036,7 +1070,8 @@ class Model(legacy.with_meta(meta.Ordered, observer.Observable, *EXTRA_CLS)):
|
|
|
1036
1070
|
def views_m(cls):
|
|
1037
1071
|
# in case the views are already "cached" in the current
|
|
1038
1072
|
# class (fast retrieval) returns immediately
|
|
1039
|
-
if "_views_m" in cls.__dict__:
|
|
1073
|
+
if "_views_m" in cls.__dict__:
|
|
1074
|
+
return cls._views_m
|
|
1040
1075
|
|
|
1041
1076
|
# creates the map that will hold the complete set of method
|
|
1042
1077
|
# names for views type methods
|
|
@@ -1051,7 +1086,8 @@ class Model(legacy.with_meta(meta.Ordered, observer.Observable, *EXTRA_CLS)):
|
|
|
1051
1086
|
# class hierarchy to determine the ones that are views
|
|
1052
1087
|
for name in methods:
|
|
1053
1088
|
method = getattr(cls, name)
|
|
1054
|
-
if not hasattr(method, "_view"):
|
|
1089
|
+
if not hasattr(method, "_view"):
|
|
1090
|
+
continue
|
|
1055
1091
|
views_m[method.__name__] = method._view
|
|
1056
1092
|
|
|
1057
1093
|
# saves the map of view method names defined under the current
|
|
@@ -1067,17 +1103,20 @@ class Model(legacy.with_meta(meta.Ordered, observer.Observable, *EXTRA_CLS)):
|
|
|
1067
1103
|
@classmethod
|
|
1068
1104
|
def definition_n(cls, name):
|
|
1069
1105
|
definition = cls.definition_extended()
|
|
1070
|
-
if not name in definition:
|
|
1106
|
+
if not name in definition:
|
|
1107
|
+
return {}
|
|
1071
1108
|
return definition[name]
|
|
1072
1109
|
|
|
1073
1110
|
@classmethod
|
|
1074
|
-
def register(cls, lazy
|
|
1075
|
-
if lazy:
|
|
1111
|
+
def register(cls, lazy=False):
|
|
1112
|
+
if lazy:
|
|
1113
|
+
return
|
|
1076
1114
|
cls.setup()
|
|
1077
1115
|
|
|
1078
1116
|
@classmethod
|
|
1079
|
-
def unregister(cls, lazy
|
|
1080
|
-
if lazy:
|
|
1117
|
+
def unregister(cls, lazy=False):
|
|
1118
|
+
if lazy:
|
|
1119
|
+
return
|
|
1081
1120
|
cls.teardown()
|
|
1082
1121
|
|
|
1083
1122
|
@classmethod
|
|
@@ -1120,9 +1159,11 @@ class Model(legacy.with_meta(meta.Ordered, observer.Observable, *EXTRA_CLS)):
|
|
|
1120
1159
|
definition = cls.definition()
|
|
1121
1160
|
for name in names:
|
|
1122
1161
|
value = definition.get(name, None)
|
|
1123
|
-
if value == None:
|
|
1162
|
+
if value == None:
|
|
1163
|
+
continue
|
|
1124
1164
|
is_private = value.get("private", False)
|
|
1125
|
-
if is_private:
|
|
1165
|
+
if is_private:
|
|
1166
|
+
continue
|
|
1126
1167
|
_names.append(name)
|
|
1127
1168
|
return _names
|
|
1128
1169
|
|
|
@@ -1147,17 +1188,20 @@ class Model(legacy.with_meta(meta.Ordered, observer.Observable, *EXTRA_CLS)):
|
|
|
1147
1188
|
return {}
|
|
1148
1189
|
|
|
1149
1190
|
@classmethod
|
|
1150
|
-
def build(cls, model, map
|
|
1151
|
-
if rules:
|
|
1191
|
+
def build(cls, model, map=False, rules=True, meta=False):
|
|
1192
|
+
if rules:
|
|
1193
|
+
cls.rules(model, map)
|
|
1152
1194
|
cls._build(model, map)
|
|
1153
|
-
if meta:
|
|
1195
|
+
if meta:
|
|
1196
|
+
cls._meta(model, map)
|
|
1154
1197
|
|
|
1155
1198
|
@classmethod
|
|
1156
1199
|
def rules(cls, model, map):
|
|
1157
1200
|
for name, _value in legacy.eager(model.items()):
|
|
1158
1201
|
definition = cls.definition_n(name)
|
|
1159
1202
|
is_private = definition.get("private", False)
|
|
1160
|
-
if not is_private:
|
|
1203
|
+
if not is_private:
|
|
1204
|
+
continue
|
|
1161
1205
|
del model[name]
|
|
1162
1206
|
|
|
1163
1207
|
@classmethod
|
|
@@ -1165,15 +1209,18 @@ class Model(legacy.with_meta(meta.Ordered, observer.Observable, *EXTRA_CLS)):
|
|
|
1165
1209
|
definition = cls.definition()
|
|
1166
1210
|
|
|
1167
1211
|
for name, value in legacy.eager(model.items()):
|
|
1168
|
-
if name == "_id":
|
|
1169
|
-
|
|
1170
|
-
if
|
|
1212
|
+
if name == "_id":
|
|
1213
|
+
continue
|
|
1214
|
+
if value == None:
|
|
1215
|
+
continue
|
|
1216
|
+
if not name in definition:
|
|
1217
|
+
continue
|
|
1171
1218
|
model[name] = cls.cast(name, value)
|
|
1172
1219
|
|
|
1173
1220
|
return model
|
|
1174
1221
|
|
|
1175
1222
|
@classmethod
|
|
1176
|
-
def fill(cls, model
|
|
1223
|
+
def fill(cls, model=None, safe=False):
|
|
1177
1224
|
"""
|
|
1178
1225
|
Fills the current model with the proper values so that
|
|
1179
1226
|
no values are unset as this would violate the model definition
|
|
@@ -1193,12 +1240,16 @@ class Model(legacy.with_meta(meta.Ordered, observer.Observable, *EXTRA_CLS)):
|
|
|
1193
1240
|
model = model or dict()
|
|
1194
1241
|
definition = cls.definition()
|
|
1195
1242
|
for name, _definition in definition.items():
|
|
1196
|
-
if name in model:
|
|
1197
|
-
|
|
1243
|
+
if name in model:
|
|
1244
|
+
continue
|
|
1245
|
+
if name in ("_id",):
|
|
1246
|
+
continue
|
|
1198
1247
|
private = _definition.get("private", False)
|
|
1199
1248
|
increment = _definition.get("increment", False)
|
|
1200
|
-
if private and safe:
|
|
1201
|
-
|
|
1249
|
+
if private and safe:
|
|
1250
|
+
continue
|
|
1251
|
+
if increment:
|
|
1252
|
+
continue
|
|
1202
1253
|
if "initial" in _definition:
|
|
1203
1254
|
initial = _definition["initial"]
|
|
1204
1255
|
model[name] = initial
|
|
@@ -1211,17 +1262,20 @@ class Model(legacy.with_meta(meta.Ordered, observer.Observable, *EXTRA_CLS)):
|
|
|
1211
1262
|
return model
|
|
1212
1263
|
|
|
1213
1264
|
@classmethod
|
|
1214
|
-
def cast(cls, name, value, safe
|
|
1265
|
+
def cast(cls, name, value, safe=True):
|
|
1215
1266
|
definition = cls.definition()
|
|
1216
|
-
if not name in definition:
|
|
1217
|
-
|
|
1267
|
+
if not name in definition:
|
|
1268
|
+
return value
|
|
1269
|
+
if value == None:
|
|
1270
|
+
return value
|
|
1218
1271
|
_definition = cls.definition_n(name)
|
|
1219
1272
|
_type = _definition.get("type", legacy.UNICODE)
|
|
1220
1273
|
builder = BUILDERS.get(_type, _type)
|
|
1221
1274
|
try:
|
|
1222
1275
|
return builder(value) if builder else value
|
|
1223
1276
|
except Exception:
|
|
1224
|
-
if not safe:
|
|
1277
|
+
if not safe:
|
|
1278
|
+
raise
|
|
1225
1279
|
default = type_d(_type, None)
|
|
1226
1280
|
default = _type._default() if hasattr(_type, "_default") else default
|
|
1227
1281
|
return default
|
|
@@ -1260,12 +1314,13 @@ class Model(legacy.with_meta(meta.Ordered, observer.Observable, *EXTRA_CLS)):
|
|
|
1260
1314
|
# the value if there's success
|
|
1261
1315
|
info = cls.definition_n(name)
|
|
1262
1316
|
description = info.get("description", None)
|
|
1263
|
-
if description:
|
|
1317
|
+
if description:
|
|
1318
|
+
return description
|
|
1264
1319
|
|
|
1265
1320
|
# because there's no explicit description defined
|
|
1266
1321
|
# runs the automatic underscore to readable conversion
|
|
1267
1322
|
# and sets the value on the field info dictionary
|
|
1268
|
-
description = util.underscore_to_readable(name, capitalize
|
|
1323
|
+
description = util.underscore_to_readable(name, capitalize=True)
|
|
1269
1324
|
info["description"] = description
|
|
1270
1325
|
return description
|
|
1271
1326
|
|
|
@@ -1287,7 +1342,8 @@ class Model(legacy.with_meta(meta.Ordered, observer.Observable, *EXTRA_CLS)):
|
|
|
1287
1342
|
def all_parents(cls):
|
|
1288
1343
|
# in case the all parents are already "cached" in the current
|
|
1289
1344
|
# class (fast retrieval) returns immediately
|
|
1290
|
-
if "_all_parents" in cls.__dict__:
|
|
1345
|
+
if "_all_parents" in cls.__dict__:
|
|
1346
|
+
return cls._all_parents
|
|
1291
1347
|
|
|
1292
1348
|
# creates the list to hold the various parent
|
|
1293
1349
|
# entity classes, populated recursively
|
|
@@ -1323,7 +1379,8 @@ class Model(legacy.with_meta(meta.Ordered, observer.Observable, *EXTRA_CLS)):
|
|
|
1323
1379
|
def hierarchy(cls):
|
|
1324
1380
|
# in case the hierarchy are already "cached" in the current
|
|
1325
1381
|
# class (fast retrieval) returns immediately
|
|
1326
|
-
if "_hierarchy" in cls.__dict__:
|
|
1382
|
+
if "_hierarchy" in cls.__dict__:
|
|
1383
|
+
return cls._hierarchy
|
|
1327
1384
|
|
|
1328
1385
|
# retrieves the complete set of parents for the current class
|
|
1329
1386
|
# and then adds the current class to it
|
|
@@ -1339,7 +1396,8 @@ class Model(legacy.with_meta(meta.Ordered, observer.Observable, *EXTRA_CLS)):
|
|
|
1339
1396
|
def increments(cls):
|
|
1340
1397
|
# in case the increments are already "cached" in the current
|
|
1341
1398
|
# class (fast retrieval) returns immediately
|
|
1342
|
-
if "_increments" in cls.__dict__:
|
|
1399
|
+
if "_increments" in cls.__dict__:
|
|
1400
|
+
return cls._increments
|
|
1343
1401
|
|
|
1344
1402
|
# creates the list that will hold the various names that are
|
|
1345
1403
|
# meant to be automatically incremented
|
|
@@ -1354,7 +1412,8 @@ class Model(legacy.with_meta(meta.Ordered, observer.Observable, *EXTRA_CLS)):
|
|
|
1354
1412
|
for name in definition:
|
|
1355
1413
|
_definition = cls.definition_n(name)
|
|
1356
1414
|
is_increment = _definition.get("increment", False)
|
|
1357
|
-
if not is_increment:
|
|
1415
|
+
if not is_increment:
|
|
1416
|
+
continue
|
|
1358
1417
|
increments.append(name)
|
|
1359
1418
|
|
|
1360
1419
|
# saves the increment list under the class and then
|
|
@@ -1366,7 +1425,8 @@ class Model(legacy.with_meta(meta.Ordered, observer.Observable, *EXTRA_CLS)):
|
|
|
1366
1425
|
def indexes(cls):
|
|
1367
1426
|
# in case the indexes are already "cached" in the current
|
|
1368
1427
|
# class (fast retrieval) returns immediately
|
|
1369
|
-
if "_indexes" in cls.__dict__:
|
|
1428
|
+
if "_indexes" in cls.__dict__:
|
|
1429
|
+
return cls._indexes
|
|
1370
1430
|
|
|
1371
1431
|
# creates the list that will hold the various names that are
|
|
1372
1432
|
# meant to be indexed in the data source
|
|
@@ -1381,7 +1441,8 @@ class Model(legacy.with_meta(meta.Ordered, observer.Observable, *EXTRA_CLS)):
|
|
|
1381
1441
|
for name in definition:
|
|
1382
1442
|
_definition = cls.definition_n(name)
|
|
1383
1443
|
direction = _definition.get("index", False)
|
|
1384
|
-
if not direction:
|
|
1444
|
+
if not direction:
|
|
1445
|
+
continue
|
|
1385
1446
|
indexes.append((name, direction))
|
|
1386
1447
|
|
|
1387
1448
|
# saves the index list under the class and then
|
|
@@ -1393,7 +1454,8 @@ class Model(legacy.with_meta(meta.Ordered, observer.Observable, *EXTRA_CLS)):
|
|
|
1393
1454
|
def safes(cls):
|
|
1394
1455
|
# in case the safes are already "cached" in the current
|
|
1395
1456
|
# class (fast retrieval) returns immediately
|
|
1396
|
-
if "_safes" in cls.__dict__:
|
|
1457
|
+
if "_safes" in cls.__dict__:
|
|
1458
|
+
return cls._safes
|
|
1397
1459
|
|
|
1398
1460
|
# creates the list that will hold the various names that are
|
|
1399
1461
|
# meant to be safe values in the data source, the underlying
|
|
@@ -1409,7 +1471,8 @@ class Model(legacy.with_meta(meta.Ordered, observer.Observable, *EXTRA_CLS)):
|
|
|
1409
1471
|
for name in definition:
|
|
1410
1472
|
_definition = cls.definition_n(name)
|
|
1411
1473
|
is_safe = _definition.get("safe", False)
|
|
1412
|
-
if not is_safe:
|
|
1474
|
+
if not is_safe:
|
|
1475
|
+
continue
|
|
1413
1476
|
safes.append(name)
|
|
1414
1477
|
|
|
1415
1478
|
# saves the safes list under the class and then
|
|
@@ -1421,7 +1484,8 @@ class Model(legacy.with_meta(meta.Ordered, observer.Observable, *EXTRA_CLS)):
|
|
|
1421
1484
|
def immutables(cls):
|
|
1422
1485
|
# in case the immutables are already "cached" in the current
|
|
1423
1486
|
# class (fast retrieval) returns immediately
|
|
1424
|
-
if "_immutables" in cls.__dict__:
|
|
1487
|
+
if "_immutables" in cls.__dict__:
|
|
1488
|
+
return cls._immutables
|
|
1425
1489
|
|
|
1426
1490
|
# creates the list that will hold the various names that are
|
|
1427
1491
|
# meant to be immutable values in the data source
|
|
@@ -1436,7 +1500,8 @@ class Model(legacy.with_meta(meta.Ordered, observer.Observable, *EXTRA_CLS)):
|
|
|
1436
1500
|
for name in definition:
|
|
1437
1501
|
_definition = cls.definition_n(name)
|
|
1438
1502
|
is_immutable = _definition.get("immutable", False)
|
|
1439
|
-
if not is_immutable:
|
|
1503
|
+
if not is_immutable:
|
|
1504
|
+
continue
|
|
1440
1505
|
immutables.append(name)
|
|
1441
1506
|
|
|
1442
1507
|
# saves the immutables list under the class and then
|
|
@@ -1448,7 +1513,8 @@ class Model(legacy.with_meta(meta.Ordered, observer.Observable, *EXTRA_CLS)):
|
|
|
1448
1513
|
def eagers(cls):
|
|
1449
1514
|
# in case the eagers are already "cached" in the current
|
|
1450
1515
|
# class (fast retrieval) returns immediately
|
|
1451
|
-
if "_eagers" in cls.__dict__:
|
|
1516
|
+
if "_eagers" in cls.__dict__:
|
|
1517
|
+
return cls._eagers
|
|
1452
1518
|
|
|
1453
1519
|
# creates the list that will hold the various names that are
|
|
1454
1520
|
# meant to be eager values in the data source
|
|
@@ -1463,7 +1529,8 @@ class Model(legacy.with_meta(meta.Ordered, observer.Observable, *EXTRA_CLS)):
|
|
|
1463
1529
|
for name in definition:
|
|
1464
1530
|
_definition = cls.definition_n(name)
|
|
1465
1531
|
is_eager = _definition.get("eager", False)
|
|
1466
|
-
if not is_eager:
|
|
1532
|
+
if not is_eager:
|
|
1533
|
+
continue
|
|
1467
1534
|
eagers.append(name)
|
|
1468
1535
|
|
|
1469
1536
|
# saves the eagers list under the class and then
|
|
@@ -1475,7 +1542,8 @@ class Model(legacy.with_meta(meta.Ordered, observer.Observable, *EXTRA_CLS)):
|
|
|
1475
1542
|
def default(cls):
|
|
1476
1543
|
# in case the default are already "cached" in the current
|
|
1477
1544
|
# class (fast retrieval) returns immediately
|
|
1478
|
-
if "_default" in cls.__dict__:
|
|
1545
|
+
if "_default" in cls.__dict__:
|
|
1546
|
+
return cls._default
|
|
1479
1547
|
|
|
1480
1548
|
# retrieves the complete hierarchy of the model to be used
|
|
1481
1549
|
# for the retrieval of the lowest possible default value for
|
|
@@ -1496,7 +1564,8 @@ class Model(legacy.with_meta(meta.Ordered, observer.Observable, *EXTRA_CLS)):
|
|
|
1496
1564
|
# continues the loop, nothing to be done
|
|
1497
1565
|
_definition = cls.definition_n(name)
|
|
1498
1566
|
is_default = _definition.get("default", False)
|
|
1499
|
-
if not is_default:
|
|
1567
|
+
if not is_default:
|
|
1568
|
+
continue
|
|
1500
1569
|
|
|
1501
1570
|
# in case the default value is found sets its name in the
|
|
1502
1571
|
# current default value and then breaks the loop
|
|
@@ -1505,7 +1574,8 @@ class Model(legacy.with_meta(meta.Ordered, observer.Observable, *EXTRA_CLS)):
|
|
|
1505
1574
|
|
|
1506
1575
|
# in case the default value has been found must break the external
|
|
1507
1576
|
# loop as nothing else remains to be found
|
|
1508
|
-
if default:
|
|
1577
|
+
if default:
|
|
1578
|
+
break
|
|
1509
1579
|
|
|
1510
1580
|
# saves the default value (name) under the class and then
|
|
1511
1581
|
# returns the sequence to the caller method
|
|
@@ -1513,7 +1583,7 @@ class Model(legacy.with_meta(meta.Ordered, observer.Observable, *EXTRA_CLS)):
|
|
|
1513
1583
|
return default
|
|
1514
1584
|
|
|
1515
1585
|
@classmethod
|
|
1516
|
-
def filter_merge(cls, name, filter, kwargs, operator
|
|
1586
|
+
def filter_merge(cls, name, filter, kwargs, operator=None):
|
|
1517
1587
|
# retrieves a possible previous filter defined for the
|
|
1518
1588
|
# provided name in case it does exist must concatenate
|
|
1519
1589
|
# that previous value in a join statement according to
|
|
@@ -1531,9 +1601,12 @@ class Model(legacy.with_meta(meta.Ordered, observer.Observable, *EXTRA_CLS)):
|
|
|
1531
1601
|
# then deletes the current name reference in the arguments
|
|
1532
1602
|
# and updates the name value to the and value
|
|
1533
1603
|
filter_a = kwargs.get(operator, [])
|
|
1534
|
-
if filter_p:
|
|
1535
|
-
|
|
1536
|
-
|
|
1604
|
+
if filter_p:
|
|
1605
|
+
filter = filter_a + [{name: filter}, {name: filter_p}]
|
|
1606
|
+
else:
|
|
1607
|
+
filter = filter_a + [{name: filter}]
|
|
1608
|
+
if name in kwargs:
|
|
1609
|
+
del kwargs[name]
|
|
1537
1610
|
name = operator
|
|
1538
1611
|
|
|
1539
1612
|
# sets the currently defined filter structures in the keyword
|
|
@@ -1554,7 +1627,8 @@ class Model(legacy.with_meta(meta.Ordered, observer.Observable, *EXTRA_CLS)):
|
|
|
1554
1627
|
|
|
1555
1628
|
@classmethod
|
|
1556
1629
|
def is_concrete(cls):
|
|
1557
|
-
if not "is_abstract" in cls.__dict__:
|
|
1630
|
+
if not "is_abstract" in cls.__dict__:
|
|
1631
|
+
return True
|
|
1558
1632
|
return not cls.is_abstract()
|
|
1559
1633
|
|
|
1560
1634
|
@classmethod
|
|
@@ -1563,32 +1637,30 @@ class Model(legacy.with_meta(meta.Ordered, observer.Observable, *EXTRA_CLS)):
|
|
|
1563
1637
|
|
|
1564
1638
|
@classmethod
|
|
1565
1639
|
def is_equal(cls, other):
|
|
1566
|
-
if not cls._name() == other._name():
|
|
1567
|
-
|
|
1640
|
+
if not cls._name() == other._name():
|
|
1641
|
+
return False
|
|
1642
|
+
if not cls.__name__ == other.__name__:
|
|
1643
|
+
return False
|
|
1568
1644
|
return True
|
|
1569
1645
|
|
|
1570
1646
|
@classmethod
|
|
1571
1647
|
def assert_is_attached_g(cls):
|
|
1572
|
-
if cls.is_attached():
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
code = 412
|
|
1576
|
-
)
|
|
1648
|
+
if cls.is_attached():
|
|
1649
|
+
return
|
|
1650
|
+
raise exceptions.OperationalError(message="Model is not attached", code=412)
|
|
1577
1651
|
|
|
1578
1652
|
@classmethod
|
|
1579
1653
|
def assert_is_concrete_g(cls):
|
|
1580
|
-
if cls.is_concrete():
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
code = 412
|
|
1584
|
-
)
|
|
1654
|
+
if cls.is_concrete():
|
|
1655
|
+
return
|
|
1656
|
+
raise exceptions.OperationalError(message="Model is not concrete", code=412)
|
|
1585
1657
|
|
|
1586
1658
|
@classmethod
|
|
1587
1659
|
def assert_is_child_g(cls, parent):
|
|
1588
|
-
if cls.is_child(parent):
|
|
1660
|
+
if cls.is_child(parent):
|
|
1661
|
+
return
|
|
1589
1662
|
raise exceptions.OperationalError(
|
|
1590
|
-
message
|
|
1591
|
-
code = 412
|
|
1663
|
+
message="Model is not child of %s" % parent, code=412
|
|
1592
1664
|
)
|
|
1593
1665
|
|
|
1594
1666
|
@classmethod
|
|
@@ -1596,7 +1668,7 @@ class Model(legacy.with_meta(meta.Ordered, observer.Observable, *EXTRA_CLS)):
|
|
|
1596
1668
|
pass
|
|
1597
1669
|
|
|
1598
1670
|
@classmethod
|
|
1599
|
-
def _meta(cls, model, map, safe
|
|
1671
|
+
def _meta(cls, model, map, safe=True):
|
|
1600
1672
|
# iterates over the complete set of keys and values for the
|
|
1601
1673
|
# current model map to try to compute the associated meta value
|
|
1602
1674
|
# for all of its attributes (should use some computation)
|
|
@@ -1605,8 +1677,7 @@ class Model(legacy.with_meta(meta.Ordered, observer.Observable, *EXTRA_CLS)):
|
|
|
1605
1677
|
# unset (none) or an unloaded reference, if that's the
|
|
1606
1678
|
# case the value is considered "invalid" not to be encoded
|
|
1607
1679
|
is_reference = isinstance(value, TYPE_REFERENCES)
|
|
1608
|
-
is_invalid = value == None or
|
|
1609
|
-
(is_reference and not value.is_resolved())
|
|
1680
|
+
is_invalid = value == None or (is_reference and not value.is_resolved())
|
|
1610
1681
|
|
|
1611
1682
|
# retrieves the definition for the current key in iteration
|
|
1612
1683
|
# so that it's possible to apply the mapper
|
|
@@ -1623,10 +1694,13 @@ class Model(legacy.with_meta(meta.Ordered, observer.Observable, *EXTRA_CLS)):
|
|
|
1623
1694
|
# invalid calls the proper mapper function otherwise runs the
|
|
1624
1695
|
# default (fallback) operation for meta retrieval
|
|
1625
1696
|
try:
|
|
1626
|
-
if mapper and not is_invalid:
|
|
1627
|
-
|
|
1697
|
+
if mapper and not is_invalid:
|
|
1698
|
+
value = mapper(value, definition, cls)
|
|
1699
|
+
else:
|
|
1700
|
+
value = value if is_invalid else legacy.UNICODE(value)
|
|
1628
1701
|
except Exception:
|
|
1629
|
-
if not safe:
|
|
1702
|
+
if not safe:
|
|
1703
|
+
raise
|
|
1630
1704
|
value = None
|
|
1631
1705
|
|
|
1632
1706
|
# sets the final meta value in the model, so that it may be
|
|
@@ -1634,14 +1708,16 @@ class Model(legacy.with_meta(meta.Ordered, observer.Observable, *EXTRA_CLS)):
|
|
|
1634
1708
|
model[key + "_meta"] = value
|
|
1635
1709
|
|
|
1636
1710
|
@classmethod
|
|
1637
|
-
def _sniff(cls, fields, rules
|
|
1711
|
+
def _sniff(cls, fields, rules=False):
|
|
1638
1712
|
fields = fields or cls.fields()
|
|
1639
1713
|
fields = list(fields)
|
|
1640
|
-
if not rules:
|
|
1714
|
+
if not rules:
|
|
1715
|
+
return fields
|
|
1641
1716
|
for field in list(fields):
|
|
1642
1717
|
definition = cls.definition_n(field)
|
|
1643
1718
|
is_private = definition.get("private", False)
|
|
1644
|
-
if is_private:
|
|
1719
|
+
if is_private:
|
|
1720
|
+
fields.remove(field)
|
|
1645
1721
|
return fields
|
|
1646
1722
|
|
|
1647
1723
|
@classmethod
|
|
@@ -1650,7 +1726,8 @@ class Model(legacy.with_meta(meta.Ordered, observer.Observable, *EXTRA_CLS)):
|
|
|
1650
1726
|
type = definition.get("type", legacy.UNICODE)
|
|
1651
1727
|
for cls in type.mro():
|
|
1652
1728
|
base = TYPE_META.get(cls, None)
|
|
1653
|
-
if base:
|
|
1729
|
+
if base:
|
|
1730
|
+
break
|
|
1654
1731
|
return definition.get("meta", base)
|
|
1655
1732
|
|
|
1656
1733
|
@classmethod
|
|
@@ -1664,14 +1741,14 @@ class Model(legacy.with_meta(meta.Ordered, observer.Observable, *EXTRA_CLS)):
|
|
|
1664
1741
|
return encoder
|
|
1665
1742
|
|
|
1666
1743
|
@classmethod
|
|
1667
|
-
def _collection(cls, name
|
|
1744
|
+
def _collection(cls, name=None):
|
|
1668
1745
|
name = name or cls._name()
|
|
1669
1746
|
adapter = cls._adapter()
|
|
1670
1747
|
collection = adapter.collection(name)
|
|
1671
1748
|
return collection
|
|
1672
1749
|
|
|
1673
1750
|
@classmethod
|
|
1674
|
-
def _collection_a(cls, name
|
|
1751
|
+
def _collection_a(cls, name=None):
|
|
1675
1752
|
name = name or cls._name()
|
|
1676
1753
|
adapter = cls._adapter()
|
|
1677
1754
|
collection = adapter.collection_a(name)
|
|
@@ -1686,18 +1763,18 @@ class Model(legacy.with_meta(meta.Ordered, observer.Observable, *EXTRA_CLS)):
|
|
|
1686
1763
|
return name
|
|
1687
1764
|
|
|
1688
1765
|
@classmethod
|
|
1689
|
-
def _under(cls, plural
|
|
1690
|
-
return cls._underscore(plural
|
|
1766
|
+
def _under(cls, plural=True):
|
|
1767
|
+
return cls._underscore(plural=plural)
|
|
1691
1768
|
|
|
1692
1769
|
@classmethod
|
|
1693
|
-
def _underscore(cls, plural
|
|
1770
|
+
def _underscore(cls, plural=True):
|
|
1694
1771
|
camel = cls._plural() if plural else cls._singular()
|
|
1695
1772
|
return util.camel_to_underscore(camel)
|
|
1696
1773
|
|
|
1697
1774
|
@classmethod
|
|
1698
|
-
def _readable(cls, plural
|
|
1775
|
+
def _readable(cls, plural=False):
|
|
1699
1776
|
camel = cls._plural() if plural else cls._singular()
|
|
1700
|
-
return util.camel_to_readable(camel, capitalize
|
|
1777
|
+
return util.camel_to_readable(camel, capitalize=True)
|
|
1701
1778
|
|
|
1702
1779
|
@classmethod
|
|
1703
1780
|
def _singular(cls):
|
|
@@ -1730,7 +1807,8 @@ class Model(legacy.with_meta(meta.Ordered, observer.Observable, *EXTRA_CLS)):
|
|
|
1730
1807
|
# that's the case runs the recursive eager loading of names and
|
|
1731
1808
|
# returns the resulting sequence to the caller method
|
|
1732
1809
|
is_list = isinstance(model, (list, tuple))
|
|
1733
|
-
if is_list:
|
|
1810
|
+
if is_list:
|
|
1811
|
+
return [cls._eager(_model, names, *args, **kwargs) for _model in model]
|
|
1734
1812
|
|
|
1735
1813
|
# iterates over the complete set of names that are meant to be
|
|
1736
1814
|
# eager loaded from the model and runs the "resolution" process
|
|
@@ -1739,9 +1817,14 @@ class Model(legacy.with_meta(meta.Ordered, observer.Observable, *EXTRA_CLS)):
|
|
|
1739
1817
|
_model = model
|
|
1740
1818
|
for part in name.split("."):
|
|
1741
1819
|
is_sequence = isinstance(_model, (list, tuple))
|
|
1742
|
-
if is_sequence:
|
|
1743
|
-
|
|
1744
|
-
|
|
1820
|
+
if is_sequence:
|
|
1821
|
+
_model = [
|
|
1822
|
+
cls._res(value, part, *args, **kwargs) for value in _model
|
|
1823
|
+
]
|
|
1824
|
+
else:
|
|
1825
|
+
_model = cls._res(_model, part, *args, **kwargs)
|
|
1826
|
+
if not _model:
|
|
1827
|
+
break
|
|
1745
1828
|
|
|
1746
1829
|
# returns the resulting model to the caller method, most of the
|
|
1747
1830
|
# times this model should have not been touched
|
|
@@ -1770,14 +1853,16 @@ class Model(legacy.with_meta(meta.Ordered, observer.Observable, *EXTRA_CLS)):
|
|
|
1770
1853
|
|
|
1771
1854
|
# in case the provided is not valid returns it (no resolution is
|
|
1772
1855
|
# possible) otherwise gather the base value for resolution
|
|
1773
|
-
if not model:
|
|
1856
|
+
if not model:
|
|
1857
|
+
return model
|
|
1774
1858
|
value = model[part]
|
|
1775
1859
|
|
|
1776
1860
|
# check the data type of the requested name for resolution
|
|
1777
1861
|
# and in case it's not valid and not a reference returns it
|
|
1778
1862
|
# immediately, no resolution to be performed
|
|
1779
1863
|
is_reference = isinstance(value, TYPE_REFERENCES)
|
|
1780
|
-
if not value and not is_reference:
|
|
1864
|
+
if not value and not is_reference:
|
|
1865
|
+
return value
|
|
1781
1866
|
|
|
1782
1867
|
# in case the value is a reference type object then runs
|
|
1783
1868
|
# the resolve operation effectively resolving the values
|
|
@@ -1785,12 +1870,14 @@ class Model(legacy.with_meta(meta.Ordered, observer.Observable, *EXTRA_CLS)):
|
|
|
1785
1870
|
# that this operation is going to respect the map vs. instance
|
|
1786
1871
|
# kind of resolution process so the data type of the resulting
|
|
1787
1872
|
# value is going to depend on that
|
|
1788
|
-
if is_reference:
|
|
1873
|
+
if is_reference:
|
|
1874
|
+
value = value.resolve(eager_l=True, *args, **kwargs)
|
|
1789
1875
|
|
|
1790
1876
|
# in case the map resolution process was requested an explicit
|
|
1791
1877
|
# set of the resolved value is required (implicit resolution
|
|
1792
1878
|
# using `resolve()`) is not enough to ensure proper type structure
|
|
1793
|
-
if kwargs.get("map", False):
|
|
1879
|
+
if kwargs.get("map", False):
|
|
1880
|
+
model[part] = value
|
|
1794
1881
|
|
|
1795
1882
|
# returns the "final" (possibly resolved) value to the caller method
|
|
1796
1883
|
# ready to be used for possible merging processes
|
|
@@ -1814,7 +1901,8 @@ class Model(legacy.with_meta(meta.Ordered, observer.Observable, *EXTRA_CLS)):
|
|
|
1814
1901
|
|
|
1815
1902
|
# in case the provided name is a simple one this is a direct attribute
|
|
1816
1903
|
# of the current class and the expected tuple is returned immediately
|
|
1817
|
-
if not "." in name:
|
|
1904
|
+
if not "." in name:
|
|
1905
|
+
return cls, name
|
|
1818
1906
|
|
|
1819
1907
|
# splits the namespace recursive name and then iterates over the multiple
|
|
1820
1908
|
# relation attributes to retrieve their respective targets
|
|
@@ -1844,9 +1932,10 @@ class Model(legacy.with_meta(meta.Ordered, observer.Observable, *EXTRA_CLS)):
|
|
|
1844
1932
|
return _attrs
|
|
1845
1933
|
|
|
1846
1934
|
@classmethod
|
|
1847
|
-
def _clean_attrs(cls, kwargs, dirty
|
|
1935
|
+
def _clean_attrs(cls, kwargs, dirty=DIRTY_PARAMS):
|
|
1848
1936
|
for key in dirty:
|
|
1849
|
-
if not key in kwargs:
|
|
1937
|
+
if not key in kwargs:
|
|
1938
|
+
continue
|
|
1850
1939
|
del kwargs[key]
|
|
1851
1940
|
|
|
1852
1941
|
@classmethod
|
|
@@ -1878,7 +1967,8 @@ class Model(legacy.with_meta(meta.Ordered, observer.Observable, *EXTRA_CLS)):
|
|
|
1878
1967
|
# in case the find string is currently not defined in the
|
|
1879
1968
|
# named arguments map returns immediately as nothing is
|
|
1880
1969
|
# meant to be done on this method
|
|
1881
|
-
if not "find_s" in kwargs:
|
|
1970
|
+
if not "find_s" in kwargs:
|
|
1971
|
+
return
|
|
1882
1972
|
|
|
1883
1973
|
# retrieves the find string into a local variable, then
|
|
1884
1974
|
# removes the find string from the named arguments map
|
|
@@ -1892,7 +1982,8 @@ class Model(legacy.with_meta(meta.Ordered, observer.Observable, *EXTRA_CLS)):
|
|
|
1892
1982
|
# there's none returns immediately, as it's not possible
|
|
1893
1983
|
# to proceed with the filter creation
|
|
1894
1984
|
default = find_n or cls.default()
|
|
1895
|
-
if not default:
|
|
1985
|
+
if not default:
|
|
1986
|
+
return
|
|
1896
1987
|
|
|
1897
1988
|
# constructs the proper right and left parts of the regex
|
|
1898
1989
|
# that is going to be constructed for the matching of the
|
|
@@ -1911,11 +2002,13 @@ class Model(legacy.with_meta(meta.Ordered, observer.Observable, *EXTRA_CLS)):
|
|
|
1911
2002
|
# string the both sides wildcard regex is used for the
|
|
1912
2003
|
# search otherwise the search value to be used is the
|
|
1913
2004
|
# exact match of the value (required type conversion)
|
|
1914
|
-
if default_t in legacy.STRINGS:
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
2005
|
+
if default_t in legacy.STRINGS:
|
|
2006
|
+
find_v = {
|
|
2007
|
+
"$regex": right + re.escape(find_s) + left,
|
|
2008
|
+
"$options": "i" if find_i else "",
|
|
2009
|
+
}
|
|
2010
|
+
else:
|
|
2011
|
+
find_v = default_t(find_s)
|
|
1919
2012
|
except Exception:
|
|
1920
2013
|
# in case there's an error in the conversion for
|
|
1921
2014
|
# the target type value sets the search value as
|
|
@@ -1934,7 +2027,8 @@ class Model(legacy.with_meta(meta.Ordered, observer.Observable, *EXTRA_CLS)):
|
|
|
1934
2027
|
# in case the find definition is currently not defined in the
|
|
1935
2028
|
# named arguments map returns immediately as nothing is
|
|
1936
2029
|
# meant to be done on this method
|
|
1937
|
-
if not "find_d" in kwargs:
|
|
2030
|
+
if not "find_d" in kwargs:
|
|
2031
|
+
return
|
|
1938
2032
|
|
|
1939
2033
|
# tries to retrieve the value of the operator that is going
|
|
1940
2034
|
# to be used to "join" the multiple find parts (find values)
|
|
@@ -1950,7 +2044,8 @@ class Model(legacy.with_meta(meta.Ordered, observer.Observable, *EXTRA_CLS)):
|
|
|
1950
2044
|
# verifies that the data type for the find definition is a
|
|
1951
2045
|
# valid sequence and in case its not converts it into one
|
|
1952
2046
|
# so that it may be used in sequence valid logic
|
|
1953
|
-
if not isinstance(find_d, list):
|
|
2047
|
+
if not isinstance(find_d, list):
|
|
2048
|
+
find_d = [find_d]
|
|
1954
2049
|
|
|
1955
2050
|
# iterates over all the filters defined in the filter definition
|
|
1956
2051
|
# so that they may be used to update the provided arguments with
|
|
@@ -1958,13 +2053,15 @@ class Model(legacy.with_meta(meta.Ordered, observer.Observable, *EXTRA_CLS)):
|
|
|
1958
2053
|
for filter in find_d:
|
|
1959
2054
|
# in case the filter is not valid (unset or invalid) it's going
|
|
1960
2055
|
# to be ignored as no valid information is present
|
|
1961
|
-
if not filter:
|
|
2056
|
+
if not filter:
|
|
2057
|
+
continue
|
|
1962
2058
|
|
|
1963
2059
|
# splits the filter string into its three main components
|
|
1964
2060
|
# the name, operator and value, that are going to be processed
|
|
1965
2061
|
# as defined by the specification to create the filter
|
|
1966
2062
|
result = filter.split(":", 2)
|
|
1967
|
-
if len(result) == 2:
|
|
2063
|
+
if len(result) == 2:
|
|
2064
|
+
result.append(None)
|
|
1968
2065
|
name, operator, value = result
|
|
1969
2066
|
|
|
1970
2067
|
# retrieves the definition for the filter attribute and uses
|
|
@@ -1973,8 +2070,10 @@ class Model(legacy.with_meta(meta.Ordered, observer.Observable, *EXTRA_CLS)):
|
|
|
1973
2070
|
# type resolution method exists it's used (recursive resolution)
|
|
1974
2071
|
definition = cls.definition_n(name)
|
|
1975
2072
|
name_t = definition.get("type", legacy.UNICODE)
|
|
1976
|
-
if hasattr(name_t, "_btype"):
|
|
1977
|
-
|
|
2073
|
+
if hasattr(name_t, "_btype"):
|
|
2074
|
+
name_t = name_t._btype()
|
|
2075
|
+
if name in ("_id",):
|
|
2076
|
+
name_t = cls._adapter().object_id
|
|
1978
2077
|
|
|
1979
2078
|
# determines if the current filter operation should be performed
|
|
1980
2079
|
# using a case insensitive based approach to the search, by default
|
|
@@ -1990,21 +2089,25 @@ class Model(legacy.with_meta(meta.Ordered, observer.Observable, *EXTRA_CLS)):
|
|
|
1990
2089
|
# in case there's a custom value mapped retrieved uses it to convert
|
|
1991
2090
|
# the string based value into the target specific value for the query
|
|
1992
2091
|
# otherwise uses the data type for the search field for value conversion
|
|
1993
|
-
if value_method:
|
|
2092
|
+
if value_method:
|
|
2093
|
+
value = value_method(value, name_t)
|
|
1994
2094
|
else:
|
|
1995
|
-
try:
|
|
1996
|
-
|
|
2095
|
+
try:
|
|
2096
|
+
value = name_t(value)
|
|
2097
|
+
except ValueError:
|
|
2098
|
+
value = None
|
|
1997
2099
|
|
|
1998
2100
|
# constructs the custom find value using a key and value map value
|
|
1999
2101
|
# in case the operator is defined otherwise (operator not defined)
|
|
2000
2102
|
# the value is used directly, then merges this find value into the
|
|
2001
2103
|
# current set of filters for the provided (keyword) arguments
|
|
2002
|
-
find_v = {operator
|
|
2003
|
-
if insensitive:
|
|
2004
|
-
|
|
2104
|
+
find_v = {operator: value} if operator else value
|
|
2105
|
+
if insensitive:
|
|
2106
|
+
find_v["$options"] = "i"
|
|
2107
|
+
cls.filter_merge(name, find_v, kwargs, operator=find_o)
|
|
2005
2108
|
|
|
2006
2109
|
@classmethod
|
|
2007
|
-
def _bases(cls, subclass
|
|
2110
|
+
def _bases(cls, subclass=None):
|
|
2008
2111
|
"""
|
|
2009
2112
|
Retrieves the complete set of base (parent) classes for
|
|
2010
2113
|
the current class, this method is safe as it removes any
|
|
@@ -2034,8 +2137,10 @@ class Model(legacy.with_meta(meta.Ordered, observer.Observable, *EXTRA_CLS)):
|
|
|
2034
2137
|
# according to their inheritance from the provided
|
|
2035
2138
|
# (model) class to be applied as filter
|
|
2036
2139
|
bases = cls.__bases__
|
|
2037
|
-
if subclass:
|
|
2038
|
-
|
|
2140
|
+
if subclass:
|
|
2141
|
+
bases = [base for base in bases if issubclass(base, subclass)]
|
|
2142
|
+
if not bases == Model.__bases__:
|
|
2143
|
+
return bases
|
|
2039
2144
|
|
|
2040
2145
|
# returns an empty tuple to the caller method as the
|
|
2041
2146
|
# top level class has been reached and the class is
|
|
@@ -2045,43 +2150,21 @@ class Model(legacy.with_meta(meta.Ordered, observer.Observable, *EXTRA_CLS)):
|
|
|
2045
2150
|
@classmethod
|
|
2046
2151
|
def _increment(cls, name):
|
|
2047
2152
|
_name = cls._name() + ":" + name
|
|
2048
|
-
store = cls._collection(name
|
|
2153
|
+
store = cls._collection(name="counters")
|
|
2049
2154
|
value = store.find_and_modify(
|
|
2050
|
-
{
|
|
2051
|
-
"_id" : _name
|
|
2052
|
-
},
|
|
2053
|
-
{
|
|
2054
|
-
"$inc" : {
|
|
2055
|
-
"seq" : 1
|
|
2056
|
-
}
|
|
2057
|
-
},
|
|
2058
|
-
new = True,
|
|
2059
|
-
upsert = True
|
|
2155
|
+
{"_id": _name}, {"$inc": {"seq": 1}}, new=True, upsert=True
|
|
2060
2156
|
)
|
|
2061
|
-
value = value or store.find_one({
|
|
2062
|
-
"_id" : _name
|
|
2063
|
-
})
|
|
2157
|
+
value = value or store.find_one({"_id": _name})
|
|
2064
2158
|
return value["seq"]
|
|
2065
2159
|
|
|
2066
2160
|
@classmethod
|
|
2067
2161
|
def _ensure_min(cls, name, value):
|
|
2068
2162
|
_name = cls._name() + ":" + name
|
|
2069
|
-
store = cls._collection(name
|
|
2163
|
+
store = cls._collection(name="counters")
|
|
2070
2164
|
value = store.find_and_modify(
|
|
2071
|
-
{
|
|
2072
|
-
"_id" : _name
|
|
2073
|
-
},
|
|
2074
|
-
{
|
|
2075
|
-
"$max" : {
|
|
2076
|
-
"seq" : value
|
|
2077
|
-
}
|
|
2078
|
-
},
|
|
2079
|
-
new = True,
|
|
2080
|
-
upsert = True
|
|
2165
|
+
{"_id": _name}, {"$max": {"seq": value}}, new=True, upsert=True
|
|
2081
2166
|
)
|
|
2082
|
-
value = value or store.find_one({
|
|
2083
|
-
"_id" : _name
|
|
2084
|
-
})
|
|
2167
|
+
value = value or store.find_one({"_id": _name})
|
|
2085
2168
|
return value["seq"]
|
|
2086
2169
|
|
|
2087
2170
|
@classmethod
|
|
@@ -2089,7 +2172,7 @@ class Model(legacy.with_meta(meta.Ordered, observer.Observable, *EXTRA_CLS)):
|
|
|
2089
2172
|
indexes = cls.indexes()
|
|
2090
2173
|
collection = cls._collection()
|
|
2091
2174
|
for index, direction in indexes:
|
|
2092
|
-
collection.ensure_index(index, direction
|
|
2175
|
+
collection.ensure_index(index, direction=direction)
|
|
2093
2176
|
|
|
2094
2177
|
@classmethod
|
|
2095
2178
|
def _destroy_indexes(cls):
|
|
@@ -2117,7 +2200,8 @@ class Model(legacy.with_meta(meta.Ordered, observer.Observable, *EXTRA_CLS)):
|
|
|
2117
2200
|
eager = list(eager) if eager else []
|
|
2118
2201
|
eagers = cls.eagers()
|
|
2119
2202
|
eager.extend(eagers)
|
|
2120
|
-
if not eager:
|
|
2203
|
+
if not eager:
|
|
2204
|
+
return eager
|
|
2121
2205
|
eager = tuple(set(eager))
|
|
2122
2206
|
return eager
|
|
2123
2207
|
|
|
@@ -2125,7 +2209,8 @@ class Model(legacy.with_meta(meta.Ordered, observer.Observable, *EXTRA_CLS)):
|
|
|
2125
2209
|
def _resolve_all(cls, model, *args, **kwargs):
|
|
2126
2210
|
definition = cls.definition()
|
|
2127
2211
|
for name, value in legacy.eager(model.items()):
|
|
2128
|
-
if not name in definition:
|
|
2212
|
+
if not name in definition:
|
|
2213
|
+
continue
|
|
2129
2214
|
model[name] = cls._resolve(name, value, *args, **kwargs)
|
|
2130
2215
|
return model
|
|
2131
2216
|
|
|
@@ -2135,7 +2220,8 @@ class Model(legacy.with_meta(meta.Ordered, observer.Observable, *EXTRA_CLS)):
|
|
|
2135
2220
|
# it is runs the evaluate method for each of the values to
|
|
2136
2221
|
# try to resolve them into the proper representation
|
|
2137
2222
|
is_iterable = isinstance(value, (list, tuple))
|
|
2138
|
-
if is_iterable:
|
|
2223
|
+
if is_iterable:
|
|
2224
|
+
return [cls._resolve(name, value, *args, **kwargs) for value in value]
|
|
2139
2225
|
|
|
2140
2226
|
# in case the current instance is a dictionary then, and in case
|
|
2141
2227
|
# there's a target class (typical for reference like types) recursion
|
|
@@ -2145,24 +2231,28 @@ class Model(legacy.with_meta(meta.Ordered, observer.Observable, *EXTRA_CLS)):
|
|
|
2145
2231
|
if isinstance(value, dict):
|
|
2146
2232
|
info = getattr(cls, name)
|
|
2147
2233
|
part_type = info.get("type", None)
|
|
2148
|
-
if hasattr(part_type, "_target")
|
|
2234
|
+
if hasattr(part_type, "_target"):
|
|
2149
2235
|
_cls = part_type._target()
|
|
2150
2236
|
return _cls._resolve_all(value, *args, **kwargs)
|
|
2151
2237
|
|
|
2152
2238
|
# verifies if the map value recursive approach should be used
|
|
2153
2239
|
# for the element and if that's the case calls the proper method
|
|
2154
2240
|
# otherwise uses the provided (raw value)
|
|
2155
|
-
if not hasattr(value, "map_v"):
|
|
2241
|
+
if not hasattr(value, "map_v"):
|
|
2242
|
+
return value
|
|
2156
2243
|
return value.map_v(*args, **kwargs)
|
|
2157
2244
|
|
|
2158
2245
|
@classmethod
|
|
2159
2246
|
def _to_meta(cls, base):
|
|
2160
|
-
if isinstance(base, str):
|
|
2247
|
+
if isinstance(base, str):
|
|
2248
|
+
return base
|
|
2161
2249
|
is_class = inspect.isclass(type)
|
|
2162
|
-
if not is_class:
|
|
2250
|
+
if not is_class:
|
|
2251
|
+
base = base.__class__
|
|
2163
2252
|
for cls in base.mro():
|
|
2164
2253
|
result = TYPE_META.get(cls, None)
|
|
2165
|
-
if not result:
|
|
2254
|
+
if not result:
|
|
2255
|
+
continue
|
|
2166
2256
|
return result
|
|
2167
2257
|
return base
|
|
2168
2258
|
|
|
@@ -2176,10 +2266,12 @@ class Model(legacy.with_meta(meta.Ordered, observer.Observable, *EXTRA_CLS)):
|
|
|
2176
2266
|
|
|
2177
2267
|
@property
|
|
2178
2268
|
def logger(self):
|
|
2179
|
-
if self.owner:
|
|
2180
|
-
|
|
2269
|
+
if self.owner:
|
|
2270
|
+
return self.owner.logger
|
|
2271
|
+
else:
|
|
2272
|
+
return logging.getLogger()
|
|
2181
2273
|
|
|
2182
|
-
def val(self, name, default
|
|
2274
|
+
def val(self, name, default=None):
|
|
2183
2275
|
return self.model.get(name, default)
|
|
2184
2276
|
|
|
2185
2277
|
def json_v(self, *args, **kwargs):
|
|
@@ -2190,15 +2282,13 @@ class Model(legacy.with_meta(meta.Ordered, observer.Observable, *EXTRA_CLS)):
|
|
|
2190
2282
|
clone = kwargs.pop("clone", False)
|
|
2191
2283
|
resolve = kwargs.get("resolve", True)
|
|
2192
2284
|
evaluator = kwargs.get("evaluator", "map_v")
|
|
2193
|
-
if clone:
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
base
|
|
2197
|
-
|
|
2198
|
-
evaluator = evaluator
|
|
2199
|
-
)
|
|
2285
|
+
if clone:
|
|
2286
|
+
base = self.clone(reset=False, deep=True)
|
|
2287
|
+
else:
|
|
2288
|
+
base = self
|
|
2289
|
+
return cls._resolve_all(base.model, resolve=resolve, evaluator=evaluator)
|
|
2200
2290
|
|
|
2201
|
-
def build_m(self, model
|
|
2291
|
+
def build_m(self, model=None, rules=True):
|
|
2202
2292
|
"""
|
|
2203
2293
|
Builds the currently defined model, this should run
|
|
2204
2294
|
additional computation for the current model creating
|
|
@@ -2222,9 +2312,9 @@ class Model(legacy.with_meta(meta.Ordered, observer.Observable, *EXTRA_CLS)):
|
|
|
2222
2312
|
|
|
2223
2313
|
cls = self.__class__
|
|
2224
2314
|
model = model or self.model
|
|
2225
|
-
cls.build(model, rules
|
|
2315
|
+
cls.build(model, rules=rules)
|
|
2226
2316
|
|
|
2227
|
-
def apply(self, model
|
|
2317
|
+
def apply(self, model=None, form=True, safe=None, safe_a=True):
|
|
2228
2318
|
# calls the complete set of event handlers for the current
|
|
2229
2319
|
# apply operation, this should trigger changes in the model
|
|
2230
2320
|
self.pre_apply()
|
|
@@ -2241,16 +2331,19 @@ class Model(legacy.with_meta(meta.Ordered, observer.Observable, *EXTRA_CLS)):
|
|
|
2241
2331
|
# to the current map of safe attributes
|
|
2242
2332
|
if safe_a:
|
|
2243
2333
|
safes = cls.safes()
|
|
2244
|
-
for _safe in safes:
|
|
2334
|
+
for _safe in safes:
|
|
2335
|
+
safe[_safe] = True
|
|
2245
2336
|
|
|
2246
2337
|
# retrieves the object loading it from all the available
|
|
2247
2338
|
# sources and then iterates over all the of the model
|
|
2248
2339
|
# values setting the values in the current instance's model
|
|
2249
2340
|
# then runs the type casting/conversion operation in it
|
|
2250
|
-
if model == None:
|
|
2341
|
+
if model == None:
|
|
2342
|
+
model = util.get_object() if form else dict()
|
|
2251
2343
|
for name, value in legacy.eager(model.items()):
|
|
2252
2344
|
is_safe = safe.get(name, False)
|
|
2253
|
-
if is_safe:
|
|
2345
|
+
if is_safe:
|
|
2346
|
+
continue
|
|
2254
2347
|
self.model[name] = value
|
|
2255
2348
|
cls = self.__class__
|
|
2256
2349
|
cls.types(self.model)
|
|
@@ -2263,22 +2356,24 @@ class Model(legacy.with_meta(meta.Ordered, observer.Observable, *EXTRA_CLS)):
|
|
|
2263
2356
|
# operation, this may be used for chaining operations
|
|
2264
2357
|
return self
|
|
2265
2358
|
|
|
2266
|
-
def copy(self, build
|
|
2359
|
+
def copy(self, build=False, rules=True):
|
|
2267
2360
|
cls = self.__class__
|
|
2268
2361
|
_copy = copy.deepcopy(self)
|
|
2269
|
-
build and cls.build(_copy.model, map
|
|
2362
|
+
build and cls.build(_copy.model, map=False, rules=rules)
|
|
2270
2363
|
return _copy
|
|
2271
2364
|
|
|
2272
|
-
def clone(self, reset
|
|
2365
|
+
def clone(self, reset=True, deep=False):
|
|
2273
2366
|
cls = self.__class__
|
|
2274
2367
|
model = dict(self.model) if deep else self.model
|
|
2275
|
-
if not reset:
|
|
2368
|
+
if not reset:
|
|
2369
|
+
return cls(model=model)
|
|
2276
2370
|
indexes = cls.increments()
|
|
2277
2371
|
indexes = indexes + cls.unique_names() + ["_id"]
|
|
2278
2372
|
for index in indexes:
|
|
2279
|
-
if not index in model:
|
|
2373
|
+
if not index in model:
|
|
2374
|
+
continue
|
|
2280
2375
|
del model[index]
|
|
2281
|
-
return cls(model
|
|
2376
|
+
return cls(model=model)
|
|
2282
2377
|
|
|
2283
2378
|
def validate_extra(self, name):
|
|
2284
2379
|
"""
|
|
@@ -2314,10 +2409,10 @@ class Model(legacy.with_meta(meta.Ordered, observer.Observable, *EXTRA_CLS)):
|
|
|
2314
2409
|
be returned to the caller method until catching of exception.
|
|
2315
2410
|
"""
|
|
2316
2411
|
|
|
2317
|
-
if self.is_new():
|
|
2412
|
+
if self.is_new():
|
|
2413
|
+
return
|
|
2318
2414
|
raise exceptions.OperationalError(
|
|
2319
|
-
message
|
|
2320
|
-
code = 412
|
|
2415
|
+
message="Instance is not new, identifier is set", code=412
|
|
2321
2416
|
)
|
|
2322
2417
|
|
|
2323
2418
|
def assert_is_attached(self):
|
|
@@ -2334,47 +2429,53 @@ class Model(legacy.with_meta(meta.Ordered, observer.Observable, *EXTRA_CLS)):
|
|
|
2334
2429
|
|
|
2335
2430
|
def save(
|
|
2336
2431
|
self,
|
|
2337
|
-
validate
|
|
2338
|
-
verify
|
|
2339
|
-
is_new
|
|
2340
|
-
increment_a
|
|
2341
|
-
immutables_a
|
|
2342
|
-
pre_validate
|
|
2343
|
-
pre_save
|
|
2344
|
-
pre_create
|
|
2345
|
-
pre_update
|
|
2346
|
-
post_validate
|
|
2347
|
-
post_save
|
|
2348
|
-
post_create
|
|
2349
|
-
post_update
|
|
2350
|
-
before_callbacks
|
|
2351
|
-
after_callbacks
|
|
2432
|
+
validate=True,
|
|
2433
|
+
verify=True,
|
|
2434
|
+
is_new=None,
|
|
2435
|
+
increment_a=None,
|
|
2436
|
+
immutables_a=None,
|
|
2437
|
+
pre_validate=True,
|
|
2438
|
+
pre_save=True,
|
|
2439
|
+
pre_create=True,
|
|
2440
|
+
pre_update=True,
|
|
2441
|
+
post_validate=True,
|
|
2442
|
+
post_save=True,
|
|
2443
|
+
post_create=True,
|
|
2444
|
+
post_update=True,
|
|
2445
|
+
before_callbacks=[],
|
|
2446
|
+
after_callbacks=[],
|
|
2352
2447
|
):
|
|
2353
2448
|
# ensures that the current instance is associated with
|
|
2354
2449
|
# a concrete model, ready to be persisted in database
|
|
2355
|
-
if verify:
|
|
2450
|
+
if verify:
|
|
2451
|
+
self.assert_is_concrete()
|
|
2356
2452
|
|
|
2357
2453
|
# checks if the instance to be saved is a new instance
|
|
2358
2454
|
# or if this is an update operation and then determines
|
|
2359
2455
|
# series of default values taking that into account
|
|
2360
|
-
if is_new == None:
|
|
2361
|
-
|
|
2362
|
-
if
|
|
2456
|
+
if is_new == None:
|
|
2457
|
+
is_new = self.is_new()
|
|
2458
|
+
if increment_a == None:
|
|
2459
|
+
increment_a = is_new
|
|
2460
|
+
if immutables_a == None:
|
|
2461
|
+
immutables_a = not is_new
|
|
2363
2462
|
|
|
2364
2463
|
# runs the validation process in the current model, this
|
|
2365
2464
|
# should ensure that the model is ready to be saved in the
|
|
2366
2465
|
# data source, without corruption of it, only run this process
|
|
2367
2466
|
# in case the validate flag is correctly set
|
|
2368
2467
|
validate and self._validate(
|
|
2369
|
-
pre_validate
|
|
2370
|
-
post_validate = post_validate
|
|
2468
|
+
pre_validate=pre_validate, post_validate=post_validate
|
|
2371
2469
|
)
|
|
2372
2470
|
|
|
2373
2471
|
# calls the complete set of event handlers for the current
|
|
2374
2472
|
# save operation, this should trigger changes in the model
|
|
2375
|
-
if pre_save:
|
|
2376
|
-
|
|
2377
|
-
if
|
|
2473
|
+
if pre_save:
|
|
2474
|
+
self.pre_save()
|
|
2475
|
+
if pre_create and is_new:
|
|
2476
|
+
self.pre_create()
|
|
2477
|
+
if pre_update and not is_new:
|
|
2478
|
+
self.pre_update()
|
|
2378
2479
|
|
|
2379
2480
|
# filters the values that are present in the current model
|
|
2380
2481
|
# so that only the valid ones are stored in, invalid values
|
|
@@ -2384,37 +2485,42 @@ class Model(legacy.with_meta(meta.Ordered, observer.Observable, *EXTRA_CLS)):
|
|
|
2384
2485
|
# any relation is loaded the reference value is returned instead
|
|
2385
2486
|
# of the loaded relation values (required for persistence)
|
|
2386
2487
|
model = self._filter(
|
|
2387
|
-
increment_a =
|
|
2388
|
-
immutables_a = immutables_a,
|
|
2389
|
-
normalize = True
|
|
2488
|
+
increment_a=increment_a, immutables_a=immutables_a, normalize=True
|
|
2390
2489
|
)
|
|
2391
2490
|
|
|
2392
2491
|
# in case the current model is not new must create a new
|
|
2393
2492
|
# model instance and remove the main identifier from it
|
|
2394
|
-
if not is_new:
|
|
2493
|
+
if not is_new:
|
|
2494
|
+
_model = copy.copy(model)
|
|
2495
|
+
del _model["_id"]
|
|
2395
2496
|
|
|
2396
2497
|
# calls the complete set of callbacks that should be called
|
|
2397
2498
|
# before the concrete data store save operation
|
|
2398
|
-
for callback in before_callbacks:
|
|
2499
|
+
for callback in before_callbacks:
|
|
2500
|
+
callback(self, model)
|
|
2399
2501
|
|
|
2400
2502
|
# retrieves the reference to the store object to be used and
|
|
2401
2503
|
# uses it to store the current model data
|
|
2402
2504
|
store = self._get_store()
|
|
2403
2505
|
if is_new:
|
|
2404
2506
|
store.insert(model)
|
|
2405
|
-
self.apply(model, safe_a
|
|
2507
|
+
self.apply(model, safe_a=False)
|
|
2406
2508
|
else:
|
|
2407
|
-
store.update({"_id"
|
|
2509
|
+
store.update({"_id": model["_id"]}, {"$set": _model})
|
|
2408
2510
|
|
|
2409
2511
|
# calls the complete set of callbacks that should be called
|
|
2410
2512
|
# after the concrete data store save operation
|
|
2411
|
-
for callback in after_callbacks:
|
|
2513
|
+
for callback in after_callbacks:
|
|
2514
|
+
callback(self, model)
|
|
2412
2515
|
|
|
2413
2516
|
# calls the post save event handlers in order to be able to
|
|
2414
2517
|
# execute appropriate post operations
|
|
2415
|
-
if post_save:
|
|
2416
|
-
|
|
2417
|
-
if
|
|
2518
|
+
if post_save:
|
|
2519
|
+
self.post_save()
|
|
2520
|
+
if post_create and is_new:
|
|
2521
|
+
self.post_create()
|
|
2522
|
+
if post_update and not is_new:
|
|
2523
|
+
self.post_update()
|
|
2418
2524
|
|
|
2419
2525
|
# returns the instance that has just been used for the save
|
|
2420
2526
|
# operation, this may be used for chaining operations
|
|
@@ -2422,28 +2528,31 @@ class Model(legacy.with_meta(meta.Ordered, observer.Observable, *EXTRA_CLS)):
|
|
|
2422
2528
|
|
|
2423
2529
|
def delete(
|
|
2424
2530
|
self,
|
|
2425
|
-
verify
|
|
2426
|
-
pre_delete
|
|
2427
|
-
post_delete
|
|
2428
|
-
before_callbacks
|
|
2429
|
-
after_callbacks
|
|
2531
|
+
verify=True,
|
|
2532
|
+
pre_delete=True,
|
|
2533
|
+
post_delete=True,
|
|
2534
|
+
before_callbacks=[],
|
|
2535
|
+
after_callbacks=[],
|
|
2430
2536
|
):
|
|
2431
2537
|
# ensures that the current instance is associated with
|
|
2432
2538
|
# a concrete model, ready to be persisted in database
|
|
2433
|
-
if verify:
|
|
2539
|
+
if verify:
|
|
2540
|
+
self.assert_is_concrete()
|
|
2434
2541
|
|
|
2435
2542
|
# calls the complete set of event handlers for the current
|
|
2436
2543
|
# delete operation, this should trigger changes in the model
|
|
2437
|
-
if pre_delete:
|
|
2544
|
+
if pre_delete:
|
|
2545
|
+
self.pre_delete()
|
|
2438
2546
|
|
|
2439
2547
|
# calls the complete set of callbacks that should be called
|
|
2440
2548
|
# before the concrete data store delete operation
|
|
2441
|
-
for callback in before_callbacks:
|
|
2549
|
+
for callback in before_callbacks:
|
|
2550
|
+
callback(self)
|
|
2442
2551
|
|
|
2443
2552
|
# retrieves the reference to the store object to be able to
|
|
2444
2553
|
# execute the removal command for the current model
|
|
2445
2554
|
store = self._get_store()
|
|
2446
|
-
store.remove({"_id"
|
|
2555
|
+
store.remove({"_id": self._id})
|
|
2447
2556
|
|
|
2448
2557
|
# calls the underlying delete handler that may be used to extend
|
|
2449
2558
|
# the default delete functionality
|
|
@@ -2451,13 +2560,15 @@ class Model(legacy.with_meta(meta.Ordered, observer.Observable, *EXTRA_CLS)):
|
|
|
2451
2560
|
|
|
2452
2561
|
# calls the complete set of callbacks that should be called
|
|
2453
2562
|
# after the concrete data store delete operation
|
|
2454
|
-
for callback in after_callbacks:
|
|
2563
|
+
for callback in after_callbacks:
|
|
2564
|
+
callback(self)
|
|
2455
2565
|
|
|
2456
2566
|
# calls the complete set of event handlers for the current
|
|
2457
2567
|
# delete operation, this should trigger changes in the model
|
|
2458
|
-
if post_delete:
|
|
2568
|
+
if post_delete:
|
|
2569
|
+
self.post_delete()
|
|
2459
2570
|
|
|
2460
|
-
def approve(self, model
|
|
2571
|
+
def approve(self, model=None, type=None):
|
|
2461
2572
|
# retrieves the class associated with the instance
|
|
2462
2573
|
# that is going to be sent for approval
|
|
2463
2574
|
cls = self.__class__
|
|
@@ -2469,71 +2580,55 @@ class Model(legacy.with_meta(meta.Ordered, observer.Observable, *EXTRA_CLS)):
|
|
|
2469
2580
|
# retrieves the proper (target) method for validation
|
|
2470
2581
|
# and then runs the inner validate method for it
|
|
2471
2582
|
method = getattr(cls, "validate" + suffix)
|
|
2472
|
-
self._validate(model
|
|
2583
|
+
self._validate(model=model, method=method)
|
|
2473
2584
|
|
|
2474
2585
|
# returns the instance that has just been used for the approve
|
|
2475
2586
|
# operation, this may be used for chaining operations
|
|
2476
2587
|
return self
|
|
2477
2588
|
|
|
2478
|
-
def advance(self, name, delta
|
|
2589
|
+
def advance(self, name, delta=1):
|
|
2479
2590
|
store = self._get_store()
|
|
2480
2591
|
value = store.find_and_modify(
|
|
2481
|
-
{
|
|
2482
|
-
"_id" : self._id
|
|
2483
|
-
},
|
|
2484
|
-
{
|
|
2485
|
-
"$inc" : {
|
|
2486
|
-
name : delta
|
|
2487
|
-
}
|
|
2488
|
-
},
|
|
2489
|
-
new = True
|
|
2592
|
+
{"_id": self._id}, {"$inc": {name: delta}}, new=True
|
|
2490
2593
|
)
|
|
2491
|
-
value = value or store.find_one({
|
|
2492
|
-
"_id" : self._id
|
|
2493
|
-
})
|
|
2594
|
+
value = value or store.find_one({"_id": self._id})
|
|
2494
2595
|
_value = value[name]
|
|
2495
2596
|
setattr(self, name, _value)
|
|
2496
2597
|
return _value
|
|
2497
2598
|
|
|
2498
2599
|
def reload(self, *args, **kwargs):
|
|
2499
2600
|
is_new = self.is_new()
|
|
2500
|
-
if is_new:
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
|
|
2601
|
+
if is_new:
|
|
2602
|
+
raise exceptions.OperationalError(
|
|
2603
|
+
message="Can't reload a new model entity", code=412
|
|
2604
|
+
)
|
|
2504
2605
|
cls = self.__class__
|
|
2505
|
-
return cls.get(_id
|
|
2606
|
+
return cls.get(_id=self._id, *args, **kwargs)
|
|
2506
2607
|
|
|
2507
2608
|
def exists(self):
|
|
2508
2609
|
is_new = self.is_new()
|
|
2509
|
-
if is_new:
|
|
2510
|
-
|
|
2610
|
+
if is_new:
|
|
2611
|
+
return False
|
|
2612
|
+
entity = self.get(_id=self._id, raise_e=False)
|
|
2511
2613
|
return True if entity else False
|
|
2512
2614
|
|
|
2513
|
-
def map(
|
|
2514
|
-
self,
|
|
2515
|
-
increment_a = False,
|
|
2516
|
-
resolve = False,
|
|
2517
|
-
all = False,
|
|
2518
|
-
evaluator = "map_v"
|
|
2519
|
-
):
|
|
2615
|
+
def map(self, increment_a=False, resolve=False, all=False, evaluator="map_v"):
|
|
2520
2616
|
model = self._filter(
|
|
2521
|
-
increment_a =
|
|
2522
|
-
resolve = resolve,
|
|
2523
|
-
all = all,
|
|
2524
|
-
evaluator = evaluator
|
|
2617
|
+
increment_a=increment_a, resolve=resolve, all=all, evaluator=evaluator
|
|
2525
2618
|
)
|
|
2526
2619
|
return model
|
|
2527
2620
|
|
|
2528
|
-
def dumps(self, encode
|
|
2621
|
+
def dumps(self, encode=True):
|
|
2529
2622
|
cls = self.__class__
|
|
2530
2623
|
encoder = cls._encoder() if encode else None
|
|
2531
|
-
return json.dumps(self.model, cls
|
|
2624
|
+
return json.dumps(self.model, cls=encoder)
|
|
2532
2625
|
|
|
2533
2626
|
def unwrap(self, **kwargs):
|
|
2534
2627
|
default = kwargs.get("default", False)
|
|
2535
|
-
if default:
|
|
2536
|
-
|
|
2628
|
+
if default:
|
|
2629
|
+
return self.map()
|
|
2630
|
+
else:
|
|
2631
|
+
return dict()
|
|
2537
2632
|
|
|
2538
2633
|
def pre_validate(self):
|
|
2539
2634
|
self.trigger("pre_validate")
|
|
@@ -2580,16 +2675,11 @@ class Model(legacy.with_meta(meta.Ordered, observer.Observable, *EXTRA_CLS)):
|
|
|
2580
2675
|
def _delete(self):
|
|
2581
2676
|
pass
|
|
2582
2677
|
|
|
2583
|
-
def _validate(
|
|
2584
|
-
self,
|
|
2585
|
-
model = None,
|
|
2586
|
-
method = None,
|
|
2587
|
-
pre_validate = True,
|
|
2588
|
-
post_validate = True
|
|
2589
|
-
):
|
|
2678
|
+
def _validate(self, model=None, method=None, pre_validate=True, post_validate=True):
|
|
2590
2679
|
# calls the event handler for the validation process this
|
|
2591
2680
|
# should setup the operations for a correct validation
|
|
2592
|
-
if pre_validate:
|
|
2681
|
+
if pre_validate:
|
|
2682
|
+
self.pre_validate()
|
|
2593
2683
|
|
|
2594
2684
|
# starts the model reference with the current model in
|
|
2595
2685
|
# case none is defined
|
|
@@ -2602,17 +2692,16 @@ class Model(legacy.with_meta(meta.Ordered, observer.Observable, *EXTRA_CLS)):
|
|
|
2602
2692
|
# checks if the current model is new (create operation)
|
|
2603
2693
|
# and sets the proper validation methods retrieval method
|
|
2604
2694
|
is_new = self.is_new()
|
|
2605
|
-
if is_new:
|
|
2606
|
-
|
|
2695
|
+
if is_new:
|
|
2696
|
+
method = method or cls.validate_new
|
|
2697
|
+
else:
|
|
2698
|
+
method = method or cls.validate
|
|
2607
2699
|
|
|
2608
2700
|
# runs the validation process on the various arguments
|
|
2609
2701
|
# provided to the account and in case an error is returned
|
|
2610
2702
|
# raises a validation error to the upper layers
|
|
2611
2703
|
errors, object = validation.validate(
|
|
2612
|
-
method,
|
|
2613
|
-
object = model,
|
|
2614
|
-
ctx = self,
|
|
2615
|
-
build = False
|
|
2704
|
+
method, object=model, ctx=self, build=False
|
|
2616
2705
|
)
|
|
2617
2706
|
|
|
2618
2707
|
# iterates over the complete set of extra validate method
|
|
@@ -2622,10 +2711,7 @@ class Model(legacy.with_meta(meta.Ordered, observer.Observable, *EXTRA_CLS)):
|
|
|
2622
2711
|
# from an end-user point of view (as expected)
|
|
2623
2712
|
for extra in self._extras:
|
|
2624
2713
|
_errors, _object = validation.validate(
|
|
2625
|
-
extra,
|
|
2626
|
-
object = model,
|
|
2627
|
-
ctx = self,
|
|
2628
|
-
build = False
|
|
2714
|
+
extra, object=model, ctx=self, build=False
|
|
2629
2715
|
)
|
|
2630
2716
|
for key, value in _errors.items():
|
|
2631
2717
|
errors_l = errors.get(key, [])
|
|
@@ -2640,20 +2726,22 @@ class Model(legacy.with_meta(meta.Ordered, observer.Observable, *EXTRA_CLS)):
|
|
|
2640
2726
|
# in case the errors map is not empty or invalid there are
|
|
2641
2727
|
# errors and they should be encapsulated around a validation
|
|
2642
2728
|
# error and raises to the top layer for handling
|
|
2643
|
-
if errors:
|
|
2729
|
+
if errors:
|
|
2730
|
+
raise exceptions.ValidationError(errors, self)
|
|
2644
2731
|
|
|
2645
2732
|
# calls the event handler for the validation process this
|
|
2646
2733
|
# should finish the operations from a correct validation
|
|
2647
|
-
if post_validate:
|
|
2734
|
+
if post_validate:
|
|
2735
|
+
self.post_validate()
|
|
2648
2736
|
|
|
2649
2737
|
def _filter(
|
|
2650
2738
|
self,
|
|
2651
|
-
increment_a
|
|
2652
|
-
immutables_a
|
|
2653
|
-
normalize
|
|
2654
|
-
resolve
|
|
2655
|
-
all
|
|
2656
|
-
evaluator
|
|
2739
|
+
increment_a=True,
|
|
2740
|
+
immutables_a=False,
|
|
2741
|
+
normalize=False,
|
|
2742
|
+
resolve=False,
|
|
2743
|
+
all=False,
|
|
2744
|
+
evaluator="json_v",
|
|
2657
2745
|
):
|
|
2658
2746
|
# creates the model that will hold the "filtered" model
|
|
2659
2747
|
# with all the items that conform with the class specification
|
|
@@ -2681,7 +2769,8 @@ class Model(legacy.with_meta(meta.Ordered, observer.Observable, *EXTRA_CLS)):
|
|
|
2681
2769
|
# fields so that a new value is set on the model, note that if
|
|
2682
2770
|
# the increment apply is unset the increment operation is ignored
|
|
2683
2771
|
for name in increments:
|
|
2684
|
-
if not increment_a:
|
|
2772
|
+
if not increment_a:
|
|
2773
|
+
continue
|
|
2685
2774
|
if name in self.model:
|
|
2686
2775
|
model[name] = cls._ensure_min(name, self.model[name])
|
|
2687
2776
|
else:
|
|
@@ -2690,10 +2779,13 @@ class Model(legacy.with_meta(meta.Ordered, observer.Observable, *EXTRA_CLS)):
|
|
|
2690
2779
|
# iterates over all the model items to filter the ones
|
|
2691
2780
|
# that are not valid for the current class context
|
|
2692
2781
|
for name, value in legacy.eager(self.model.items()):
|
|
2693
|
-
if not name in definition:
|
|
2694
|
-
|
|
2695
|
-
if
|
|
2696
|
-
|
|
2782
|
+
if not name in definition:
|
|
2783
|
+
continue
|
|
2784
|
+
if increment_a and name in increments:
|
|
2785
|
+
continue
|
|
2786
|
+
if immutables_a and name in immutables:
|
|
2787
|
+
continue
|
|
2788
|
+
value = self._evaluate(name, value, evaluator=evaluator)
|
|
2697
2789
|
model[name] = value
|
|
2698
2790
|
|
|
2699
2791
|
# in case the normalize flag is set must iterate over all
|
|
@@ -2702,8 +2794,10 @@ class Model(legacy.with_meta(meta.Ordered, observer.Observable, *EXTRA_CLS)):
|
|
|
2702
2794
|
# the normal value that would prevent normalization
|
|
2703
2795
|
if normalize:
|
|
2704
2796
|
for name, value in legacy.eager(self.model.items()):
|
|
2705
|
-
if not name in definition:
|
|
2706
|
-
|
|
2797
|
+
if not name in definition:
|
|
2798
|
+
continue
|
|
2799
|
+
if not hasattr(value, "ref_v"):
|
|
2800
|
+
continue
|
|
2707
2801
|
model[name] = value.ref_v()
|
|
2708
2802
|
|
|
2709
2803
|
# in case the resolution flag is set, it means that a recursive
|
|
@@ -2713,7 +2807,8 @@ class Model(legacy.with_meta(meta.Ordered, observer.Observable, *EXTRA_CLS)):
|
|
|
2713
2807
|
# may imply access to the base data source
|
|
2714
2808
|
if resolve:
|
|
2715
2809
|
for name, value in legacy.eager(self.model.items()):
|
|
2716
|
-
if not name in definition:
|
|
2810
|
+
if not name in definition:
|
|
2811
|
+
continue
|
|
2717
2812
|
model[name] = cls._resolve(name, value)
|
|
2718
2813
|
|
|
2719
2814
|
# in case the all flag is set the extra fields (not present
|
|
@@ -2722,14 +2817,15 @@ class Model(legacy.with_meta(meta.Ordered, observer.Observable, *EXTRA_CLS)):
|
|
|
2722
2817
|
# present in the base map of the current instance
|
|
2723
2818
|
if all:
|
|
2724
2819
|
for name, value in legacy.eager(self.model.items()):
|
|
2725
|
-
if name in model:
|
|
2820
|
+
if name in model:
|
|
2821
|
+
continue
|
|
2726
2822
|
model[name] = value
|
|
2727
2823
|
|
|
2728
2824
|
# returns the model containing the "filtered" items resulting
|
|
2729
2825
|
# from the validation of the items against the model class
|
|
2730
2826
|
return model
|
|
2731
2827
|
|
|
2732
|
-
def _evaluate(self, name, value, evaluator
|
|
2828
|
+
def _evaluate(self, name, value, evaluator="json_v"):
|
|
2733
2829
|
# verifies if the current value is an iterable one in case
|
|
2734
2830
|
# it is runs the evaluate method for each of the values to
|
|
2735
2831
|
# try to resolve them into the proper representation, note
|
|
@@ -2737,12 +2833,13 @@ class Model(legacy.with_meta(meta.Ordered, observer.Observable, *EXTRA_CLS)):
|
|
|
2737
2833
|
# objects that implement the evaluator method are not considered
|
|
2738
2834
|
# to be iterables and normal operation applies
|
|
2739
2835
|
is_iterable = hasattr(value, "__iter__")
|
|
2740
|
-
is_iterable =
|
|
2741
|
-
|
|
2742
|
-
|
|
2743
|
-
|
|
2744
|
-
|
|
2745
|
-
|
|
2836
|
+
is_iterable = (
|
|
2837
|
+
is_iterable
|
|
2838
|
+
and not isinstance(value, ITERABLES)
|
|
2839
|
+
and (not hasattr(value, evaluator) or not evaluator)
|
|
2840
|
+
)
|
|
2841
|
+
if is_iterable:
|
|
2842
|
+
return [self._evaluate(name, value, evaluator=evaluator) for value in value]
|
|
2746
2843
|
|
|
2747
2844
|
# verifies the current value's class is sub class of the model
|
|
2748
2845
|
# class and in case it's extracts the relation name from the
|
|
@@ -2757,8 +2854,12 @@ class Model(legacy.with_meta(meta.Ordered, observer.Observable, *EXTRA_CLS)):
|
|
|
2757
2854
|
# iterates over all the values and retrieves the map value for
|
|
2758
2855
|
# each of them in case the value contains a map value retrieval
|
|
2759
2856
|
# method otherwise uses the normal value returning it to the caller
|
|
2760
|
-
method =
|
|
2761
|
-
|
|
2857
|
+
method = (
|
|
2858
|
+
getattr(value, evaluator)
|
|
2859
|
+
if evaluator and hasattr(value, evaluator)
|
|
2860
|
+
else None
|
|
2861
|
+
)
|
|
2862
|
+
value = method(resolve=False) if method else value
|
|
2762
2863
|
return value
|
|
2763
2864
|
|
|
2764
2865
|
def _res_entity(self, name, *args, **kwargs):
|
|
@@ -2782,7 +2883,8 @@ class Model(legacy.with_meta(meta.Ordered, observer.Observable, *EXTRA_CLS)):
|
|
|
2782
2883
|
|
|
2783
2884
|
# in case the provided name is a simple one this is a direct attribute
|
|
2784
2885
|
# of the current entity and so the expected values are returned
|
|
2785
|
-
if not "." in name:
|
|
2886
|
+
if not "." in name:
|
|
2887
|
+
return self, name
|
|
2786
2888
|
|
|
2787
2889
|
# sets the initial entity value for iteration as the current instance
|
|
2788
2890
|
# (root node) and then splits the name around its components
|
|
@@ -2798,6 +2900,7 @@ class Model(legacy.with_meta(meta.Ordered, observer.Observable, *EXTRA_CLS)):
|
|
|
2798
2900
|
# final name of the attribute on the leaf node
|
|
2799
2901
|
return entity, name_s[-1]
|
|
2800
2902
|
|
|
2903
|
+
|
|
2801
2904
|
class LocalModel(Model):
|
|
2802
2905
|
"""
|
|
2803
2906
|
Concrete model aimed at cases where data source based
|
|
@@ -2812,6 +2915,7 @@ class LocalModel(Model):
|
|
|
2812
2915
|
def is_attached(cls):
|
|
2813
2916
|
return False
|
|
2814
2917
|
|
|
2918
|
+
|
|
2815
2919
|
class Field(dict):
|
|
2816
2920
|
"""
|
|
2817
2921
|
Top level field class that should be used for the
|
|
@@ -2832,9 +2936,11 @@ class Field(dict):
|
|
|
2832
2936
|
Field.creation_counter += 1
|
|
2833
2937
|
|
|
2834
2938
|
def __getattr__(self, name):
|
|
2835
|
-
if name in self:
|
|
2939
|
+
if name in self:
|
|
2940
|
+
return self[name]
|
|
2836
2941
|
raise AttributeError("'%s' not found" % name)
|
|
2837
2942
|
|
|
2943
|
+
|
|
2838
2944
|
class Action(dict):
|
|
2839
2945
|
"""
|
|
2840
2946
|
The abstract class that defines an action to be performed
|
|
@@ -2846,10 +2952,11 @@ class Action(dict):
|
|
|
2846
2952
|
"""
|
|
2847
2953
|
|
|
2848
2954
|
def __getattr__(self, name):
|
|
2849
|
-
if name in self:
|
|
2955
|
+
if name in self:
|
|
2956
|
+
return self[name]
|
|
2850
2957
|
raise AttributeError("'%s' not found" % name)
|
|
2851
2958
|
|
|
2852
|
-
def cast(self, values, keyword
|
|
2959
|
+
def cast(self, values, keyword=False):
|
|
2853
2960
|
"""
|
|
2854
2961
|
Runs the "casting" operation for a series of provided
|
|
2855
2962
|
values, the order sequence of the provided values is
|
|
@@ -2882,15 +2989,20 @@ class Action(dict):
|
|
|
2882
2989
|
cast = type
|
|
2883
2990
|
is_default = value in (None, "")
|
|
2884
2991
|
cast = BUILDERS.get(cast, cast)
|
|
2885
|
-
if cast and not is_default:
|
|
2886
|
-
|
|
2887
|
-
if
|
|
2888
|
-
|
|
2992
|
+
if cast and not is_default:
|
|
2993
|
+
value = cast(value)
|
|
2994
|
+
if is_default:
|
|
2995
|
+
value = type_d(type, value)
|
|
2996
|
+
if keyword:
|
|
2997
|
+
casted[name] = value
|
|
2998
|
+
else:
|
|
2999
|
+
casted.append(value)
|
|
2889
3000
|
|
|
2890
3001
|
# returns the final list/map of casted values to the caller method
|
|
2891
3002
|
# so that it may be used safely in the context
|
|
2892
3003
|
return casted
|
|
2893
3004
|
|
|
3005
|
+
|
|
2894
3006
|
class Link(Action):
|
|
2895
3007
|
"""
|
|
2896
3008
|
Internal link class used to encapsulate some of the
|
|
@@ -2900,6 +3012,7 @@ class Link(Action):
|
|
|
2900
3012
|
|
|
2901
3013
|
pass
|
|
2902
3014
|
|
|
3015
|
+
|
|
2903
3016
|
class Operation(Action):
|
|
2904
3017
|
"""
|
|
2905
3018
|
Logical structure representing an operation, should
|
|
@@ -2909,6 +3022,7 @@ class Operation(Action):
|
|
|
2909
3022
|
|
|
2910
3023
|
pass
|
|
2911
3024
|
|
|
3025
|
+
|
|
2912
3026
|
class View(Action):
|
|
2913
3027
|
"""
|
|
2914
3028
|
Definition associated with a model that represents
|
|
@@ -2918,13 +3032,8 @@ class View(Action):
|
|
|
2918
3032
|
|
|
2919
3033
|
pass
|
|
2920
3034
|
|
|
2921
|
-
|
|
2922
|
-
|
|
2923
|
-
description = None,
|
|
2924
|
-
parameters = (),
|
|
2925
|
-
context = False,
|
|
2926
|
-
devel = False
|
|
2927
|
-
):
|
|
3035
|
+
|
|
3036
|
+
def link(name=None, description=None, parameters=(), context=False, devel=False):
|
|
2928
3037
|
"""
|
|
2929
3038
|
Decorator function to be used to "annotate" the provided
|
|
2930
3039
|
function as an link (string) that is able to change the user
|
|
@@ -2955,25 +3064,26 @@ def link(
|
|
|
2955
3064
|
|
|
2956
3065
|
def decorator(function, *args, **kwargs):
|
|
2957
3066
|
function._link = Link(
|
|
2958
|
-
method
|
|
2959
|
-
name
|
|
2960
|
-
description
|
|
2961
|
-
parameters
|
|
2962
|
-
context
|
|
2963
|
-
devel
|
|
3067
|
+
method=function.__name__,
|
|
3068
|
+
name=name or function.__name__,
|
|
3069
|
+
description=description,
|
|
3070
|
+
parameters=parameters,
|
|
3071
|
+
context=context,
|
|
3072
|
+
devel=devel,
|
|
2964
3073
|
)
|
|
2965
3074
|
return function
|
|
2966
3075
|
|
|
2967
3076
|
return decorator
|
|
2968
3077
|
|
|
3078
|
+
|
|
2969
3079
|
def operation(
|
|
2970
|
-
name
|
|
2971
|
-
description
|
|
2972
|
-
parameters
|
|
2973
|
-
kwargs
|
|
2974
|
-
factory
|
|
2975
|
-
level
|
|
2976
|
-
devel
|
|
3080
|
+
name=None,
|
|
3081
|
+
description=None,
|
|
3082
|
+
parameters=(),
|
|
3083
|
+
kwargs=None,
|
|
3084
|
+
factory=False,
|
|
3085
|
+
level=1,
|
|
3086
|
+
devel=False,
|
|
2977
3087
|
):
|
|
2978
3088
|
"""
|
|
2979
3089
|
Decorator function to be used to "annotate" the provided
|
|
@@ -3015,26 +3125,22 @@ def operation(
|
|
|
3015
3125
|
|
|
3016
3126
|
def decorator(function, *args, **kwargs):
|
|
3017
3127
|
function._operation = Operation(
|
|
3018
|
-
method
|
|
3019
|
-
name
|
|
3020
|
-
description
|
|
3021
|
-
parameters
|
|
3022
|
-
kwargs
|
|
3023
|
-
factory
|
|
3024
|
-
level
|
|
3025
|
-
devel
|
|
3128
|
+
method=function.__name__,
|
|
3129
|
+
name=name or function.__name__,
|
|
3130
|
+
description=description,
|
|
3131
|
+
parameters=parameters,
|
|
3132
|
+
kwargs=_kwargs,
|
|
3133
|
+
factory=factory,
|
|
3134
|
+
level=level,
|
|
3135
|
+
devel=devel,
|
|
3026
3136
|
)
|
|
3027
3137
|
|
|
3028
3138
|
return function
|
|
3029
3139
|
|
|
3030
3140
|
return decorator
|
|
3031
3141
|
|
|
3032
|
-
|
|
3033
|
-
|
|
3034
|
-
description = None,
|
|
3035
|
-
parameters = (),
|
|
3036
|
-
devel = False
|
|
3037
|
-
):
|
|
3142
|
+
|
|
3143
|
+
def view(name=None, description=None, parameters=(), devel=False):
|
|
3038
3144
|
"""
|
|
3039
3145
|
Decorator function to be used to "annotate" the provided
|
|
3040
3146
|
function as an view that is able to return a set of configurations
|
|
@@ -3062,18 +3168,19 @@ def view(
|
|
|
3062
3168
|
|
|
3063
3169
|
def decorator(function, *args, **kwargs):
|
|
3064
3170
|
function._view = View(
|
|
3065
|
-
method
|
|
3066
|
-
name
|
|
3067
|
-
description
|
|
3068
|
-
parameters
|
|
3069
|
-
devel
|
|
3171
|
+
method=function.__name__,
|
|
3172
|
+
name=name or function.__name__,
|
|
3173
|
+
description=description,
|
|
3174
|
+
parameters=parameters,
|
|
3175
|
+
devel=devel,
|
|
3070
3176
|
)
|
|
3071
3177
|
|
|
3072
3178
|
return function
|
|
3073
3179
|
|
|
3074
3180
|
return decorator
|
|
3075
3181
|
|
|
3076
|
-
|
|
3182
|
+
|
|
3183
|
+
def type_d(type, default=None):
|
|
3077
3184
|
"""
|
|
3078
3185
|
Retrieves the default (initial) value for the a certain
|
|
3079
3186
|
provided data type falling back to the provided default
|
|
@@ -3097,11 +3204,14 @@ def type_d(type, default = None):
|
|
|
3097
3204
|
to the best possible strategy.
|
|
3098
3205
|
"""
|
|
3099
3206
|
|
|
3100
|
-
if not type in TYPE_DEFAULTS:
|
|
3207
|
+
if not type in TYPE_DEFAULTS:
|
|
3208
|
+
return default
|
|
3101
3209
|
default = TYPE_DEFAULTS[type]
|
|
3102
|
-
if not hasattr(default, "__call__"):
|
|
3210
|
+
if not hasattr(default, "__call__"):
|
|
3211
|
+
return default
|
|
3103
3212
|
return default()
|
|
3104
3213
|
|
|
3214
|
+
|
|
3105
3215
|
def is_unset(value):
|
|
3106
3216
|
"""
|
|
3107
3217
|
Verifies if the provided value is unset trying the multiple
|
|
@@ -3118,9 +3228,11 @@ def is_unset(value):
|
|
|
3118
3228
|
underlying promises of the data type of the provided value.
|
|
3119
3229
|
"""
|
|
3120
3230
|
|
|
3121
|
-
if value == None:
|
|
3122
|
-
|
|
3123
|
-
|
|
3231
|
+
if value == None:
|
|
3232
|
+
return True
|
|
3233
|
+
if isinstance(value, typesf.Reference) and not value.is_resolvable():
|
|
3234
|
+
return True
|
|
3124
3235
|
return False
|
|
3125
3236
|
|
|
3237
|
+
|
|
3126
3238
|
field = Field
|