-
Notifications
You must be signed in to change notification settings - Fork 62
/
document.py
118 lines (92 loc) · 3.73 KB
/
document.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
from .data_proxy import DataProxy
from .exceptions import NotCreatedError, AbstractDocumentError
from .meta import MetaDocument, DocumentOpts
from .data_objects import Reference
from .registerer import retrieve_document
from bson import DBRef
def _base_opts():
opts = DocumentOpts('Document', {}, ())
opts.abstract = True
opts.allow_inheritance = True
opts.register_document = False
return opts
class Document(metaclass=MetaDocument):
__slots__ = ('created', '_data', 'parent')
# opts is generated by MetaDocument from Meta class except for root Document
opts = _base_opts()
def __init__(self, **kwargs):
if self.opts.abstract:
raise AbstractDocumentError("Cannot instaciate an abstract Document")
super().__init__()
self.created = False
self._data = DataProxy(self.schema, data=kwargs if kwargs else None)
def __repr__(self):
return '<object Document %s.%s(%s)>' % (
self.__module__, self.__class__.__name__, self._data._data)
def __eq__(self, other):
if self.pk is None:
return self is other
elif isinstance(other, self.__class__) and other.pk is not None:
return self.pk == other.pk
elif isinstance(other, DBRef):
return other.collection == self.collection.name and other.id == self.pk
elif isinstance(other, Reference):
return isinstance(self, other.document_cls) and self.pk == other.pk
return NotImplemented
@property
def pk(self):
"""Return the document's primary key (i.e. `_id` in mongo notation) or
None if not available yet
"""
return self._data.get_by_mongo_name('_id')
@property
def dbref(self):
"""Return a pymongo DBRef instance related to the document
"""
if not self.created:
raise NotCreatedError('Must create the document before'
' having access to DBRef')
return DBRef(collection=self.collection.name, id=self.pk)
@classmethod
def build_from_mongo(cls, data, partial=False, use_cls=False):
# If a _cls is specified, we have to use this document class
if use_cls and '_cls' in data:
cls = retrieve_document(data['_cls'])
doc = cls()
doc.from_mongo(data, partial=partial)
return doc
def from_mongo(self, data, partial=False):
# TODO: handle partial
self._data.from_mongo(data, partial=partial)
self.created = True
def to_mongo(self, update=False):
if update and not self.created:
raise NotCreatedError('Must create the document before'
' using update')
return self._data.to_mongo(update=update)
def update(self, data, **kwargs):
return self._data.update(data, **kwargs)
def dump(self, **kwargs):
return self._data.dump(**kwargs)
def clear_modified(self):
self._data.clear_modified()
@property
def collection(self):
# Cannot implicitly access to the class's property
return type(self).collection
# Data-proxy accessor shortcuts
def __getitem__(self, name):
return self._data.get(name)
def __delitem__(self, name):
self._data.delete(name)
def __setitem__(self, name, value):
self._data.set(name, value)
def __setattr__(self, name, value):
if name in Document.__dict__:
Document.__dict__[name].__set__(self, value)
else:
self._data.set(name, value, to_raise=AttributeError)
def __getattr__(self, name):
return self._data.get(name, to_raise=AttributeError)
def __delattr__(self, name):
self._data.delete(name, to_raise=AttributeError)