-
Notifications
You must be signed in to change notification settings - Fork 196
Expand file tree
/
Copy pathdocument_options.py
More file actions
277 lines (226 loc) · 8.02 KB
/
document_options.py
File metadata and controls
277 lines (226 loc) · 8.02 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
import sys
from django.core.exceptions import FieldDoesNotExist
from django.db.models.options import Options
from django.utils.encoding import smart_str
from django.utils.text import capfirst
try:
from django.db.models.options import get_verbose_name as camel_case_to_spaces
except ImportError:
from django.utils.text import camel_case_to_spaces, format_lazy
from mongoengine.fields import ReferenceField
class PkWrapper:
"""Used to wrap the Primary Key so it can mimic Django's expectations"""
editable = False
remote_field = None
def __init__(self, wrapped):
self.obj = wrapped
def __getattr__(self, attr):
if attr in dir(self.obj):
return getattr(self.obj, attr)
raise AttributeError(f"{self} has no {attr}")
def __setattr__(self, attr, value):
if attr != "obj" and hasattr(self.obj, attr):
setattr(self.obj, attr, value)
super().__setattr__(attr, value)
def value_to_string(self, obj):
"""
Returns a string value of this field from the passed obj.
This is used by the serialization framework.
"""
return smart_str(obj.pk)
def get_internal_type(self):
return "CharField"
class DocumentMetaWrapper:
"""
Used to store mongoengine's _meta dict to make the document admin
as compatible as possible to django's meta class on models.
"""
_pk = None
pk_name = None
object_name = None
model_name = None
verbose_name = None
has_auto_field = False
abstract = False
object_name = None
proxy = []
virtual_fields = []
concrete_fields = []
proxied_children = []
parents = {}
private_fields = []
many_to_many = []
related_fkey_lookups = []
swapped = False
_field_cache = None
document = None
_meta = None
try:
default_apps = Options.default_apps
except AttributeError:
pass
app_config = Options.__dict__["app_config"]
try:
_property_names = Options.__dict__["_property_names"]
except KeyError:
pass
def __init__(self, document):
if isinstance(document._meta, DocumentMetaWrapper):
meta = document._meta._meta
else:
meta = document._meta
self.document = document
self._meta = meta or {}
self.model = document
self.concrete_model = document
self.concrete_fields = document._fields.values()
self.object_name = document.__name__
self.model_name = self.object_name.lower()
self.fields = self.concrete_fields
try:
self.apps = self.default_apps
except AttributeError:
pass
try:
self.object_name = self.document.__name__
except AttributeError:
self.object_name = self.document.__class__.__name__
self.verbose_name = self.get_verbose_name()
# EmbeddedDocuments don't have an id field.
try:
self.pk_name = self._meta["id_field"]
self._init_pk()
except KeyError:
pass
@property
def label(self):
return "%s.%s" % (self.app_label, self.model_name)
@property
def label_lower(self):
return "%s.%s" % (self.app_label, self.model_name)
@property
def app_label(self) -> str:
if "app_label" in self._meta:
return self._meta["app_label"]
else:
model_module = sys.modules[self.document.__module__]
return model_module.__name__.split(".")[-2]
def get_path_to_parent(self, parent):
# This is just placeholders.
# If you depend on this, port it from django.
return []
def get_path_from_parent(self, parent):
return []
def get_verbose_name(self):
"""
Returns the verbose name of the document.
Checks the original meta dict first. If it is not found
then generates a verbose name from from the object name.
"""
if "verbose_name" in self._meta:
return self._meta["verbose_name"]
else:
return capfirst(camel_case_to_spaces(self.object_name))
@property
def verbose_name_raw(self):
return str(self.verbose_name)
@property
def verbose_name_plural(self):
if "verbose_name_plural" in self._meta:
return self.meta["verbose_name_plural"]
else:
return format_lazy("{}s", self.verbose_name)
@property
def pk(self):
if not hasattr(self._pk, "attname"):
self._init_pk()
return self._pk
def get_fields(self, include_parents=True, include_hidden=False):
# XXX: simple placeholder; TODO: handle options;
return self.concrete_fields
def _init_pk(self):
"""
Adds a wrapper around the documents pk field. The wrapper object gets the attributes
django expects on the pk field, like name and attname.
The function also adds a _get_pk_val method to the document.
"""
if self.id_field is None:
return
try:
pk_field = getattr(self.document, self.id_field)
self._pk = PkWrapper(pk_field)
self._pk.name = self.id_field
self._pk.attname = self.id_field
except AttributeError:
return
def get_add_permission(self):
return "add_%s" % self.object_name.lower()
def get_change_permission(self):
return "change_%s" % self.object_name.lower()
def get_delete_permission(self):
return "delete_%s" % self.object_name.lower()
def get_ordered_objects(self):
return []
def get_field_by_name(self, name):
"""
Returns the (field_object, model, direct, m2m), where field_object is
the Field instance for the given name, model is the model containing
this field (None for local fields), direct is True if the field exists
on this model, and m2m is True for many-to-many relations. When
'direct' is False, 'field_object' is the corresponding RelatedObject
for this field (since the field doesn't have an instance associated
with it).
Uses a cache internally, so after the first access, this is very fast.
"""
try:
try:
return self._field_cache[name]
except TypeError:
self._init_field_cache()
return self._field_cache[name]
except KeyError:
raise FieldDoesNotExist("%s has no field named %r" % (self.object_name, name))
def _init_field_cache(self):
if self._field_cache is None:
self._field_cache = {}
for f in self.document._fields.values():
if isinstance(f, ReferenceField):
document = f.document_type
self._field_cache[document._meta.model_name] = (f, document, False, False)
else:
self._field_cache[f.name] = (f, None, True, False)
return self._field_cache
def get_field(self, name, many_to_many=True):
"""
Returns the requested field by name. Raises FieldDoesNotExist on error.
"""
return self.get_field_by_name(name)[0]
def __getattr__(self, name):
try:
return self._meta[name]
except KeyError as e:
raise AttributeError(*e.args)
def __setattr__(self, name, value):
if not hasattr(self, name):
self._meta[name] = value
else:
super().__setattr__(name, value)
def __getitem__(self, key):
return self._meta[key]
def __setitem__(self, key, value):
self._meta[key] = value
def __contains__(self, key):
return key in self._meta
def get(self, key, default=None):
try:
return self.__getitem__(key)
except KeyError:
return default
def get_parent_list(self):
return []
def get_all_related_objects(self, *args, **kwargs):
return []
def iteritems(self):
return self._meta.iteritems()
def items(self):
return self._meta.items()