51
by Gustavo Niemeyer
- Added locals.py. |
1 |
#
|
129
by Gustavo Niemeyer
Added LGPL version 2.1 in the LICENSE file, and a standard |
2 |
# Copyright (c) 2006, 2007 Canonical
|
51
by Gustavo Niemeyer
- Added locals.py. |
3 |
#
|
4 |
# Written by Gustavo Niemeyer <gustavo@niemeyer.net>
|
|
5 |
#
|
|
6 |
# This file is part of Storm Object Relational Mapper.
|
|
7 |
#
|
|
129
by Gustavo Niemeyer
Added LGPL version 2.1 in the LICENSE file, and a standard |
8 |
# Storm is free software; you can redistribute it and/or modify
|
9 |
# it under the terms of the GNU Lesser General Public License as
|
|
10 |
# published by the Free Software Foundation; either version 2.1 of
|
|
11 |
# the License, or (at your option) any later version.
|
|
12 |
#
|
|
13 |
# Storm is distributed in the hope that it will be useful,
|
|
14 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
15 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
16 |
# GNU Lesser General Public License for more details.
|
|
17 |
#
|
|
18 |
# You should have received a copy of the GNU Lesser General Public License
|
|
19 |
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
51
by Gustavo Niemeyer
- Added locals.py. |
20 |
#
|
142.3.20
by Christopher Armstrong
massive doc improvements for store.py |
21 |
|
22 |
"""The Store interface to a database.
|
|
23 |
||
24 |
This module contains the highest-level ORM interface in Storm.
|
|
25 |
"""
|
|
26 |
||
27 |
||
140.1.1
by Johan Dahlin
Pyflakes |
28 |
from weakref import WeakValueDictionary |
10
by Gustavo Niemeyer
- Renamed ClassInfo.primary_key to pk_prop_insts and introduced |
29 |
|
142.2.3
by Gustavo Niemeyer
- Finished implementation of the C version of ObjectInfo |
30 |
from storm.info import get_cls_info, get_obj_info, set_obj_info |
83.1.1
by Gustavo Niemeyer
- Implemented 'autoreload' feature! |
31 |
from storm.variables import Variable, LazyValue |
68
by Gustavo Niemeyer
Added meaningful implementations of ResultSet.{any,one,first,last}(). |
32 |
from storm.expr import ( |
140.1.1
by Johan Dahlin
Pyflakes |
33 |
Expr, Select, Insert, Update, Delete, Column, Count, Max, Min, |
95.3.20
by Gustavo Niemeyer
Initial implementation of ResultSet.union(). order_by() doesn't yet |
34 |
Avg, Sum, Eq, And, Asc, Desc, compile_python, compare_columns, SQLRaw, |
95.3.60
by Gustavo Niemeyer
Implemented support for ResultSet.difference()/intersection(). |
35 |
Union, Except, Intersect, Alias) |
59
by Gustavo Niemeyer
- Created exception hierarchy, and patched DBAPI2 modules to include |
36 |
from storm.exceptions import ( |
68
by Gustavo Niemeyer
Added meaningful implementations of ResultSet.{any,one,first,last}(). |
37 |
WrongStoreError, NotFlushedError, OrderLoopError, UnorderedError, |
95.3.25
by Gustavo Niemeyer
- In Property, do not access obj.__class__, to avoid problems with |
38 |
NotOneError, FeatureError, CompileError, LostObjectError, ClassInfoError) |
58
by Gustavo Niemeyer
- Major refactoring of the typing system. Kinds are gone. Now Variables |
39 |
from storm import Undef |
196.1.4
by Bernd Dorn
implementation of strong reference cache finished |
40 |
from storm.cache import Cache |
7
by Gustavo Niemeyer
- Started implementing Store. |
41 |
|
196.1.6
by Gustavo Niemeyer
These changes address a few issues and perform a few stylistic changes |
42 |
|
92.1.1
by Jamshed Kakar
- Implemented EmptyResultSet. |
43 |
__all__ = ["Store", "AutoReload", "EmptyResultSet"] |
12
by Gustavo Niemeyer
Included some additional checks on Store.add/remove(). |
44 |
|
196.1.6
by Gustavo Niemeyer
These changes address a few issues and perform a few stylistic changes |
45 |
|
26
by Gustavo Niemeyer
Major cleanup in the store and improved caching behavior. |
46 |
PENDING_ADD = 1 |
47 |
PENDING_REMOVE = 2 |
|
11
by Gustavo Niemeyer
Store.add() and remove() now only act on flush. |
48 |
|
7
by Gustavo Niemeyer
- Started implementing Store. |
49 |
|
50 |
class Store(object): |
|
142.3.20
by Christopher Armstrong
massive doc improvements for store.py |
51 |
"""The Storm Store.
|
52 |
||
53 |
This is the highest-level interface to a database. It manages
|
|
54 |
transactions with L{commit} and L{rollback}, caching, high-level
|
|
55 |
querying with L{find}, and more.
|
|
56 |
||
57 |
Note that Store objects are not threadsafe. You should create one
|
|
58 |
Store per thread in your application, passing them the same
|
|
59 |
backend L{Database<storm.store.Database>} object.
|
|
60 |
"""
|
|
7
by Gustavo Niemeyer
- Started implementing Store. |
61 |
|
62
by Gustavo Niemeyer
Changed the way that objects are keyed. Now they use the obj_info, |
62 |
_result_set_factory = None |
63 |
||
169.1.2
by Jamu Kakar
- Moved the store name retrieval functionality entirely into ZStorm, |
64 |
def __init__(self, database): |
142.3.20
by Christopher Armstrong
massive doc improvements for store.py |
65 |
"""
|
66 |
@param database: The L{storm.database.Database} instance to use.
|
|
67 |
"""
|
|
7
by Gustavo Niemeyer
- Started implementing Store. |
68 |
self._connection = database.connect() |
196.1.2
by Bernd Dorn
ups alive is the right name |
69 |
self._alive = WeakValueDictionary() |
95.1.8
by Gustavo Niemeyer
- Event system now has owner as a weak reference, to prevent the |
70 |
self._dirty = {} |
62
by Gustavo Niemeyer
Changed the way that objects are keyed. Now they use the obj_info, |
71 |
self._order = {} # (info, info) = count |
196.1.4
by Bernd Dorn
implementation of strong reference cache finished |
72 |
self._cache = Cache(100) |
7
by Gustavo Niemeyer
- Started implementing Store. |
73 |
|
74 |
@staticmethod
|
|
75 |
def of(obj): |
|
142.3.20
by Christopher Armstrong
massive doc improvements for store.py |
76 |
"""Get the Store that the object is associated with.
|
77 |
||
78 |
If the given object has not yet been associated with a store,
|
|
79 |
return None.
|
|
80 |
"""
|
|
9
by Gustavo Niemeyer
- Merged Connection.execute_expr() into Connection.execute(). |
81 |
try: |
25
by Gustavo Niemeyer
- Moved ObjectInfo and ClassInfo to an implementation based on |
82 |
return get_obj_info(obj).get("store") |
95.3.25
by Gustavo Niemeyer
- In Property, do not access obj.__class__, to avoid problems with |
83 |
except (AttributeError, ClassInfoError): |
9
by Gustavo Niemeyer
- Merged Connection.execute_expr() into Connection.execute(). |
84 |
return None |
85 |
||
19
by Gustavo Niemeyer
- Replaced Store.get_connection() by Store.execute(). |
86 |
def execute(self, statement, params=None, noresult=False): |
142.3.20
by Christopher Armstrong
massive doc improvements for store.py |
87 |
"""Execute a basic query.
|
88 |
||
89 |
This is just like L{storm.database.Database.execute}, except
|
|
90 |
that a flush is performed first.
|
|
91 |
"""
|
|
19
by Gustavo Niemeyer
- Replaced Store.get_connection() by Store.execute(). |
92 |
self.flush() |
93 |
return self._connection.execute(statement, params, noresult) |
|
9
by Gustavo Niemeyer
- Merged Connection.execute_expr() into Connection.execute(). |
94 |
|
59.1.3
by Jamshed Kakar
- Introduced Result.close() and Connection.close(). |
95 |
def close(self): |
142.3.20
by Christopher Armstrong
massive doc improvements for store.py |
96 |
"""Close the connection."""
|
59.1.3
by Jamshed Kakar
- Introduced Result.close() and Connection.close(). |
97 |
self._connection.close() |
98 |
||
9
by Gustavo Niemeyer
- Merged Connection.execute_expr() into Connection.execute(). |
99 |
def commit(self): |
142.3.20
by Christopher Armstrong
massive doc improvements for store.py |
100 |
"""Commit all changes to the database.
|
101 |
||
102 |
This invalidates the cache, so all live objects will have data
|
|
103 |
reloaded next time they are touched.
|
|
104 |
"""
|
|
10
by Gustavo Niemeyer
- Renamed ClassInfo.primary_key to pk_prop_insts and introduced |
105 |
self.flush() |
95.3.71
by Gustavo Niemeyer
As discussed with James Henstrigde, committing and rolling back |
106 |
self.invalidate() |
9
by Gustavo Niemeyer
- Merged Connection.execute_expr() into Connection.execute(). |
107 |
self._connection.commit() |
108 |
||
109 |
def rollback(self): |
|
142.3.20
by Christopher Armstrong
massive doc improvements for store.py |
110 |
"""Roll back all outstanding changes, reverting to database state."""
|
95.3.71
by Gustavo Niemeyer
As discussed with James Henstrigde, committing and rolling back |
111 |
for obj_info in self._dirty: |
112 |
pending = obj_info.pop("pending", None) |
|
113 |
if pending is PENDING_ADD: |
|
114 |
# Object never got in the cache, so being "in the store"
|
|
115 |
# has no actual meaning for it.
|
|
116 |
del obj_info["store"] |
|
117 |
elif pending is PENDING_REMOVE: |
|
118 |
# Object never got removed, so it's still in the cache,
|
|
119 |
# and thus should continue to resolve from now on.
|
|
81.1.20
by Gustavo Niemeyer
- Implemented support for expressions as lazy values in the store! |
120 |
self._enable_lazy_resolving(obj_info) |
26
by Gustavo Niemeyer
Major cleanup in the store and improved caching behavior. |
121 |
self._dirty.clear() |
95.3.71
by Gustavo Niemeyer
As discussed with James Henstrigde, committing and rolling back |
122 |
self.invalidate() |
9
by Gustavo Niemeyer
- Merged Connection.execute_expr() into Connection.execute(). |
123 |
self._connection.rollback() |
7
by Gustavo Niemeyer
- Started implementing Store. |
124 |
|
112
by Gustavo Niemeyer
Removed support for adaptation. In practice we were not able |
125 |
def get(self, cls, key): |
106.1.3
by Gustavo Niemeyer
- Refactored common methods of BoundReferenceSet and |
126 |
"""Get object of type cls with the given primary key from the database.
|
127 |
||
196.1.4
by Bernd Dorn
implementation of strong reference cache finished |
128 |
If the object is alive the database won't be touched.
|
106.1.3
by Gustavo Niemeyer
- Refactored common methods of BoundReferenceSet and |
129 |
|
130 |
@param cls: Class of the object to be retrieved.
|
|
131 |
@param key: Primary key of object. May be a tuple for composed keys.
|
|
171
by Christopher Armstrong
Mention in the Store.get() docstring that None may be returned |
132 |
|
133 |
@return: The object found with the given primary key, or None
|
|
134 |
if no object is found.
|
|
106.1.3
by Gustavo Niemeyer
- Refactored common methods of BoundReferenceSet and |
135 |
"""
|
136 |
||
10
by Gustavo Niemeyer
- Renamed ClassInfo.primary_key to pk_prop_insts and introduced |
137 |
self.flush() |
138 |
||
35
by Gustavo Niemeyer
- Implemented generic hooks in ObjectInfo. |
139 |
if type(key) != tuple: |
7
by Gustavo Niemeyer
- Started implementing Store. |
140 |
key = (key,) |
35
by Gustavo Niemeyer
- Implemented generic hooks in ObjectInfo. |
141 |
|
142 |
cls_info = get_cls_info(cls) |
|
143 |
||
144 |
assert len(key) == len(cls_info.primary_key) |
|
145 |
||
58
by Gustavo Niemeyer
- Major refactoring of the typing system. Kinds are gone. Now Variables |
146 |
primary_vars = [] |
147 |
for column, variable in zip(cls_info.primary_key, key): |
|
148 |
if not isinstance(variable, Variable): |
|
149 |
variable = column.variable_factory(value=variable) |
|
150 |
primary_vars.append(variable) |
|
151 |
||
196.1.2
by Bernd Dorn
ups alive is the right name |
152 |
obj_info = self._alive.get((cls_info.cls, tuple(primary_vars))) |
62
by Gustavo Niemeyer
Changed the way that objects are keyed. Now they use the obj_info, |
153 |
if obj_info is not None: |
95.3.71
by Gustavo Niemeyer
As discussed with James Henstrigde, committing and rolling back |
154 |
if obj_info.get("invalidated"): |
85
by Gustavo Niemeyer
- Greatly improved the autoreload feature support. |
155 |
try: |
196.1.3
by Bernd Dorn
more rename to alive |
156 |
self._validate_alive(obj_info) |
85
by Gustavo Niemeyer
- Greatly improved the autoreload feature support. |
157 |
except LostObjectError: |
158 |
return None |
|
142.2.2
by Gustavo Niemeyer
- When an object died in memory, ResultSet.cached() wasn't returning |
159 |
return self._get_object(obj_info) |
58
by Gustavo Niemeyer
- Major refactoring of the typing system. Kinds are gone. Now Variables |
160 |
|
161 |
where = compare_columns(cls_info.primary_key, primary_vars) |
|
7
by Gustavo Niemeyer
- Started implementing Store. |
162 |
|
33
by Gustavo Niemeyer
- Refactored the way properties are stored in the object info. Now |
163 |
select = Select(cls_info.columns, where, |
24
by Gustavo Niemeyer
- Removed AutoTable. It's now implemented internally in statements. |
164 |
default_tables=cls_info.table, limit=1) |
7
by Gustavo Niemeyer
- Started implementing Store. |
165 |
|
40
by Gustavo Niemeyer
- Implemented result.to_kind(). It'll be used soon to process |
166 |
result = self._connection.execute(select) |
50
by Gustavo Niemeyer
- Renamed Result.fetch_one/all to get_one/all. |
167 |
values = result.get_one() |
14
by Gustavo Niemeyer
Minor changes for beautification. |
168 |
if values is None: |
7
by Gustavo Niemeyer
- Started implementing Store. |
169 |
return None |
196.1.5
by Bernd Dorn
added more tests and changed according to review from niemeyer |
170 |
return self._load_object(cls_info, result, values) |
7
by Gustavo Niemeyer
- Started implementing Store. |
171 |
|
81.1.5
by Gustavo Niemeyer
Implemented support for using a tuple of classes as the first argument of |
172 |
def find(self, cls_spec, *args, **kwargs): |
142.3.20
by Christopher Armstrong
massive doc improvements for store.py |
173 |
"""Perform a query.
|
174 |
||
175 |
Some examples::
|
|
176 |
||
177 |
store.find(Person, Person.name == u"Joe") --> all Persons named Joe
|
|
178 |
store.find(Person, name=u"Joe") --> same
|
|
179 |
store.find((Company, Person), Person.company_id == Company.id) -->
|
|
180 |
iterator of tuples of Company and Person instances which are
|
|
181 |
associated via the company_id -> Company relation.
|
|
182 |
||
183 |
@param cls_spec: The class or tuple of classes whose
|
|
184 |
associated tables will be queried.
|
|
185 |
@param args: Instances of L{Expr}.
|
|
186 |
@param kwargs: Mapping of simple column names to values or
|
|
187 |
expressions to query for.
|
|
188 |
||
189 |
@return: A L{ResultSet} of instances C{cls_spec}. If C{cls_spec}
|
|
190 |
was a tuple, then an iterator of tuples of such instances.
|
|
191 |
"""
|
|
10
by Gustavo Niemeyer
- Renamed ClassInfo.primary_key to pk_prop_insts and introduced |
192 |
self.flush() |
81.1.5
by Gustavo Niemeyer
Implemented support for using a tuple of classes as the first argument of |
193 |
if type(cls_spec) is tuple: |
194 |
cls_spec_info = tuple(get_cls_info(cls) for cls in cls_spec) |
|
195 |
where = get_where_for_args(args, kwargs) |
|
196 |
else: |
|
197 |
cls_spec_info = get_cls_info(cls_spec) |
|
198 |
where = get_where_for_args(args, kwargs, cls_spec) |
|
199 |
return self._result_set_factory(self, cls_spec_info, where) |
|
9
by Gustavo Niemeyer
- Merged Connection.execute_expr() into Connection.execute(). |
200 |
|
81.1.3
by Gustavo Niemeyer
- Redesigned join expressions to handle more complex cases. |
201 |
def using(self, *tables): |
142.3.21
by Christopher Armstrong
improvements to store documentation on Gustavo's advice, thanks! |
202 |
"""Specify tables to use explicitly.
|
203 |
||
204 |
The L{find} method generally does a good job at figuring out
|
|
205 |
the tables to query by itself, but in some cases it's useful
|
|
206 |
to specify them explicitly.
|
|
207 |
||
208 |
This is most often necessary when an explicit SQL join is
|
|
209 |
required. An example follows::
|
|
210 |
||
211 |
join = LeftJoin(Person, Person.id == Company.person_id)
|
|
212 |
print list(store.using(Company, join).find((Company, Person)))
|
|
213 |
||
214 |
The previous code snippet will produce an SQL statement
|
|
215 |
somewhat similar to this, depending on your backend::
|
|
216 |
||
217 |
SELECT company.id, employee.company_id, employee.id
|
|
218 |
FROM company
|
|
219 |
LEFT JOIN employee ON employee.company_id = company.id;
|
|
220 |
||
221 |
@return: A L{TableSet}, which has a C{find} method similar to
|
|
222 |
L{Store.find}.
|
|
142.3.20
by Christopher Armstrong
massive doc improvements for store.py |
223 |
"""
|
81.1.12
by Gustavo Niemeyer
- Reimplemented currval() call in the postgres database backend |
224 |
return self._table_set(self, tables) |
81.1.3
by Gustavo Niemeyer
- Redesigned join expressions to handle more complex cases. |
225 |
|
9
by Gustavo Niemeyer
- Merged Connection.execute_expr() into Connection.execute(). |
226 |
def add(self, obj): |
142.3.20
by Christopher Armstrong
massive doc improvements for store.py |
227 |
"""Add the given object to the store.
|
228 |
||
229 |
The object will be inserted into the database if it has not
|
|
230 |
yet been added.
|
|
231 |
||
232 |
The C{added} event will be fired on the object info's event system.
|
|
233 |
"""
|
|
25
by Gustavo Niemeyer
- Moved ObjectInfo and ClassInfo to an implementation based on |
234 |
obj_info = get_obj_info(obj) |
26
by Gustavo Niemeyer
Major cleanup in the store and improved caching behavior. |
235 |
|
25
by Gustavo Niemeyer
- Moved ObjectInfo and ClassInfo to an implementation based on |
236 |
store = obj_info.get("store") |
26
by Gustavo Niemeyer
Major cleanup in the store and improved caching behavior. |
237 |
if store is not None and store is not self: |
62
by Gustavo Niemeyer
Changed the way that objects are keyed. Now they use the obj_info, |
238 |
raise WrongStoreError("%s is part of another store" % repr(obj)) |
26
by Gustavo Niemeyer
Major cleanup in the store and improved caching behavior. |
239 |
|
240 |
pending = obj_info.get("pending") |
|
241 |
||
242 |
if pending is PENDING_ADD: |
|
60
by Gustavo Niemeyer
- Implemented Store.new() |
243 |
pass
|
26
by Gustavo Niemeyer
Major cleanup in the store and improved caching behavior. |
244 |
elif pending is PENDING_REMOVE: |
245 |
del obj_info["pending"] |
|
81.1.20
by Gustavo Niemeyer
- Implemented support for expressions as lazy values in the store! |
246 |
self._enable_lazy_resolving(obj_info) |
61
by Gustavo Niemeyer
- Now ReferenceSets will not require the object to be in a store |
247 |
# obj_info.event.emit("added")
|
95.3.71
by Gustavo Niemeyer
As discussed with James Henstrigde, committing and rolling back |
248 |
elif store is None: |
249 |
obj_info["store"] = self |
|
26
by Gustavo Niemeyer
Major cleanup in the store and improved caching behavior. |
250 |
obj_info["pending"] = PENDING_ADD |
62
by Gustavo Niemeyer
Changed the way that objects are keyed. Now they use the obj_info, |
251 |
self._set_dirty(obj_info) |
81.1.20
by Gustavo Niemeyer
- Implemented support for expressions as lazy values in the store! |
252 |
self._enable_lazy_resolving(obj_info) |
60
by Gustavo Niemeyer
- Implemented Store.new() |
253 |
obj_info.event.emit("added") |
9
by Gustavo Niemeyer
- Merged Connection.execute_expr() into Connection.execute(). |
254 |
|
101.1.1
by Jamu Kakar
- Store.add(obj) returns obj. |
255 |
return obj |
256 |
||
9
by Gustavo Niemeyer
- Merged Connection.execute_expr() into Connection.execute(). |
257 |
def remove(self, obj): |
142.3.20
by Christopher Armstrong
massive doc improvements for store.py |
258 |
"""Remove the given object from the store.
|
259 |
||
260 |
The associated row will be deleted from the database.
|
|
261 |
"""
|
|
25
by Gustavo Niemeyer
- Moved ObjectInfo and ClassInfo to an implementation based on |
262 |
obj_info = get_obj_info(obj) |
26
by Gustavo Niemeyer
Major cleanup in the store and improved caching behavior. |
263 |
|
44
by Gustavo Niemeyer
- Decoupled most of the logic from Reference into Relation, and |
264 |
if obj_info.get("store") is not self: |
62
by Gustavo Niemeyer
Changed the way that objects are keyed. Now they use the obj_info, |
265 |
raise WrongStoreError("%s is not in this store" % repr(obj)) |
26
by Gustavo Niemeyer
Major cleanup in the store and improved caching behavior. |
266 |
|
267 |
pending = obj_info.get("pending") |
|
268 |
||
269 |
if pending is PENDING_REMOVE: |
|
60
by Gustavo Niemeyer
- Implemented Store.new() |
270 |
pass
|
26
by Gustavo Niemeyer
Major cleanup in the store and improved caching behavior. |
271 |
elif pending is PENDING_ADD: |
95.3.71
by Gustavo Niemeyer
As discussed with James Henstrigde, committing and rolling back |
272 |
del obj_info["store"] |
26
by Gustavo Niemeyer
Major cleanup in the store and improved caching behavior. |
273 |
del obj_info["pending"] |
62
by Gustavo Niemeyer
Changed the way that objects are keyed. Now they use the obj_info, |
274 |
self._set_clean(obj_info) |
81.1.20
by Gustavo Niemeyer
- Implemented support for expressions as lazy values in the store! |
275 |
self._disable_lazy_resolving(obj_info) |
95.3.71
by Gustavo Niemeyer
As discussed with James Henstrigde, committing and rolling back |
276 |
else: |
26
by Gustavo Niemeyer
Major cleanup in the store and improved caching behavior. |
277 |
obj_info["pending"] = PENDING_REMOVE |
62
by Gustavo Niemeyer
Changed the way that objects are keyed. Now they use the obj_info, |
278 |
self._set_dirty(obj_info) |
81.1.20
by Gustavo Niemeyer
- Implemented support for expressions as lazy values in the store! |
279 |
self._disable_lazy_resolving(obj_info) |
10
by Gustavo Niemeyer
- Renamed ClassInfo.primary_key to pk_prop_insts and introduced |
280 |
|
44
by Gustavo Niemeyer
- Decoupled most of the logic from Reference into Relation, and |
281 |
def reload(self, obj): |
142.3.20
by Christopher Armstrong
massive doc improvements for store.py |
282 |
"""Reload the given object.
|
283 |
||
284 |
The object will immediately have all of its data reset from
|
|
285 |
the database. Any pending changes will be thrown away.
|
|
286 |
"""
|
|
62
by Gustavo Niemeyer
Changed the way that objects are keyed. Now they use the obj_info, |
287 |
obj_info = get_obj_info(obj) |
288 |
cls_info = obj_info.cls_info |
|
95.3.71
by Gustavo Niemeyer
As discussed with James Henstrigde, committing and rolling back |
289 |
if obj_info.get("store") is not self: |
62
by Gustavo Niemeyer
Changed the way that objects are keyed. Now they use the obj_info, |
290 |
raise WrongStoreError("%s is not in this store" % repr(obj)) |
58
by Gustavo Niemeyer
- Major refactoring of the typing system. Kinds are gone. Now Variables |
291 |
if "primary_vars" not in obj_info: |
59
by Gustavo Niemeyer
- Created exception hierarchy, and patched DBAPI2 modules to include |
292 |
raise NotFlushedError("Can't reload an object if it was " |
293 |
"never flushed") |
|
62
by Gustavo Niemeyer
Changed the way that objects are keyed. Now they use the obj_info, |
294 |
where = compare_columns(cls_info.primary_key, obj_info["primary_vars"]) |
44
by Gustavo Niemeyer
- Decoupled most of the logic from Reference into Relation, and |
295 |
select = Select(cls_info.columns, where, |
296 |
default_tables=cls_info.table, limit=1) |
|
297 |
result = self._connection.execute(select) |
|
50
by Gustavo Niemeyer
- Renamed Result.fetch_one/all to get_one/all. |
298 |
values = result.get_one() |
58
by Gustavo Niemeyer
- Major refactoring of the typing system. Kinds are gone. Now Variables |
299 |
self._set_values(obj_info, cls_info.columns, result, values) |
44
by Gustavo Niemeyer
- Decoupled most of the logic from Reference into Relation, and |
300 |
obj_info.checkpoint() |
62
by Gustavo Niemeyer
Changed the way that objects are keyed. Now they use the obj_info, |
301 |
self._set_clean(obj_info) |
44
by Gustavo Niemeyer
- Decoupled most of the logic from Reference into Relation, and |
302 |
|
83.1.1
by Gustavo Niemeyer
- Implemented 'autoreload' feature! |
303 |
def autoreload(self, obj=None): |
142.3.20
by Christopher Armstrong
massive doc improvements for store.py |
304 |
"""Set an object or all objects to be reloaded automatically on access.
|
305 |
||
142.3.21
by Christopher Armstrong
improvements to store documentation on Gustavo's advice, thanks! |
306 |
When a database-backed attribute of one of the objects is
|
307 |
accessed, the object will be reloaded entirely from the database.
|
|
142.3.20
by Christopher Armstrong
massive doc improvements for store.py |
308 |
|
309 |
@param obj: If passed, only mark the given object for
|
|
310 |
autoreload. Otherwise, all cached objects will be marked for
|
|
311 |
autoreload.
|
|
312 |
"""
|
|
85
by Gustavo Niemeyer
- Greatly improved the autoreload feature support. |
313 |
self._mark_autoreload(obj, False) |
314 |
||
315 |
def invalidate(self, obj=None): |
|
142.3.21
by Christopher Armstrong
improvements to store documentation on Gustavo's advice, thanks! |
316 |
"""Set an object or all objects to be invalidated.
|
317 |
||
318 |
This prevents Storm from returning the cached object without
|
|
319 |
first verifying that the object is still available in the
|
|
320 |
database.
|
|
321 |
||
322 |
This should almost never be called by application code; it is
|
|
323 |
only necessary if it is possible that an object has
|
|
324 |
disappeared through some mechanism that Storm was unable to
|
|
325 |
detect, like direct SQL statements within the current
|
|
326 |
transaction that bypassed the ORM layer. The Store
|
|
327 |
automatically invalidates all cached objects on transaction
|
|
328 |
boundaries.
|
|
142.3.20
by Christopher Armstrong
massive doc improvements for store.py |
329 |
"""
|
196.1.4
by Bernd Dorn
implementation of strong reference cache finished |
330 |
if obj is None: |
331 |
self._cache.clear() |
|
332 |
else: |
|
333 |
self._cache.remove(get_obj_info(obj)) |
|
85
by Gustavo Niemeyer
- Greatly improved the autoreload feature support. |
334 |
self._mark_autoreload(obj, True) |
335 |
||
336 |
def _mark_autoreload(self, obj=None, invalidate=False): |
|
337 |
if obj is None: |
|
196.1.3
by Bernd Dorn
more rename to alive |
338 |
obj_infos = self._iter_alive() |
85
by Gustavo Niemeyer
- Greatly improved the autoreload feature support. |
339 |
else: |
340 |
obj_infos = (get_obj_info(obj),) |
|
341 |
for obj_info in obj_infos: |
|
342 |
cls_info = obj_info.cls_info |
|
343 |
for column in cls_info.columns: |
|
91
by Gustavo Niemeyer
- Autoreloading objects won't autoreload the primary key, since it's |
344 |
if id(column) not in cls_info.primary_key_idx: |
345 |
obj_info.variables[column].set(AutoReload) |
|
85
by Gustavo Niemeyer
- Greatly improved the autoreload feature support. |
346 |
if invalidate: |
95.3.71
by Gustavo Niemeyer
As discussed with James Henstrigde, committing and rolling back |
347 |
# Marking an object with 'invalidated' means that we're
|
348 |
# not sure if the object is actually in the database
|
|
349 |
# anymore, so before the object is returned from the cache
|
|
350 |
# (e.g. by a get()), the database should be queried to see
|
|
351 |
# if the object's still there.
|
|
352 |
obj_info["invalidated"] = True |
|
106.1.5
by Christopher Armstrong
Clean up magic-typing a bit: |
353 |
# We want to make sure we've marked all objects as invalidated and set
|
354 |
# up their autoreloads before calling the invalidated hook on *any* of
|
|
112
by Gustavo Niemeyer
Removed support for adaptation. In practice we were not able |
355 |
# them, because an invalidated hook might use other objects and we want
|
356 |
# to prevent invalidation ordering issues.
|
|
95.6.7
by Gustavo Niemeyer
Renamed invalidate hook to invalidated, confirming to other |
357 |
if invalidate: |
358 |
for obj_info in obj_infos: |
|
359 |
self._run_hook(obj_info, "__storm_invalidated__") |
|
85
by Gustavo Niemeyer
- Greatly improved the autoreload feature support. |
360 |
|
36
by Gustavo Niemeyer
- Implemented support for back references. |
361 |
def add_flush_order(self, before, after): |
142.3.20
by Christopher Armstrong
massive doc improvements for store.py |
362 |
"""Explicitly specify the order of flushing two objects.
|
363 |
||
364 |
When the next database flush occurs, the order of data
|
|
365 |
modification statements will be ensured.
|
|
366 |
||
367 |
@param before: The object to flush first.
|
|
368 |
@param after: The object to flush after C{before}.
|
|
369 |
"""
|
|
62
by Gustavo Niemeyer
Changed the way that objects are keyed. Now they use the obj_info, |
370 |
pair = (get_obj_info(before), get_obj_info(after)) |
36
by Gustavo Niemeyer
- Implemented support for back references. |
371 |
try: |
372 |
self._order[pair] += 1 |
|
373 |
except KeyError: |
|
374 |
self._order[pair] = 1 |
|
375 |
||
376 |
def remove_flush_order(self, before, after): |
|
142.3.20
by Christopher Armstrong
massive doc improvements for store.py |
377 |
"""Cancel an explicit flush order specified with L{add_flush_order}.
|
378 |
||
379 |
@param before: The C{before} object previously specified in a
|
|
380 |
call to L{add_flush_order}.
|
|
381 |
@param after: The C{after} object previously specified in a
|
|
382 |
call to L{add_flush_order}.
|
|
383 |
"""
|
|
62
by Gustavo Niemeyer
Changed the way that objects are keyed. Now they use the obj_info, |
384 |
pair = (get_obj_info(before), get_obj_info(after)) |
36
by Gustavo Niemeyer
- Implemented support for back references. |
385 |
try: |
386 |
self._order[pair] -= 1 |
|
387 |
except KeyError: |
|
388 |
pass
|
|
389 |
||
10
by Gustavo Niemeyer
- Renamed ClassInfo.primary_key to pk_prop_insts and introduced |
390 |
def flush(self): |
144.1.2
by Bjorn Stabell
Added docstring to flush() based on reviews from Christopher Armstrong and Jamu Kakar. |
391 |
"""Flush all dirty objects in cache to database.
|
392 |
||
144.1.3
by Gustavo Niemeyer
- Renamed __storm_flush__ to __storm_pre_flush__, as discussed. |
393 |
This method will first call the __storm_pre_flush__ hook of all dirty
|
144.1.2
by Bjorn Stabell
Added docstring to flush() based on reviews from Christopher Armstrong and Jamu Kakar. |
394 |
objects. If more objects become dirty as a result of executing code
|
395 |
in the hooks, the hook is also called on them. The hook is only
|
|
396 |
called once for each object.
|
|
397 |
||
144.1.3
by Gustavo Niemeyer
- Renamed __storm_flush__ to __storm_pre_flush__, as discussed. |
398 |
It will then flush each dirty object to the database, that is,
|
399 |
execute the SQL code to insert/delete/update them. After each
|
|
400 |
object is flushed, the hook __storm_flushed__ is called on it,
|
|
401 |
and if changes are made to the object it will get back to the
|
|
402 |
dirty list, and be flushed again.
|
|
144.1.2
by Bjorn Stabell
Added docstring to flush() based on reviews from Christopher Armstrong and Jamu Kakar. |
403 |
|
404 |
Note that Storm will flush objects for you automatically, so you'll
|
|
405 |
only need to call this method explicitly in very rare cases where
|
|
406 |
normal flushing times are insufficient, such as when you want to
|
|
407 |
make sure a database trigger gets run at a particular time.
|
|
142.3.20
by Christopher Armstrong
massive doc improvements for store.py |
408 |
"""
|
196.1.3
by Bernd Dorn
more rename to alive |
409 |
for obj_info in self._iter_alive(): |
95.1.8
by Gustavo Niemeyer
- Event system now has owner as a weak reference, to prevent the |
410 |
obj_info.event.emit("flush") |
411 |
||
144.1.1
by Bjorn Stabell
Pair programming: Added __storm_flush__ hook that will be called before commits, as opposed to |
412 |
# The _dirty list may change under us while we're running
|
413 |
# the flush hooks, so we cannot just simply loop over it
|
|
414 |
# once. To prevent infinite looping we keep track of which
|
|
415 |
# objects we've called the hook for using a `flushing` dict.
|
|
416 |
flushing = {} |
|
417 |
while self._dirty: |
|
418 |
(obj_info, obj) = self._dirty.popitem() |
|
419 |
if obj_info not in flushing: |
|
420 |
flushing[obj_info] = obj |
|
144.1.3
by Gustavo Niemeyer
- Renamed __storm_flush__ to __storm_pre_flush__, as discussed. |
421 |
self._run_hook(obj_info, "__storm_pre_flush__") |
144.1.1
by Bjorn Stabell
Pair programming: Added __storm_flush__ hook that will be called before commits, as opposed to |
422 |
self._dirty = flushing |
423 |
||
36
by Gustavo Niemeyer
- Implemented support for back references. |
424 |
predecessors = {} |
62
by Gustavo Niemeyer
Changed the way that objects are keyed. Now they use the obj_info, |
425 |
for (before_info, after_info), n in self._order.iteritems(): |
36
by Gustavo Niemeyer
- Implemented support for back references. |
426 |
if n > 0: |
62
by Gustavo Niemeyer
Changed the way that objects are keyed. Now they use the obj_info, |
427 |
before_set = predecessors.get(after_info) |
36
by Gustavo Niemeyer
- Implemented support for back references. |
428 |
if before_set is None: |
62
by Gustavo Niemeyer
Changed the way that objects are keyed. Now they use the obj_info, |
429 |
predecessors[after_info] = set((before_info,)) |
36
by Gustavo Niemeyer
- Implemented support for back references. |
430 |
else: |
62
by Gustavo Niemeyer
Changed the way that objects are keyed. Now they use the obj_info, |
431 |
before_set.add(before_info) |
36
by Gustavo Niemeyer
- Implemented support for back references. |
432 |
|
35
by Gustavo Niemeyer
- Implemented generic hooks in ObjectInfo. |
433 |
while self._dirty: |
62
by Gustavo Niemeyer
Changed the way that objects are keyed. Now they use the obj_info, |
434 |
for obj_info in self._dirty: |
435 |
for before_info in predecessors.get(obj_info, ()): |
|
436 |
if before_info in self._dirty: |
|
36
by Gustavo Niemeyer
- Implemented support for back references. |
437 |
break # A predecessor is still dirty. |
438 |
else: |
|
439 |
break # Found an item without dirty predecessors. |
|
440 |
else: |
|
59
by Gustavo Niemeyer
- Created exception hierarchy, and patched DBAPI2 modules to include |
441 |
raise OrderLoopError("Can't flush due to ordering loop") |
95.1.8
by Gustavo Niemeyer
- Event system now has owner as a weak reference, to prevent the |
442 |
self._dirty.pop(obj_info, None) |
62
by Gustavo Niemeyer
Changed the way that objects are keyed. Now they use the obj_info, |
443 |
self._flush_one(obj_info) |
36
by Gustavo Niemeyer
- Implemented support for back references. |
444 |
|
445 |
self._order.clear() |
|
446 |
||
62
by Gustavo Niemeyer
Changed the way that objects are keyed. Now they use the obj_info, |
447 |
def _flush_one(self, obj_info): |
448 |
cls_info = obj_info.cls_info |
|
35
by Gustavo Niemeyer
- Implemented generic hooks in ObjectInfo. |
449 |
|
450 |
pending = obj_info.pop("pending", None) |
|
451 |
||
452 |
if pending is PENDING_REMOVE: |
|
36
by Gustavo Niemeyer
- Implemented support for back references. |
453 |
expr = Delete(compare_columns(cls_info.primary_key, |
58
by Gustavo Niemeyer
- Major refactoring of the typing system. Kinds are gone. Now Variables |
454 |
obj_info["primary_vars"]), |
35
by Gustavo Niemeyer
- Implemented generic hooks in ObjectInfo. |
455 |
cls_info.table) |
456 |
self._connection.execute(expr, noresult=True) |
|
457 |
||
85
by Gustavo Niemeyer
- Greatly improved the autoreload feature support. |
458 |
# We're sure the cache is valid at this point.
|
95.3.71
by Gustavo Niemeyer
As discussed with James Henstrigde, committing and rolling back |
459 |
obj_info.pop("invalidated", None) |
85
by Gustavo Niemeyer
- Greatly improved the autoreload feature support. |
460 |
|
62
by Gustavo Niemeyer
Changed the way that objects are keyed. Now they use the obj_info, |
461 |
self._disable_change_notification(obj_info) |
196.1.3
by Bernd Dorn
more rename to alive |
462 |
self._remove_from_alive(obj_info) |
95.3.71
by Gustavo Niemeyer
As discussed with James Henstrigde, committing and rolling back |
463 |
del obj_info["store"] |
35
by Gustavo Niemeyer
- Implemented generic hooks in ObjectInfo. |
464 |
|
465 |
elif pending is PENDING_ADD: |
|
156.1.1
by Gustavo Niemeyer
- Implemented support for lazy expressions in primary keys. This opens |
466 |
|
467 |
# Give a chance to the backend to process primary variables.
|
|
468 |
self._connection.preset_primary_key(cls_info.primary_key, |
|
469 |
obj_info.primary_vars) |
|
470 |
||
471 |
changes = self._get_changes_map(obj_info, True) |
|
472 |
||
473 |
expr = Insert(changes, cls_info.table, |
|
474 |
primary_columns=cls_info.primary_key, |
|
475 |
primary_variables=obj_info.primary_vars) |
|
35
by Gustavo Niemeyer
- Implemented generic hooks in ObjectInfo. |
476 |
|
477 |
result = self._connection.execute(expr) |
|
478 |
||
85
by Gustavo Niemeyer
- Greatly improved the autoreload feature support. |
479 |
# We're sure the cache is valid at this point. We just added
|
480 |
# the object.
|
|
95.3.71
by Gustavo Niemeyer
As discussed with James Henstrigde, committing and rolling back |
481 |
obj_info.pop("invalidated", None) |
85
by Gustavo Niemeyer
- Greatly improved the autoreload feature support. |
482 |
|
190.1.2
by Gustavo Niemeyer
Rather than retrieving all values from the database after an object |
483 |
self._fill_missing_values(obj_info, obj_info.primary_vars, result) |
35
by Gustavo Niemeyer
- Implemented generic hooks in ObjectInfo. |
484 |
|
62
by Gustavo Niemeyer
Changed the way that objects are keyed. Now they use the obj_info, |
485 |
self._enable_change_notification(obj_info) |
196.1.3
by Bernd Dorn
more rename to alive |
486 |
self._add_to_alive(obj_info) |
35
by Gustavo Niemeyer
- Implemented generic hooks in ObjectInfo. |
487 |
|
44
by Gustavo Niemeyer
- Decoupled most of the logic from Reference into Relation, and |
488 |
obj_info.checkpoint() |
489 |
||
58
by Gustavo Niemeyer
- Major refactoring of the typing system. Kinds are gone. Now Variables |
490 |
else: |
39
by Gustavo Niemeyer
- Implemented new column typing and value filtering system |
491 |
|
85
by Gustavo Niemeyer
- Greatly improved the autoreload feature support. |
492 |
cached_primary_vars = obj_info["primary_vars"] |
493 |
primary_key_idx = cls_info.primary_key_idx |
|
494 |
||
156.1.1
by Gustavo Niemeyer
- Implemented support for lazy expressions in primary keys. This opens |
495 |
changes = self._get_changes_map(obj_info) |
35
by Gustavo Niemeyer
- Implemented generic hooks in ObjectInfo. |
496 |
|
58
by Gustavo Niemeyer
- Major refactoring of the typing system. Kinds are gone. Now Variables |
497 |
if changes: |
498 |
expr = Update(changes, |
|
36
by Gustavo Niemeyer
- Implemented support for back references. |
499 |
compare_columns(cls_info.primary_key, |
85
by Gustavo Niemeyer
- Greatly improved the autoreload feature support. |
500 |
cached_primary_vars), |
26
by Gustavo Niemeyer
Major cleanup in the store and improved caching behavior. |
501 |
cls_info.table) |
502 |
self._connection.execute(expr, noresult=True) |
|
503 |
||
190.1.2
by Gustavo Niemeyer
Rather than retrieving all values from the database after an object |
504 |
self._fill_missing_values(obj_info, obj_info.primary_vars) |
81.1.20
by Gustavo Niemeyer
- Implemented support for expressions as lazy values in the store! |
505 |
|
196.1.3
by Bernd Dorn
more rename to alive |
506 |
self._add_to_alive(obj_info) |
26
by Gustavo Niemeyer
Major cleanup in the store and improved caching behavior. |
507 |
|
85
by Gustavo Niemeyer
- Greatly improved the autoreload feature support. |
508 |
|
44
by Gustavo Niemeyer
- Decoupled most of the logic from Reference into Relation, and |
509 |
obj_info.checkpoint() |
510 |
||
95.6.3
by Gustavo Niemeyer
Renamed all hooks to __storm_<name>__, instead of __<name>__. |
511 |
self._run_hook(obj_info, "__storm_flushed__") |
95.6.2
by Gustavo Niemeyer
Implemented __flushed__ hook. |
512 |
|
58
by Gustavo Niemeyer
- Major refactoring of the typing system. Kinds are gone. Now Variables |
513 |
obj_info.event.emit("flushed") |
10
by Gustavo Niemeyer
- Renamed ClassInfo.primary_key to pk_prop_insts and introduced |
514 |
|
156.1.1
by Gustavo Niemeyer
- Implemented support for lazy expressions in primary keys. This opens |
515 |
def _get_changes_map(self, obj_info, adding=False): |
516 |
"""Return a {column: variable} dictionary suitable for inserts/updates.
|
|
517 |
||
518 |
@param obj_info: ObjectInfo to inspect for changes.
|
|
519 |
@param adding: If true, any defined variables will be considered
|
|
520 |
a change and included in the returned map.
|
|
521 |
"""
|
|
522 |
cls_info = obj_info.cls_info |
|
523 |
changes = {} |
|
524 |
select_variables = [] |
|
525 |
for column in cls_info.columns: |
|
526 |
variable = obj_info.variables[column] |
|
527 |
if adding or variable.has_changed(): |
|
528 |
if variable.is_defined(): |
|
529 |
changes[column] = variable |
|
530 |
else: |
|
531 |
lazy_value = variable.get_lazy() |
|
532 |
if isinstance(lazy_value, Expr): |
|
533 |
if id(column) in cls_info.primary_key_idx: |
|
534 |
select_variables.append(variable) # See below. |
|
535 |
changes[column] = variable |
|
536 |
else: |
|
537 |
changes[column] = lazy_value |
|
538 |
||
539 |
# If we have any expressions in the primary variables, we
|
|
540 |
# have to resolve them now so that we have the identity of
|
|
541 |
# the inserted object available later.
|
|
542 |
if select_variables: |
|
543 |
resolve_expr = Select([variable.get_lazy() |
|
544 |
for variable in select_variables]) |
|
545 |
result = self._connection.execute(resolve_expr) |
|
546 |
for variable, value in zip(select_variables, result.get_one()): |
|
547 |
result.set_variable(variable, value) |
|
548 |
||
549 |
return changes |
|
550 |
||
190.1.2
by Gustavo Niemeyer
Rather than retrieving all values from the database after an object |
551 |
def _fill_missing_values(self, obj_info, primary_vars, result=None): |
552 |
"""Fill missing values in variables of the given obj_info.
|
|
133
by Gustavo Niemeyer
Added big fat comment in Store._fill_missing_values(), as suggested |
553 |
|
554 |
This method will verify which values are unset in obj_info,
|
|
190.1.2
by Gustavo Niemeyer
Rather than retrieving all values from the database after an object |
555 |
and set them to AutoReload, or if it's part of the primary
|
556 |
key, query the database for the actual values.
|
|
133
by Gustavo Niemeyer
Added big fat comment in Store._fill_missing_values(), as suggested |
557 |
|
558 |
@param obj_info: ObjectInfo to have its values filled.
|
|
559 |
@param primary_vars: Variables composing the primary key with
|
|
560 |
up-to-date values (cached variables may be out-of-date when
|
|
561 |
this method is called).
|
|
562 |
@param result: If some value in the set of primary variables
|
|
563 |
isn't defined, it must be retrieved from the database
|
|
564 |
using database-dependent logic, which is provided by the
|
|
565 |
backend in the result of the query which inserted the object.
|
|
566 |
"""
|
|
62
by Gustavo Niemeyer
Changed the way that objects are keyed. Now they use the obj_info, |
567 |
cls_info = obj_info.cls_info |
30
by Gustavo Niemeyer
- Added support for retrieving values automatically set by the |
568 |
|
85
by Gustavo Niemeyer
- Greatly improved the autoreload feature support. |
569 |
cached_primary_vars = obj_info.get("primary_vars") |
570 |
primary_key_idx = cls_info.primary_key_idx |
|
33
by Gustavo Niemeyer
- Refactored the way properties are stored in the object info. Now |
571 |
missing_columns = [] |
39
by Gustavo Niemeyer
- Implemented new column typing and value filtering system |
572 |
for column in cls_info.columns: |
85
by Gustavo Niemeyer
- Greatly improved the autoreload feature support. |
573 |
variable = obj_info.variables[column] |
574 |
if not variable.is_defined(): |
|
575 |
idx = primary_key_idx.get(id(column)) |
|
190.1.2
by Gustavo Niemeyer
Rather than retrieving all values from the database after an object |
576 |
if idx is not None: |
577 |
if (cached_primary_vars is not None |
|
578 |
and variable.get_lazy() is AutoReload): |
|
579 |
# For auto-reloading a primary key, just
|
|
580 |
# get the value out of the cache.
|
|
581 |
variable.set(cached_primary_vars[idx].get()) |
|
582 |
else: |
|
583 |
missing_columns.append(column) |
|
584 |
else: |
|
585 |
# Any lazy values are overwritten here. This value
|
|
586 |
# must have just been sent to the database, so this
|
|
587 |
# was already set there.
|
|
588 |
variable.set(AutoReload) |
|
30
by Gustavo Niemeyer
- Added support for retrieving values automatically set by the |
589 |
|
33
by Gustavo Niemeyer
- Refactored the way properties are stored in the object info. Now |
590 |
if missing_columns: |
190.1.2
by Gustavo Niemeyer
Rather than retrieving all values from the database after an object |
591 |
where = result.get_insert_identity(cls_info.primary_key, |
592 |
primary_vars) |
|
40
by Gustavo Niemeyer
- Implemented result.to_kind(). It'll be used soon to process |
593 |
result = self._connection.execute(Select(missing_columns, where)) |
594 |
self._set_values(obj_info, missing_columns, |
|
50
by Gustavo Niemeyer
- Renamed Result.fetch_one/all to get_one/all. |
595 |
result, result.get_one()) |
40
by Gustavo Niemeyer
- Implemented result.to_kind(). It'll be used soon to process |
596 |
|
196.1.3
by Bernd Dorn
more rename to alive |
597 |
def _validate_alive(self, obj_info): |
190.1.2
by Gustavo Niemeyer
Rather than retrieving all values from the database after an object |
598 |
"""Perform cache validation for the given obj_info."""
|
599 |
where = compare_columns(obj_info.cls_info.primary_key, |
|
600 |
obj_info["primary_vars"]) |
|
601 |
result = self._connection.execute(Select(SQLRaw("1"), where)) |
|
602 |
if not result.get_one(): |
|
603 |
raise LostObjectError("Object is not in the database anymore") |
|
604 |
obj_info.pop("invalidated", None) |
|
85
by Gustavo Niemeyer
- Greatly improved the autoreload feature support. |
605 |
|
112
by Gustavo Niemeyer
Removed support for adaptation. In practice we were not able |
606 |
def _load_objects(self, cls_spec_info, result, values): |
81.1.5
by Gustavo Niemeyer
Implemented support for using a tuple of classes as the first argument of |
607 |
if type(cls_spec_info) is not tuple: |
112
by Gustavo Niemeyer
Removed support for adaptation. In practice we were not able |
608 |
return self._load_object(cls_spec_info, result, values) |
81.1.5
by Gustavo Niemeyer
Implemented support for using a tuple of classes as the first argument of |
609 |
else: |
610 |
objects = [] |
|
611 |
values_start = values_end = 0 |
|
612 |
for cls_info in cls_spec_info: |
|
613 |
values_end += len(cls_info.columns) |
|
614 |
obj = self._load_object(cls_info, result, |
|
112
by Gustavo Niemeyer
Removed support for adaptation. In practice we were not able |
615 |
values[values_start:values_end]) |
81.1.5
by Gustavo Niemeyer
Implemented support for using a tuple of classes as the first argument of |
616 |
objects.append(obj) |
617 |
values_start = values_end |
|
618 |
return tuple(objects) |
|
619 |
||
112
by Gustavo Niemeyer
Removed support for adaptation. In practice we were not able |
620 |
def _load_object(self, cls_info, result, values): |
81.1.12
by Gustavo Niemeyer
- Reimplemented currval() call in the postgres database backend |
621 |
# _set_values() need the cls_info columns for the class of the
|
85
by Gustavo Niemeyer
- Greatly improved the autoreload feature support. |
622 |
# actual object, not from a possible wrapper (e.g. an alias).
|
81.1.12
by Gustavo Niemeyer
- Reimplemented currval() call in the postgres database backend |
623 |
cls = cls_info.cls |
624 |
cls_info = get_cls_info(cls) |
|
95.1.8
by Gustavo Niemeyer
- Event system now has owner as a weak reference, to prevent the |
625 |
|
626 |
# Prepare cache key.
|
|
627 |
primary_vars = [] |
|
628 |
columns = cls_info.columns |
|
629 |
is_null = True |
|
630 |
for i in cls_info.primary_key_pos: |
|
631 |
value = values[i] |
|
632 |
if value is not None: |
|
633 |
is_null = False |
|
106.1.3
by Gustavo Niemeyer
- Refactored common methods of BoundReferenceSet and |
634 |
variable = columns[i].variable_factory(value=value, from_db=True) |
95.1.8
by Gustavo Niemeyer
- Event system now has owner as a weak reference, to prevent the |
635 |
primary_vars.append(variable) |
636 |
||
637 |
if is_null: |
|
638 |
# We've got a row full of NULLs, so consider that the object
|
|
639 |
# wasn't found. This is useful for joins, where unexistent
|
|
640 |
# rows are reprsented like that.
|
|
641 |
return None |
|
642 |
||
643 |
# Lookup cache.
|
|
196.1.2
by Bernd Dorn
ups alive is the right name |
644 |
obj_info = self._alive.get((cls, tuple(primary_vars))) |
95.1.8
by Gustavo Niemeyer
- Event system now has owner as a weak reference, to prevent the |
645 |
|
646 |
if obj_info is not None: |
|
647 |
# Found object in cache, and it must be valid since the
|
|
648 |
# primary key was extracted from result values.
|
|
95.3.71
by Gustavo Niemeyer
As discussed with James Henstrigde, committing and rolling back |
649 |
obj_info.pop("invalidated", None) |
95.1.8
by Gustavo Niemeyer
- Event system now has owner as a weak reference, to prevent the |
650 |
|
142.2.2
by Gustavo Niemeyer
- When an object died in memory, ResultSet.cached() wasn't returning |
651 |
# We're not sure if the obj is still in memory at this
|
652 |
# point. This will rebuild it if needed.
|
|
653 |
obj = self._get_object(obj_info) |
|
95.1.8
by Gustavo Niemeyer
- Event system now has owner as a weak reference, to prevent the |
654 |
else: |
655 |
# Nothing found in the cache. Build everything from the ground.
|
|
81.1.12
by Gustavo Niemeyer
- Reimplemented currval() call in the postgres database backend |
656 |
obj = cls.__new__(cls) |
26
by Gustavo Niemeyer
Major cleanup in the store and improved caching behavior. |
657 |
|
95.1.8
by Gustavo Niemeyer
- Event system now has owner as a weak reference, to prevent the |
658 |
obj_info = get_obj_info(obj) |
659 |
obj_info["store"] = self |
|
660 |
||
661 |
self._set_values(obj_info, cls_info.columns, result, values) |
|
662 |
||
95.3.71
by Gustavo Niemeyer
As discussed with James Henstrigde, committing and rolling back |
663 |
obj_info.checkpoint() |
95.1.8
by Gustavo Niemeyer
- Event system now has owner as a weak reference, to prevent the |
664 |
|
196.1.3
by Bernd Dorn
more rename to alive |
665 |
self._add_to_alive(obj_info) |
95.1.8
by Gustavo Niemeyer
- Event system now has owner as a weak reference, to prevent the |
666 |
self._enable_change_notification(obj_info) |
667 |
self._enable_lazy_resolving(obj_info) |
|
668 |
||
95.6.3
by Gustavo Niemeyer
Renamed all hooks to __storm_<name>__, instead of __<name>__. |
669 |
self._run_hook(obj_info, "__storm_loaded__") |
106.1.3
by Gustavo Niemeyer
- Refactored common methods of BoundReferenceSet and |
670 |
|
671 |
return obj |
|
95.1.8
by Gustavo Niemeyer
- Event system now has owner as a weak reference, to prevent the |
672 |
|
142.2.2
by Gustavo Niemeyer
- When an object died in memory, ResultSet.cached() wasn't returning |
673 |
def _get_object(self, obj_info): |
674 |
"""Return object for obj_info, rebuilding it if it's dead."""
|
|
675 |
obj = obj_info.get_obj() |
|
676 |
if obj is None: |
|
677 |
cls = obj_info.cls_info.cls |
|
678 |
obj = cls.__new__(cls) |
|
679 |
obj_info.set_obj(obj) |
|
680 |
set_obj_info(obj, obj_info) |
|
681 |
self._run_hook(obj_info, "__storm_loaded__") |
|
196.1.6
by Gustavo Niemeyer
These changes address a few issues and perform a few stylistic changes |
682 |
# Renew the cache.
|
683 |
self._cache.add(obj_info) |
|
95.1.8
by Gustavo Niemeyer
- Event system now has owner as a weak reference, to prevent the |
684 |
return obj |
685 |
||
686 |
@staticmethod
|
|
95.6.2
by Gustavo Niemeyer
Implemented __flushed__ hook. |
687 |
def _run_hook(obj_info, hook_name): |
688 |
func = getattr(obj_info.get_obj(), hook_name, None) |
|
95.6.1
by Gustavo Niemeyer
Renamed __load__ signal to __loaded__. |
689 |
if func is not None: |
690 |
func() |
|
29
by Gustavo Niemeyer
Now changes to object attributes done in __load__() persist if the |
691 |
|
40
by Gustavo Niemeyer
- Implemented result.to_kind(). It'll be used soon to process |
692 |
def _set_values(self, obj_info, columns, result, values): |
85
by Gustavo Niemeyer
- Greatly improved the autoreload feature support. |
693 |
if values is None: |
694 |
raise LostObjectError("Can't obtain values from the database " |
|
695 |
"(object got removed?)") |
|
190.1.2
by Gustavo Niemeyer
Rather than retrieving all values from the database after an object |
696 |
obj_info.pop("invalidated", None) |
40
by Gustavo Niemeyer
- Implemented result.to_kind(). It'll be used soon to process |
697 |
for column, value in zip(columns, values): |
44
by Gustavo Niemeyer
- Decoupled most of the logic from Reference into Relation, and |
698 |
if value is None: |
58
by Gustavo Niemeyer
- Major refactoring of the typing system. Kinds are gone. Now Variables |
699 |
obj_info.variables[column].set(value, from_db=True) |
44
by Gustavo Niemeyer
- Decoupled most of the logic from Reference into Relation, and |
700 |
else: |
58
by Gustavo Niemeyer
- Major refactoring of the typing system. Kinds are gone. Now Variables |
701 |
result.set_variable(obj_info.variables[column], value) |
40
by Gustavo Niemeyer
- Implemented result.to_kind(). It'll be used soon to process |
702 |
|
26
by Gustavo Niemeyer
Major cleanup in the store and improved caching behavior. |
703 |
|
62
by Gustavo Niemeyer
Changed the way that objects are keyed. Now they use the obj_info, |
704 |
def _is_dirty(self, obj_info): |
705 |
return obj_info in self._dirty |
|
706 |
||
707 |
def _set_dirty(self, obj_info): |
|
95.1.8
by Gustavo Niemeyer
- Event system now has owner as a weak reference, to prevent the |
708 |
self._dirty[obj_info] = obj_info.get_obj() |
62
by Gustavo Niemeyer
Changed the way that objects are keyed. Now they use the obj_info, |
709 |
|
710 |
def _set_clean(self, obj_info): |
|
95.1.8
by Gustavo Niemeyer
- Event system now has owner as a weak reference, to prevent the |
711 |
self._dirty.pop(obj_info, None) |
26
by Gustavo Niemeyer
Major cleanup in the store and improved caching behavior. |
712 |
|
713 |
def _iter_dirty(self): |
|
62
by Gustavo Niemeyer
Changed the way that objects are keyed. Now they use the obj_info, |
714 |
return self._dirty |
715 |
||
716 |
||
196.1.3
by Bernd Dorn
more rename to alive |
717 |
def _add_to_alive(self, obj_info): |
196.1.7
by Gustavo Niemeyer
Introducing new changes coded with Chris about his review on #172357. |
718 |
"""Add an object to the set of known in-memory objects.
|
719 |
||
720 |
When an object is added to the set of known in-memory objects,
|
|
721 |
the key is built from a copy of the current variables that are
|
|
722 |
part of the primary key. This means that, when an object is
|
|
723 |
retrieved from the database, these values may be used to get
|
|
724 |
the cached object which is already in memory, even if it
|
|
725 |
requested the primary key value to be changed. For that reason,
|
|
726 |
when changes to the primary key are flushed, the alive object
|
|
727 |
key should also be updated to reflect these changes.
|
|
728 |
||
729 |
In addition to tracking objects alive in memory, we have a strong
|
|
730 |
reference cache which keeps a fixed number of last-used objects
|
|
731 |
in-memory, to prevent further database access for recently fetched
|
|
732 |
objects.
|
|
95.3.71
by Gustavo Niemeyer
As discussed with James Henstrigde, committing and rolling back |
733 |
"""
|
62
by Gustavo Niemeyer
Changed the way that objects are keyed. Now they use the obj_info, |
734 |
cls_info = obj_info.cls_info |
58
by Gustavo Niemeyer
- Major refactoring of the typing system. Kinds are gone. Now Variables |
735 |
old_primary_vars = obj_info.get("primary_vars") |
736 |
if old_primary_vars is not None: |
|
196.1.2
by Bernd Dorn
ups alive is the right name |
737 |
self._alive.pop((cls_info.cls, old_primary_vars), None) |
58
by Gustavo Niemeyer
- Major refactoring of the typing system. Kinds are gone. Now Variables |
738 |
new_primary_vars = tuple(variable.copy() |
739 |
for variable in obj_info.primary_vars) |
|
196.1.2
by Bernd Dorn
ups alive is the right name |
740 |
self._alive[cls_info.cls, new_primary_vars] = obj_info |
58
by Gustavo Niemeyer
- Major refactoring of the typing system. Kinds are gone. Now Variables |
741 |
obj_info["primary_vars"] = new_primary_vars |
196.1.4
by Bernd Dorn
implementation of strong reference cache finished |
742 |
self._cache.add(obj_info) |
743 |
||
196.1.3
by Bernd Dorn
more rename to alive |
744 |
def _remove_from_alive(self, obj_info): |
95.3.71
by Gustavo Niemeyer
As discussed with James Henstrigde, committing and rolling back |
745 |
"""Remove an object from the cache.
|
746 |
||
747 |
This method is only called for objects that were explicitly
|
|
748 |
deleted and flushed. Objects that are unused will get removed
|
|
749 |
from the cache dictionary automatically by their weakref callbacks.
|
|
750 |
"""
|
|
58
by Gustavo Niemeyer
- Major refactoring of the typing system. Kinds are gone. Now Variables |
751 |
primary_vars = obj_info.get("primary_vars") |
752 |
if primary_vars is not None: |
|
196.1.5
by Bernd Dorn
added more tests and changed according to review from niemeyer |
753 |
self._cache.remove(obj_info) |
196.1.2
by Bernd Dorn
ups alive is the right name |
754 |
del self._alive[obj_info.cls_info.cls, primary_vars] |
58
by Gustavo Niemeyer
- Major refactoring of the typing system. Kinds are gone. Now Variables |
755 |
del obj_info["primary_vars"] |
26
by Gustavo Niemeyer
Major cleanup in the store and improved caching behavior. |
756 |
|
196.1.3
by Bernd Dorn
more rename to alive |
757 |
def _iter_alive(self): |
196.1.2
by Bernd Dorn
ups alive is the right name |
758 |
return self._alive.values() |
26
by Gustavo Niemeyer
Major cleanup in the store and improved caching behavior. |
759 |
|
760 |
||
62
by Gustavo Niemeyer
Changed the way that objects are keyed. Now they use the obj_info, |
761 |
def _enable_change_notification(self, obj_info): |
762 |
obj_info.event.hook("changed", self._variable_changed) |
|
26
by Gustavo Niemeyer
Major cleanup in the store and improved caching behavior. |
763 |
|
62
by Gustavo Niemeyer
Changed the way that objects are keyed. Now they use the obj_info, |
764 |
def _disable_change_notification(self, obj_info): |
765 |
obj_info.event.unhook("changed", self._variable_changed) |
|
10
by Gustavo Niemeyer
- Renamed ClassInfo.primary_key to pk_prop_insts and introduced |
766 |
|
81.1.20
by Gustavo Niemeyer
- Implemented support for expressions as lazy values in the store! |
767 |
def _variable_changed(self, obj_info, variable, |
768 |
old_value, new_value, fromdb): |
|
769 |
# The fromdb check makes sure that values coming from the
|
|
770 |
# database don't mark the object as dirty again.
|
|
771 |
# XXX The fromdb check is untested. How to test it?
|
|
123
by Gustavo Niemeyer
Storm will now raise a LostObjectError if an invalidated and |
772 |
if not fromdb: |
773 |
if new_value is not Undef and new_value is not AutoReload: |
|
774 |
if obj_info.get("invalidated"): |
|
196.1.3
by Bernd Dorn
more rename to alive |
775 |
# This might be a previously alive object being
|
123
by Gustavo Niemeyer
Storm will now raise a LostObjectError if an invalidated and |
776 |
# updated. Let's validate it now to improve debugging.
|
777 |
# This will raise LostObjectError if the object is gone.
|
|
196.1.3
by Bernd Dorn
more rename to alive |
778 |
self._validate_alive(obj_info) |
123
by Gustavo Niemeyer
Storm will now raise a LostObjectError if an invalidated and |
779 |
self._set_dirty(obj_info) |
10
by Gustavo Niemeyer
- Renamed ClassInfo.primary_key to pk_prop_insts and introduced |
780 |
|
7
by Gustavo Niemeyer
- Started implementing Store. |
781 |
|
81.1.20
by Gustavo Niemeyer
- Implemented support for expressions as lazy values in the store! |
782 |
def _enable_lazy_resolving(self, obj_info): |
783 |
obj_info.event.hook("resolve-lazy-value", self._resolve_lazy_value) |
|
784 |
||
785 |
def _disable_lazy_resolving(self, obj_info): |
|
786 |
obj_info.event.unhook("resolve-lazy-value", self._resolve_lazy_value) |
|
787 |
||
788 |
def _resolve_lazy_value(self, obj_info, variable, lazy_value): |
|
190.1.4
by Gustavo Niemeyer
Applying reviews from Jamu and Chris. |
789 |
"""Resolve a variable set to a lazy value when it's touched.
|
790 |
||
791 |
This method is hooked into the obj_info to resolve variables
|
|
792 |
set to lazy values when they're accessed. It will first flush
|
|
793 |
the store, and then set all variables set to AutoReload to
|
|
794 |
their database values.
|
|
795 |
"""
|
|
190.1.2
by Gustavo Niemeyer
Rather than retrieving all values from the database after an object |
796 |
# XXX This will do it for now, but it should really flush
|
797 |
# just this single object and ones that it depends on.
|
|
798 |
# _flush_one() doesn't consider dependencies, so it may
|
|
799 |
# not be used directly. Maybe allow flush(obj)?
|
|
800 |
self.flush() |
|
801 |
||
802 |
autoreload_columns = [] |
|
803 |
for column in obj_info.cls_info.columns: |
|
804 |
if obj_info.variables[column].get_lazy() is AutoReload: |
|
805 |
autoreload_columns.append(column) |
|
806 |
||
807 |
if autoreload_columns: |
|
808 |
where = compare_columns(obj_info.cls_info.primary_key, |
|
809 |
obj_info["primary_vars"]) |
|
810 |
result = self._connection.execute(Select(autoreload_columns, where)) |
|
811 |
self._set_values(obj_info, autoreload_columns, |
|
812 |
result, result.get_one()) |
|
813 |
for column in autoreload_columns: |
|
814 |
obj_info.variables[column].checkpoint() |
|
81.1.20
by Gustavo Niemeyer
- Implemented support for expressions as lazy values in the store! |
815 |
|
816 |
||
9
by Gustavo Niemeyer
- Merged Connection.execute_expr() into Connection.execute(). |
817 |
class ResultSet(object): |
142.3.20
by Christopher Armstrong
massive doc improvements for store.py |
818 |
"""The representation of the results of a query.
|
819 |
||
820 |
Note that having an instance of this class does not indicate that
|
|
821 |
a database query has necessarily been made. Database queries are
|
|
822 |
put off until absolutely necessary.
|
|
823 |
||
824 |
Generally these should not be constructed directly, but instead
|
|
825 |
retrieved from calls to L{Store.find}.
|
|
826 |
"""
|
|
95.3.20
by Gustavo Niemeyer
Initial implementation of ResultSet.union(). order_by() doesn't yet |
827 |
def __init__(self, store, cls_spec_info, |
828 |
where=Undef, tables=Undef, select=Undef): |
|
44
by Gustavo Niemeyer
- Decoupled most of the logic from Reference into Relation, and |
829 |
self._store = store |
81.1.5
by Gustavo Niemeyer
Implemented support for using a tuple of classes as the first argument of |
830 |
self._cls_spec_info = cls_spec_info |
24
by Gustavo Niemeyer
- Removed AutoTable. It's now implemented internally in statements. |
831 |
self._where = where |
81.1.3
by Gustavo Niemeyer
- Redesigned join expressions to handle more complex cases. |
832 |
self._tables = tables |
95.3.20
by Gustavo Niemeyer
Initial implementation of ResultSet.union(). order_by() doesn't yet |
833 |
self._select = select |
95.3.4
by Gustavo Niemeyer
Forgot to commit changes of Store to support default ordering. |
834 |
self._order_by = getattr(cls_spec_info, "default_order", Undef) |
81.1.6
by Gustavo Niemeyer
- ResultSet is now mutable. Running order_by() will change the instance |
835 |
self._offset = Undef |
836 |
self._limit = Undef |
|
837 |
self._distinct = False |
|
838 |
||
839 |
def copy(self): |
|
142.3.20
by Christopher Armstrong
massive doc improvements for store.py |
840 |
"""Return a copy of this ResultSet object, with the same configuration.
|
106.1.3
by Gustavo Niemeyer
- Refactored common methods of BoundReferenceSet and |
841 |
"""
|
81.1.6
by Gustavo Niemeyer
- ResultSet is now mutable. Running order_by() will change the instance |
842 |
result_set = object.__new__(self.__class__) |
843 |
result_set.__dict__.update(self.__dict__) |
|
844 |
return result_set |
|
845 |
||
112
by Gustavo Niemeyer
Removed support for adaptation. In practice we were not able |
846 |
def config(self, distinct=None, offset=None, limit=None): |
106.1.3
by Gustavo Niemeyer
- Refactored common methods of BoundReferenceSet and |
847 |
"""Configure this result object in-place. All parameters are optional.
|
848 |
||
849 |
@param distinct: Boolean enabling/disabling usage of the DISTINCT
|
|
850 |
keyword in the query made.
|
|
851 |
@param offset: Offset where results will start to be retrieved
|
|
852 |
from the result set.
|
|
853 |
@param limit: Limit the number of objects retrieved from the
|
|
854 |
result set.
|
|
855 |
||
856 |
@return: self (not a copy).
|
|
857 |
"""
|
|
858 |
||
81.1.6
by Gustavo Niemeyer
- ResultSet is now mutable. Running order_by() will change the instance |
859 |
if distinct is not None: |
860 |
self._distinct = distinct |
|
861 |
if offset is not None: |
|
862 |
self._offset = offset |
|
863 |
if limit is not None: |
|
864 |
self._limit = limit |
|
865 |
return self |
|
866 |
||
95.3.20
by Gustavo Niemeyer
Initial implementation of ResultSet.union(). order_by() doesn't yet |
867 |
def _get_select(self): |
868 |
if self._select is not Undef: |
|
95.3.32
by Gustavo Niemeyer
- Improved a bit support for unions. |
869 |
if self._order_by is not Undef: |
870 |
self._select.order_by = self._order_by |
|
871 |
if self._limit is not Undef: # XXX UNTESTED! |
|
872 |
self._select.limit = self._limit |
|
873 |
if self._offset is not Undef: # XXX UNTESTED! |
|
874 |
self._select.offset = self._offset |
|
95.3.20
by Gustavo Niemeyer
Initial implementation of ResultSet.union(). order_by() doesn't yet |
875 |
return self._select |
81.1.5
by Gustavo Niemeyer
Implemented support for using a tuple of classes as the first argument of |
876 |
if type(self._cls_spec_info) is tuple: |
877 |
columns = [] |
|
878 |
default_tables = [] |
|
879 |
for cls_info in self._cls_spec_info: |
|
880 |
columns.append(cls_info.columns) |
|
881 |
default_tables.append(cls_info.table) |
|
882 |
else: |
|
883 |
columns = self._cls_spec_info.columns |
|
884 |
default_tables = self._cls_spec_info.table |
|
885 |
return Select(columns, self._where, self._tables, default_tables, |
|
81.1.6
by Gustavo Niemeyer
- ResultSet is now mutable. Running order_by() will change the instance |
886 |
self._order_by, offset=self._offset, limit=self._limit, |
887 |
distinct=self._distinct) |
|
81.1.5
by Gustavo Niemeyer
Implemented support for using a tuple of classes as the first argument of |
888 |
|
106.1.3
by Gustavo Niemeyer
- Refactored common methods of BoundReferenceSet and |
889 |
def _load_objects(self, result, values): |
112
by Gustavo Niemeyer
Removed support for adaptation. In practice we were not able |
890 |
return self._store._load_objects(self._cls_spec_info, result, values) |
106.1.3
by Gustavo Niemeyer
- Refactored common methods of BoundReferenceSet and |
891 |
|
7
by Gustavo Niemeyer
- Started implementing Store. |
892 |
def __iter__(self): |
142.3.20
by Christopher Armstrong
massive doc improvements for store.py |
893 |
"""Iterate the results of the query.
|
894 |
"""
|
|
81.1.5
by Gustavo Niemeyer
Implemented support for using a tuple of classes as the first argument of |
895 |
result = self._store._connection.execute(self._get_select()) |
40
by Gustavo Niemeyer
- Implemented result.to_kind(). It'll be used soon to process |
896 |
for values in result: |
106.1.3
by Gustavo Niemeyer
- Refactored common methods of BoundReferenceSet and |
897 |
yield self._load_objects(result, values) |
7
by Gustavo Niemeyer
- Started implementing Store. |
898 |
|
71
by Gustavo Niemeyer
Implemented support for slicing sliced result sets. |
899 |
def __getitem__(self, index): |
142.3.20
by Christopher Armstrong
massive doc improvements for store.py |
900 |
"""Get an individual item by offset, or a range of items by slice.
|
901 |
||
902 |
If a slice is used, a new L{ResultSet} will be return
|
|
903 |
appropriately modified with OFFSET and LIMIT clauses.
|
|
904 |
"""
|
|
72
by Gustavo Niemeyer
- Implemented support for non-slice ResultSet indexing. |
905 |
if isinstance(index, (int, long)): |
906 |
if index == 0: |
|
907 |
result_set = self |
|
908 |
else: |
|
909 |
if self._offset is not Undef: |
|
910 |
index += self._offset |
|
81.1.6
by Gustavo Niemeyer
- ResultSet is now mutable. Running order_by() will change the instance |
911 |
result_set = self.copy() |
912 |
result_set.config(offset=index, limit=1) |
|
72
by Gustavo Niemeyer
- Implemented support for non-slice ResultSet indexing. |
913 |
obj = result_set.any() |
914 |
if obj is None: |
|
915 |
raise IndexError("Index out of range") |
|
916 |
return obj |
|
917 |
||
71
by Gustavo Niemeyer
Implemented support for slicing sliced result sets. |
918 |
if not isinstance(index, slice): |
81.1.9
by Gustavo Niemeyer
- Renamed UnsupportedDatabaseError to DatabaseModuleError. |
919 |
raise IndexError("Can't index ResultSets with %r" % (index,)) |
71
by Gustavo Niemeyer
Implemented support for slicing sliced result sets. |
920 |
if index.step is not None: |
81.1.9
by Gustavo Niemeyer
- Renamed UnsupportedDatabaseError to DatabaseModuleError. |
921 |
raise IndexError("Stepped slices not yet supported: %r" |
71
by Gustavo Niemeyer
Implemented support for slicing sliced result sets. |
922 |
% (index.step,)) |
72
by Gustavo Niemeyer
- Implemented support for non-slice ResultSet indexing. |
923 |
|
71
by Gustavo Niemeyer
Implemented support for slicing sliced result sets. |
924 |
offset = self._offset |
925 |
limit = self._limit |
|
72
by Gustavo Niemeyer
- Implemented support for non-slice ResultSet indexing. |
926 |
|
71
by Gustavo Niemeyer
Implemented support for slicing sliced result sets. |
927 |
if index.start is not None: |
928 |
if offset is Undef: |
|
929 |
offset = index.start |
|
930 |
else: |
|
931 |
offset += index.start |
|
932 |
if limit is not Undef: |
|
933 |
limit = max(0, limit - index.start) |
|
72
by Gustavo Niemeyer
- Implemented support for non-slice ResultSet indexing. |
934 |
|
71
by Gustavo Niemeyer
Implemented support for slicing sliced result sets. |
935 |
if index.stop is not None: |
936 |
if index.start is None: |
|
937 |
new_limit = index.stop |
|
938 |
else: |
|
939 |
new_limit = index.stop - index.start |
|
940 |
if limit is Undef or limit > new_limit: |
|
941 |
limit = new_limit |
|
72
by Gustavo Niemeyer
- Implemented support for non-slice ResultSet indexing. |
942 |
|
81.1.6
by Gustavo Niemeyer
- ResultSet is now mutable. Running order_by() will change the instance |
943 |
return self.copy().config(offset=offset, limit=limit) |
81.1.5
by Gustavo Niemeyer
Implemented support for using a tuple of classes as the first argument of |
944 |
|
68
by Gustavo Niemeyer
Added meaningful implementations of ResultSet.{any,one,first,last}(). |
945 |
def any(self): |
946 |
"""Return a single item from the result set.
|
|
947 |
||
948 |
See also one(), first(), and last().
|
|
949 |
"""
|
|
81.1.5
by Gustavo Niemeyer
Implemented support for using a tuple of classes as the first argument of |
950 |
select = self._get_select() |
951 |
select.limit = 1 |
|
68
by Gustavo Niemeyer
Added meaningful implementations of ResultSet.{any,one,first,last}(). |
952 |
result = self._store._connection.execute(select) |
953 |
values = result.get_one() |
|
954 |
if values: |
|
106.1.3
by Gustavo Niemeyer
- Refactored common methods of BoundReferenceSet and |
955 |
return self._load_objects(result, values) |
68
by Gustavo Niemeyer
Added meaningful implementations of ResultSet.{any,one,first,last}(). |
956 |
return None |
957 |
||
958 |
def first(self): |
|
959 |
"""Return the first item from an ordered result set.
|
|
960 |
||
961 |
Will raise UnorderedError if the result set isn't ordered.
|
|
962 |
||
963 |
See also last(), one(), and any().
|
|
964 |
"""
|
|
965 |
if self._order_by is Undef: |
|
966 |
raise UnorderedError("Can't use first() on unordered result set") |
|
967 |
return self.any() |
|
968 |
||
969 |
def last(self): |
|
970 |
"""Return the last item from an ordered result set.
|
|
971 |
||
972 |
Will raise UnorderedError if the result set isn't ordered.
|
|
973 |
||
974 |
See also first(), one(), and any().
|
|
975 |
"""
|
|
976 |
if self._order_by is Undef: |
|
977 |
raise UnorderedError("Can't use last() on unordered result set") |
|
72
by Gustavo Niemeyer
- Implemented support for non-slice ResultSet indexing. |
978 |
if self._limit is not Undef: |
81.1.9
by Gustavo Niemeyer
- Renamed UnsupportedDatabaseError to DatabaseModuleError. |
979 |
raise FeatureError("Can't use last() with a slice " |
980 |
"of defined stop index") |
|
81.1.5
by Gustavo Niemeyer
Implemented support for using a tuple of classes as the first argument of |
981 |
select = self._get_select() |
982 |
select.offset = Undef |
|
983 |
select.limit = 1 |
|
984 |
select.order_by = [] |
|
68
by Gustavo Niemeyer
Added meaningful implementations of ResultSet.{any,one,first,last}(). |
985 |
for expr in self._order_by: |
986 |
if isinstance(expr, Desc): |
|
81.1.5
by Gustavo Niemeyer
Implemented support for using a tuple of classes as the first argument of |
987 |
select.order_by.append(expr.expr) |
68
by Gustavo Niemeyer
Added meaningful implementations of ResultSet.{any,one,first,last}(). |
988 |
elif isinstance(expr, Asc): |
81.1.5
by Gustavo Niemeyer
Implemented support for using a tuple of classes as the first argument of |
989 |
select.order_by.append(Desc(expr.expr)) |
68
by Gustavo Niemeyer
Added meaningful implementations of ResultSet.{any,one,first,last}(). |
990 |
else: |
81.1.5
by Gustavo Niemeyer
Implemented support for using a tuple of classes as the first argument of |
991 |
select.order_by.append(Desc(expr)) |
68
by Gustavo Niemeyer
Added meaningful implementations of ResultSet.{any,one,first,last}(). |
992 |
result = self._store._connection.execute(select) |
993 |
values = result.get_one() |
|
994 |
if values: |
|
106.1.3
by Gustavo Niemeyer
- Refactored common methods of BoundReferenceSet and |
995 |
return self._load_objects(result, values) |
68
by Gustavo Niemeyer
Added meaningful implementations of ResultSet.{any,one,first,last}(). |
996 |
return None |
997 |
||
7
by Gustavo Niemeyer
- Started implementing Store. |
998 |
def one(self): |
68
by Gustavo Niemeyer
Added meaningful implementations of ResultSet.{any,one,first,last}(). |
999 |
"""Return one item from a result set containing at most one item.
|
1000 |
||
1001 |
Will raise NotOneError if the result set contains more than one item.
|
|
1002 |
||
1003 |
See also first(), one(), and any().
|
|
1004 |
"""
|
|
81.1.5
by Gustavo Niemeyer
Implemented support for using a tuple of classes as the first argument of |
1005 |
select = self._get_select() |
1006 |
# limit could be 1 due to slicing, for instance.
|
|
1007 |
if select.limit is not Undef and select.limit > 2: |
|
1008 |
select.limit = 2 |
|
44
by Gustavo Niemeyer
- Decoupled most of the logic from Reference into Relation, and |
1009 |
result = self._store._connection.execute(select) |
50
by Gustavo Niemeyer
- Renamed Result.fetch_one/all to get_one/all. |
1010 |
values = result.get_one() |
68
by Gustavo Niemeyer
Added meaningful implementations of ResultSet.{any,one,first,last}(). |
1011 |
if result.get_one(): |
1012 |
raise NotOneError("one() used with more than one result available") |
|
10
by Gustavo Niemeyer
- Renamed ClassInfo.primary_key to pk_prop_insts and introduced |
1013 |
if values: |
106.1.3
by Gustavo Niemeyer
- Refactored common methods of BoundReferenceSet and |
1014 |
return self._load_objects(result, values) |
10
by Gustavo Niemeyer
- Renamed ClassInfo.primary_key to pk_prop_insts and introduced |
1015 |
return None |
7
by Gustavo Niemeyer
- Started implementing Store. |
1016 |
|
1017 |
def order_by(self, *args): |
|
142.3.20
by Christopher Armstrong
massive doc improvements for store.py |
1018 |
"""Specify the ordering of the results.
|
1019 |
||
1020 |
The query will be modified appropriately with an ORDER BY clause.
|
|
1021 |
||
142.3.22
by Christopher Armstrong
mention Asc and Desc in the order_by docstring |
1022 |
Ascending and descending order can be specified by wrapping
|
1023 |
the columns in L{Asc} and L{Desc}.
|
|
1024 |
||
142.3.20
by Christopher Armstrong
massive doc improvements for store.py |
1025 |
@param args: One or more L{storm.expr.Column} objects.
|
1026 |
"""
|
|
72
by Gustavo Niemeyer
- Implemented support for non-slice ResultSet indexing. |
1027 |
if self._offset is not Undef or self._limit is not Undef: |
81.1.9
by Gustavo Niemeyer
- Renamed UnsupportedDatabaseError to DatabaseModuleError. |
1028 |
raise FeatureError("Can't reorder a sliced result set") |
95.3.70
by Gustavo Niemeyer
- Fixed bug in postgres compilation of set expressions, reported by Kiko. |
1029 |
self._order_by = args or Undef |
81.1.6
by Gustavo Niemeyer
- ResultSet is now mutable. Running order_by() will change the instance |
1030 |
return self |
9
by Gustavo Niemeyer
- Merged Connection.execute_expr() into Connection.execute(). |
1031 |
|
1032 |
def remove(self): |
|
142.3.20
by Christopher Armstrong
massive doc improvements for store.py |
1033 |
"""Remove all rows represented by this ResultSet from the database.
|
1034 |
||
1035 |
This is done efficiently with a DELETE statement, so objects
|
|
1036 |
are not actually loaded into Python.
|
|
1037 |
"""
|
|
72
by Gustavo Niemeyer
- Implemented support for non-slice ResultSet indexing. |
1038 |
if self._offset is not Undef or self._limit is not Undef: |
81.1.9
by Gustavo Niemeyer
- Renamed UnsupportedDatabaseError to DatabaseModuleError. |
1039 |
raise FeatureError("Can't remove a sliced result set") |
81.1.6
by Gustavo Niemeyer
- ResultSet is now mutable. Running order_by() will change the instance |
1040 |
if type(self._cls_spec_info) is tuple: |
81.1.9
by Gustavo Niemeyer
- Renamed UnsupportedDatabaseError to DatabaseModuleError. |
1041 |
raise FeatureError("Removing not yet supported with tuple finds") |
95.3.20
by Gustavo Niemeyer
Initial implementation of ResultSet.union(). order_by() doesn't yet |
1042 |
if self._select is not Undef: |
95.3.75
by Gustavo Niemeyer
Fixing typos, as reported by Jamu. |
1043 |
raise FeatureError("Removing isn't supported with " |
95.3.20
by Gustavo Niemeyer
Initial implementation of ResultSet.union(). order_by() doesn't yet |
1044 |
"set expressions (unions, etc)") |
44
by Gustavo Niemeyer
- Decoupled most of the logic from Reference into Relation, and |
1045 |
self._store._connection.execute(Delete(self._where, |
81.1.5
by Gustavo Niemeyer
Implemented support for using a tuple of classes as the first argument of |
1046 |
self._cls_spec_info.table), |
44
by Gustavo Niemeyer
- Decoupled most of the logic from Reference into Relation, and |
1047 |
noresult=True) |
7
by Gustavo Niemeyer
- Started implementing Store. |
1048 |
|
73
by Gustavo Niemeyer
Now aggregating functions (max(), min(), etc) will pass through the |
1049 |
def _aggregate(self, expr, column=None): |
81.1.6
by Gustavo Niemeyer
- ResultSet is now mutable. Running order_by() will change the instance |
1050 |
if type(self._cls_spec_info) is tuple: |
1051 |
default_tables = [cls_info.table |
|
1052 |
for cls_info in self._cls_spec_info] |
|
1053 |
else: |
|
1054 |
default_tables = self._cls_spec_info.table |
|
95.3.59
by Gustavo Niemeyer
Fixed behavior of ResultSet.count() with set expressions, |
1055 |
if self._select is Undef: |
1056 |
select = Select(expr, self._where, self._tables, default_tables) |
|
1057 |
else: |
|
1058 |
select = Select(expr, tables=Alias(self._select)) |
|
73
by Gustavo Niemeyer
Now aggregating functions (max(), min(), etc) will pass through the |
1059 |
result = self._store._connection.execute(select) |
1060 |
value = result.get_one()[0] |
|
163
by Gustavo Niemeyer
Merged patch from Johan Dahlin [r=jkakar,niemeyer] [f=126001] |
1061 |
variable_factory = getattr(column, "variable_factory", None) |
1062 |
if variable_factory: |
|
1063 |
variable = variable_factory() |
|
73
by Gustavo Niemeyer
Now aggregating functions (max(), min(), etc) will pass through the |
1064 |
result.set_variable(variable, value) |
1065 |
return variable.get() |
|
163
by Gustavo Niemeyer
Merged patch from Johan Dahlin [r=jkakar,niemeyer] [f=126001] |
1066 |
return value |
1067 |
||
1068 |
def count(self, expr=Undef, distinct=False): |
|
142.3.20
by Christopher Armstrong
massive doc improvements for store.py |
1069 |
"""Get the number of objects represented by this ResultSet."""
|
163
by Gustavo Niemeyer
Merged patch from Johan Dahlin [r=jkakar,niemeyer] [f=126001] |
1070 |
return int(self._aggregate(Count(expr, distinct))) |
1071 |
||
1072 |
def max(self, expr): |
|
142.3.20
by Christopher Armstrong
massive doc improvements for store.py |
1073 |
"""Get the highest value from an expression."""
|
163
by Gustavo Niemeyer
Merged patch from Johan Dahlin [r=jkakar,niemeyer] [f=126001] |
1074 |
return self._aggregate(Max(expr), expr) |
1075 |
||
1076 |
def min(self, expr): |
|
142.3.20
by Christopher Armstrong
massive doc improvements for store.py |
1077 |
"""Get the lowest value from an expression."""
|
163
by Gustavo Niemeyer
Merged patch from Johan Dahlin [r=jkakar,niemeyer] [f=126001] |
1078 |
return self._aggregate(Min(expr), expr) |
1079 |
||
1080 |
def avg(self, expr): |
|
142.3.20
by Christopher Armstrong
massive doc improvements for store.py |
1081 |
"""Get the average value from an expression."""
|
163
by Gustavo Niemeyer
Merged patch from Johan Dahlin [r=jkakar,niemeyer] [f=126001] |
1082 |
value = self._aggregate(Avg(expr)) |
92.1.2
by Jamshed Kakar
- Fixed float conversion bug in ResultSet.avg(). Updated |
1083 |
if value is None: |
1084 |
return value |
|
1085 |
return float(value) |
|
73
by Gustavo Niemeyer
Now aggregating functions (max(), min(), etc) will pass through the |
1086 |
|
163
by Gustavo Niemeyer
Merged patch from Johan Dahlin [r=jkakar,niemeyer] [f=126001] |
1087 |
def sum(self, expr): |
142.3.20
by Christopher Armstrong
massive doc improvements for store.py |
1088 |
"""Get the sum of all values in an expression."""
|
163
by Gustavo Niemeyer
Merged patch from Johan Dahlin [r=jkakar,niemeyer] [f=126001] |
1089 |
return self._aggregate(Sum(expr), expr) |
44
by Gustavo Niemeyer
- Decoupled most of the logic from Reference into Relation, and |
1090 |
|
75
by Gustavo Niemeyer
Implemented support for multiple arguments in ResultSet.values(). |
1091 |
def values(self, *columns): |
142.3.20
by Christopher Armstrong
massive doc improvements for store.py |
1092 |
"""Retrieve only the specified columns.
|
1093 |
||
1094 |
This does not load full objects from the database into Python.
|
|
1095 |
||
1096 |
@param columns: One or more L{storm.expr.Column} objects whose
|
|
1097 |
values will be fetched.
|
|
1098 |
@return: An iterator of tuples of the values for each column
|
|
1099 |
from each matching row in the database.
|
|
1100 |
"""
|
|
75
by Gustavo Niemeyer
Implemented support for multiple arguments in ResultSet.values(). |
1101 |
if not columns: |
81.1.9
by Gustavo Niemeyer
- Renamed UnsupportedDatabaseError to DatabaseModuleError. |
1102 |
raise FeatureError("values() takes at least one column " |
1103 |
"as argument") |
|
81.1.5
by Gustavo Niemeyer
Implemented support for using a tuple of classes as the first argument of |
1104 |
select = self._get_select() |
1105 |
select.columns = columns |
|
74
by Gustavo Niemeyer
Implemented ResultSet.values(). |
1106 |
result = self._store._connection.execute(select) |
75
by Gustavo Niemeyer
Implemented support for multiple arguments in ResultSet.values(). |
1107 |
if len(columns) == 1: |
1108 |
variable = columns[0].variable_factory() |
|
1109 |
for values in result: |
|
1110 |
result.set_variable(variable, values[0]) |
|
1111 |
yield variable.get() |
|
1112 |
else: |
|
1113 |
variables = [column.variable_factory() for column in columns] |
|
1114 |
for values in result: |
|
1115 |
for variable, value in zip(variables, values): |
|
1116 |
result.set_variable(variable, value) |
|
1117 |
yield tuple(variable.get() for variable in variables) |
|
74
by Gustavo Niemeyer
Implemented ResultSet.values(). |
1118 |
|
44
by Gustavo Niemeyer
- Decoupled most of the logic from Reference into Relation, and |
1119 |
def set(self, *args, **kwargs): |
99.1.1
by Gustavo Niemeyer
- Now ResultSet.set() will set only the involved attributes to |
1120 |
"""Update objects in the result set with the given arguments.
|
1121 |
||
1122 |
This method will update all objects in the current result set
|
|
1123 |
to match expressions given as equalities or keyword arguments.
|
|
1124 |
These objects may still be in the database (an UPDATE is issued)
|
|
1125 |
or may be cached.
|
|
1126 |
||
1127 |
For instance, C{result.set(Class.attr1 == 1, attr2=2)} will set
|
|
1128 |
C{attr1} to 1 and C{attr2} to 2, on all matching objects.
|
|
1129 |
"""
|
|
1130 |
||
81.1.6
by Gustavo Niemeyer
- ResultSet is now mutable. Running order_by() will change the instance |
1131 |
if type(self._cls_spec_info) is tuple: |
95.3.75
by Gustavo Niemeyer
Fixing typos, as reported by Jamu. |
1132 |
raise FeatureError("Setting isn't supported with tuple finds") |
95.3.20
by Gustavo Niemeyer
Initial implementation of ResultSet.union(). order_by() doesn't yet |
1133 |
if self._select is not Undef: |
95.3.75
by Gustavo Niemeyer
Fixing typos, as reported by Jamu. |
1134 |
raise FeatureError("Setting isn't supported with " |
95.3.20
by Gustavo Niemeyer
Initial implementation of ResultSet.union(). order_by() doesn't yet |
1135 |
"set expressions (unions, etc)") |
81.1.6
by Gustavo Niemeyer
- ResultSet is now mutable. Running order_by() will change the instance |
1136 |
|
44
by Gustavo Niemeyer
- Decoupled most of the logic from Reference into Relation, and |
1137 |
if not (args or kwargs): |
1138 |
return
|
|
1139 |
||
58
by Gustavo Niemeyer
- Major refactoring of the typing system. Kinds are gone. Now Variables |
1140 |
changes = {} |
81.1.5
by Gustavo Niemeyer
Implemented support for using a tuple of classes as the first argument of |
1141 |
cls = self._cls_spec_info.cls |
44
by Gustavo Niemeyer
- Decoupled most of the logic from Reference into Relation, and |
1142 |
|
99.1.1
by Gustavo Niemeyer
- Now ResultSet.set() will set only the involved attributes to |
1143 |
# For now only "Class.attr == var" is supported in args.
|
44
by Gustavo Niemeyer
- Decoupled most of the logic from Reference into Relation, and |
1144 |
for expr in args: |
1145 |
if (not isinstance(expr, Eq) or |
|
1146 |
not isinstance(expr.expr1, Column) or |
|
58
by Gustavo Niemeyer
- Major refactoring of the typing system. Kinds are gone. Now Variables |
1147 |
not isinstance(expr.expr2, (Column, Variable))): |
81.1.9
by Gustavo Niemeyer
- Renamed UnsupportedDatabaseError to DatabaseModuleError. |
1148 |
raise FeatureError("Unsupported set expression: %r" % |
1149 |
repr(expr)) |
|
58
by Gustavo Niemeyer
- Major refactoring of the typing system. Kinds are gone. Now Variables |
1150 |
changes[expr.expr1] = expr.expr2 |
44
by Gustavo Niemeyer
- Decoupled most of the logic from Reference into Relation, and |
1151 |
|
1152 |
for key, value in kwargs.items(): |
|
1153 |
column = getattr(cls, key) |
|
1154 |
if value is None: |
|
58
by Gustavo Niemeyer
- Major refactoring of the typing system. Kinds are gone. Now Variables |
1155 |
changes[column] = None |
44
by Gustavo Niemeyer
- Decoupled most of the logic from Reference into Relation, and |
1156 |
elif isinstance(value, Expr): |
1157 |
if not isinstance(value, Column): |
|
81.1.9
by Gustavo Niemeyer
- Renamed UnsupportedDatabaseError to DatabaseModuleError. |
1158 |
raise FeatureError("Unsupported set expression: %r" % |
1159 |
repr(value)) |
|
58
by Gustavo Niemeyer
- Major refactoring of the typing system. Kinds are gone. Now Variables |
1160 |
changes[column] = value |
44
by Gustavo Niemeyer
- Decoupled most of the logic from Reference into Relation, and |
1161 |
else: |
58
by Gustavo Niemeyer
- Major refactoring of the typing system. Kinds are gone. Now Variables |
1162 |
changes[column] = column.variable_factory(value=value) |
44
by Gustavo Niemeyer
- Decoupled most of the logic from Reference into Relation, and |
1163 |
|
81.1.5
by Gustavo Niemeyer
Implemented support for using a tuple of classes as the first argument of |
1164 |
expr = Update(changes, self._where, self._cls_spec_info.table) |
142.2.2
by Gustavo Niemeyer
- When an object died in memory, ResultSet.cached() wasn't returning |
1165 |
self._store.execute(expr, noresult=True) |
44
by Gustavo Niemeyer
- Decoupled most of the logic from Reference into Relation, and |
1166 |
|
1167 |
try: |
|
196.1.5
by Bernd Dorn
added more tests and changed according to review from niemeyer |
1168 |
cached = self.cached() |
44
by Gustavo Niemeyer
- Decoupled most of the logic from Reference into Relation, and |
1169 |
except CompileError: |
196.1.3
by Bernd Dorn
more rename to alive |
1170 |
for obj_info in self._store._iter_alive(): |
99.1.1
by Gustavo Niemeyer
- Now ResultSet.set() will set only the involved attributes to |
1171 |
for column in changes: |
1172 |
obj_info.variables[column].set(AutoReload) |
|
44
by Gustavo Niemeyer
- Decoupled most of the logic from Reference into Relation, and |
1173 |
else: |
58
by Gustavo Niemeyer
- Major refactoring of the typing system. Kinds are gone. Now Variables |
1174 |
changes = changes.items() |
44
by Gustavo Niemeyer
- Decoupled most of the logic from Reference into Relation, and |
1175 |
for obj in cached: |
58
by Gustavo Niemeyer
- Major refactoring of the typing system. Kinds are gone. Now Variables |
1176 |
for column, value in changes: |
1177 |
variables = get_obj_info(obj).variables |
|
1178 |
if value is None: |
|
1179 |
pass
|
|
1180 |
elif isinstance(value, Variable): |
|
1181 |
value = value.get() |
|
44
by Gustavo Niemeyer
- Decoupled most of the logic from Reference into Relation, and |
1182 |
else: |
58
by Gustavo Niemeyer
- Major refactoring of the typing system. Kinds are gone. Now Variables |
1183 |
value = variables[value].get() |
1184 |
variables[column].set(value) |
|
1185 |
variables[column].checkpoint() |
|
44
by Gustavo Niemeyer
- Decoupled most of the logic from Reference into Relation, and |
1186 |
|
196.1.5
by Bernd Dorn
added more tests and changed according to review from niemeyer |
1187 |
def cached(self): |
196.1.6
by Gustavo Niemeyer
These changes address a few issues and perform a few stylistic changes |
1188 |
"""Return matching objects from the cache for the current query."""
|
81.1.6
by Gustavo Niemeyer
- ResultSet is now mutable. Running order_by() will change the instance |
1189 |
if type(self._cls_spec_info) is tuple: |
196.1.6
by Gustavo Niemeyer
These changes address a few issues and perform a few stylistic changes |
1190 |
raise FeatureError("Cache finds not supported with tuples") |
81.1.8
by Gustavo Niemeyer
Fix check in ResultSet.cached(). |
1191 |
if self._tables is not Undef: |
196.1.6
by Gustavo Niemeyer
These changes address a few issues and perform a few stylistic changes |
1192 |
raise FeatureError("Cache finds not supported with custom tables") |
44
by Gustavo Niemeyer
- Decoupled most of the logic from Reference into Relation, and |
1193 |
if self._where is Undef: |
1194 |
match = None |
|
1195 |
else: |
|
142.1.7
by Gustavo Niemeyer
Sanitized return values from Compile. Now it always return only |
1196 |
match = compile_python.get_matcher(self._where) |
81.1.5
by Gustavo Niemeyer
Implemented support for using a tuple of classes as the first argument of |
1197 |
name_to_column = dict((column.name, column) |
1198 |
for column in self._cls_spec_info.columns) |
|
44
by Gustavo Niemeyer
- Decoupled most of the logic from Reference into Relation, and |
1199 |
def get_column(name, name_to_column=name_to_column): |
62
by Gustavo Niemeyer
Changed the way that objects are keyed. Now they use the obj_info, |
1200 |
return obj_info.variables[name_to_column[name]].get() |
44
by Gustavo Niemeyer
- Decoupled most of the logic from Reference into Relation, and |
1201 |
objects = [] |
81.1.5
by Gustavo Niemeyer
Implemented support for using a tuple of classes as the first argument of |
1202 |
cls = self._cls_spec_info.cls |
196.1.3
by Bernd Dorn
more rename to alive |
1203 |
for obj_info in self._store._iter_alive(): |
99.1.1
by Gustavo Niemeyer
- Now ResultSet.set() will set only the involved attributes to |
1204 |
try: |
1205 |
if (obj_info.cls_info is self._cls_spec_info and |
|
1206 |
(match is None or match(get_column))): |
|
142.2.2
by Gustavo Niemeyer
- When an object died in memory, ResultSet.cached() wasn't returning |
1207 |
objects.append(self._store._get_object(obj_info)) |
99.1.1
by Gustavo Niemeyer
- Now ResultSet.set() will set only the involved attributes to |
1208 |
except LostObjectError: |
142.2.2
by Gustavo Niemeyer
- When an object died in memory, ResultSet.cached() wasn't returning |
1209 |
pass # This may happen when resolving lazy values |
1210 |
# in get_column().
|
|
44
by Gustavo Niemeyer
- Decoupled most of the logic from Reference into Relation, and |
1211 |
return objects |
62
by Gustavo Niemeyer
Changed the way that objects are keyed. Now they use the obj_info, |
1212 |
|
95.3.60
by Gustavo Niemeyer
Implemented support for ResultSet.difference()/intersection(). |
1213 |
def _set_expr(self, expr_cls, other, all=False): |
1214 |
if self._cls_spec_info != other._cls_spec_info: |
|
1215 |
raise FeatureError("Incompatible results for set operation") |
|
1216 |
||
1217 |
expr = expr_cls(self._get_select(), other._get_select(), all=all) |
|
1218 |
return ResultSet(self._store, self._cls_spec_info, select=expr) |
|
1219 |
||
95.3.20
by Gustavo Niemeyer
Initial implementation of ResultSet.union(). order_by() doesn't yet |
1220 |
def union(self, other, all=False): |
142.3.20
by Christopher Armstrong
massive doc improvements for store.py |
1221 |
"""Get the L{Union} of this result set and another.
|
1222 |
||
1223 |
@param all: If True, include duplicates.
|
|
1224 |
"""
|
|
95.3.20
by Gustavo Niemeyer
Initial implementation of ResultSet.union(). order_by() doesn't yet |
1225 |
if isinstance(other, EmptyResultSet): |
1226 |
return self |
|
95.3.60
by Gustavo Niemeyer
Implemented support for ResultSet.difference()/intersection(). |
1227 |
return self._set_expr(Union, other, all) |
1228 |
||
1229 |
def difference(self, other, all=False): |
|
142.3.20
by Christopher Armstrong
massive doc improvements for store.py |
1230 |
"""Get the difference, using L{Except}, of this result set and another.
|
1231 |
||
1232 |
@param all: If True, include duplicates.
|
|
1233 |
"""
|
|
95.3.60
by Gustavo Niemeyer
Implemented support for ResultSet.difference()/intersection(). |
1234 |
if isinstance(other, EmptyResultSet): |
1235 |
return self |
|
1236 |
return self._set_expr(Except, other, all) |
|
1237 |
||
1238 |
def intersection(self, other, all=False): |
|
142.3.20
by Christopher Armstrong
massive doc improvements for store.py |
1239 |
"""Get the L{Intersection} of this result set and another.
|
1240 |
||
1241 |
@param all: If True, include duplicates.
|
|
1242 |
"""
|
|
95.3.60
by Gustavo Niemeyer
Implemented support for ResultSet.difference()/intersection(). |
1243 |
if isinstance(other, EmptyResultSet): |
1244 |
return other |
|
1245 |
return self._set_expr(Intersect, other, all) |
|
95.3.20
by Gustavo Niemeyer
Initial implementation of ResultSet.union(). order_by() doesn't yet |
1246 |
|
59.1.3
by Jamshed Kakar
- Introduced Result.close() and Connection.close(). |
1247 |
|
92.1.1
by Jamshed Kakar
- Implemented EmptyResultSet. |
1248 |
class EmptyResultSet(object): |
142.3.20
by Christopher Armstrong
massive doc improvements for store.py |
1249 |
"""An object that looks like a L{ResultSet} but represents no rows.
|
1250 |
||
1251 |
This is convenient for application developers who want to provide
|
|
1252 |
a method which is guaranteed to return a L{ResultSet}-like object
|
|
1253 |
but which, in certain cases, knows there is no point in querying
|
|
1254 |
the database. For example::
|
|
1255 |
||
1256 |
def get_people(self, ids):
|
|
1257 |
if not ids:
|
|
1258 |
return EmptyResultSet()
|
|
1259 |
return store.find(People, People.id.is_in(ids))
|
|
1260 |
||
1261 |
The methods on EmptyResultSet (L{one}, L{config}, L{union}, etc)
|
|
1262 |
are meant to emulate a L{ResultSet} which has matched no rows.
|
|
1263 |
"""
|
|
92.1.1
by Jamshed Kakar
- Implemented EmptyResultSet. |
1264 |
|
92.1.2
by Jamshed Kakar
- Fixed float conversion bug in ResultSet.avg(). Updated |
1265 |
def __init__(self, ordered=False): |
1266 |
self._order_by = ordered |
|
92.1.1
by Jamshed Kakar
- Implemented EmptyResultSet. |
1267 |
|
95.3.20
by Gustavo Niemeyer
Initial implementation of ResultSet.union(). order_by() doesn't yet |
1268 |
def _get_select(self): |
1269 |
return Select(SQLRaw("1"), SQLRaw("1 = 2")) |
|
1270 |
||
92.1.1
by Jamshed Kakar
- Implemented EmptyResultSet. |
1271 |
def copy(self): |
92.1.2
by Jamshed Kakar
- Fixed float conversion bug in ResultSet.avg(). Updated |
1272 |
result = EmptyResultSet(self._order_by) |
92.1.1
by Jamshed Kakar
- Implemented EmptyResultSet. |
1273 |
return result |
1274 |
||
1275 |
def config(self, distinct=None, offset=None, limit=None): |
|
1276 |
pass
|
|
1277 |
||
1278 |
def __iter__(self): |
|
92.1.2
by Jamshed Kakar
- Fixed float conversion bug in ResultSet.avg(). Updated |
1279 |
return
|
1280 |
yield None |
|
92.1.1
by Jamshed Kakar
- Implemented EmptyResultSet. |
1281 |
|
1282 |
def __getitem__(self, index): |
|
92.1.2
by Jamshed Kakar
- Fixed float conversion bug in ResultSet.avg(). Updated |
1283 |
return self.copy() |
92.1.1
by Jamshed Kakar
- Implemented EmptyResultSet. |
1284 |
|
1285 |
def any(self): |
|
1286 |
return None |
|
1287 |
||
1288 |
def first(self): |
|
1289 |
if self._order_by: |
|
1290 |
return None |
|
1291 |
raise UnorderedError("Can't use first() on unordered result set") |
|
1292 |
||
1293 |
def last(self): |
|
1294 |
if self._order_by: |
|
1295 |
return None |
|
1296 |
raise UnorderedError("Can't use last() on unordered result set") |
|
1297 |
||
1298 |
def one(self): |
|
1299 |
return None |
|
1300 |
||
1301 |
def order_by(self, *args): |
|
1302 |
self._order_by = True |
|
1303 |
return self |
|
1304 |
||
1305 |
def remove(self): |
|
1306 |
pass
|
|
1307 |
||
93.1.1
by Christopher Armstrong
make EmptyResultSet.count take the same kind of args as the real ResultSet.count |
1308 |
def count(self, column=Undef, distinct=False): |
92.1.1
by Jamshed Kakar
- Implemented EmptyResultSet. |
1309 |
return 0 |
1310 |
||
1311 |
def max(self, column): |
|
1312 |
return None |
|
1313 |
||
1314 |
def min(self, column): |
|
1315 |
return None |
|
1316 |
||
1317 |
def avg(self, column): |
|
92.1.2
by Jamshed Kakar
- Fixed float conversion bug in ResultSet.avg(). Updated |
1318 |
return None |
92.1.1
by Jamshed Kakar
- Implemented EmptyResultSet. |
1319 |
|
1320 |
def sum(self, column): |
|
1321 |
return None |
|
1322 |
||
1323 |
def values(self, *columns): |
|
1324 |
if not columns: |
|
1325 |
raise FeatureError("values() takes at least one column " |
|
1326 |
"as argument") |
|
92.1.2
by Jamshed Kakar
- Fixed float conversion bug in ResultSet.avg(). Updated |
1327 |
return
|
1328 |
yield None |
|
92.1.1
by Jamshed Kakar
- Implemented EmptyResultSet. |
1329 |
|
1330 |
def set(self, *args, **kwargs): |
|
1331 |
pass
|
|
1332 |
||
196.1.5
by Bernd Dorn
added more tests and changed according to review from niemeyer |
1333 |
def cached(self): |
92.1.1
by Jamshed Kakar
- Implemented EmptyResultSet. |
1334 |
return [] |
1335 |
||
95.3.20
by Gustavo Niemeyer
Initial implementation of ResultSet.union(). order_by() doesn't yet |
1336 |
def union(self, other): |
1337 |
if isinstance(other, EmptyResultSet): |
|
1338 |
return self |
|
1339 |
return other.union(self) |
|
1340 |
||
95.3.60
by Gustavo Niemeyer
Implemented support for ResultSet.difference()/intersection(). |
1341 |
def difference(self, other): |
1342 |
return self |
|
1343 |
||
1344 |
def intersection(self, other): |
|
1345 |
return self |
|
1346 |
||
92.1.1
by Jamshed Kakar
- Implemented EmptyResultSet. |
1347 |
|
81.1.3
by Gustavo Niemeyer
- Redesigned join expressions to handle more complex cases. |
1348 |
class TableSet(object): |
142.3.20
by Christopher Armstrong
massive doc improvements for store.py |
1349 |
"""The representation of a set of tables which can be queried at once.
|
1350 |
||
1351 |
This will typically be constructed by a call to L{Store.using}.
|
|
1352 |
"""
|
|
165
by Gustavo Niemeyer
Applying whitespace normalization patch by Johan Dahlin. [trivial] |
1353 |
|
81.1.3
by Gustavo Niemeyer
- Redesigned join expressions to handle more complex cases. |
1354 |
def __init__(self, store, tables): |
1355 |
self._store = store |
|
1356 |
self._tables = tables |
|
1357 |
||
81.1.5
by Gustavo Niemeyer
Implemented support for using a tuple of classes as the first argument of |
1358 |
def find(self, cls_spec, *args, **kwargs): |
142.3.20
by Christopher Armstrong
massive doc improvements for store.py |
1359 |
"""Perform a query on the previously specified tables.
|
1360 |
||
142.3.21
by Christopher Armstrong
improvements to store documentation on Gustavo's advice, thanks! |
1361 |
This is identical to L{Store.find} except that the tables are
|
1362 |
explicitly specified instead of relying on inference.
|
|
142.3.20
by Christopher Armstrong
massive doc improvements for store.py |
1363 |
|
1364 |
@return: A L{ResultSet}.
|
|
1365 |
"""
|
|
81.1.3
by Gustavo Niemeyer
- Redesigned join expressions to handle more complex cases. |
1366 |
self._store.flush() |
81.1.5
by Gustavo Niemeyer
Implemented support for using a tuple of classes as the first argument of |
1367 |
if type(cls_spec) is tuple: |
1368 |
cls_spec_info = tuple(get_cls_info(cls) for cls in cls_spec) |
|
1369 |
where = get_where_for_args(args, kwargs) |
|
1370 |
else: |
|
1371 |
cls_spec_info = get_cls_info(cls_spec) |
|
1372 |
where = get_where_for_args(args, kwargs, cls_spec) |
|
1373 |
return self._store._result_set_factory(self._store, cls_spec_info, |
|
1374 |
where, self._tables) |
|
81.1.3
by Gustavo Niemeyer
- Redesigned join expressions to handle more complex cases. |
1375 |
|
1376 |
||
62
by Gustavo Niemeyer
Changed the way that objects are keyed. Now they use the obj_info, |
1377 |
Store._result_set_factory = ResultSet |
81.1.3
by Gustavo Niemeyer
- Redesigned join expressions to handle more complex cases. |
1378 |
Store._table_set = TableSet |
80
by Gustavo Niemeyer
- Implemented support for "where" expressions being passed to |
1379 |
|
1380 |
||
81.1.5
by Gustavo Niemeyer
Implemented support for using a tuple of classes as the first argument of |
1381 |
def get_where_for_args(args, kwargs, cls=None): |
80
by Gustavo Niemeyer
- Implemented support for "where" expressions being passed to |
1382 |
equals = list(args) |
1383 |
if kwargs: |
|
81.1.9
by Gustavo Niemeyer
- Renamed UnsupportedDatabaseError to DatabaseModuleError. |
1384 |
if cls is None: |
1385 |
raise FeatureError("Can't determine class that keyword " |
|
1386 |
"arguments are associated with") |
|
80
by Gustavo Niemeyer
- Implemented support for "where" expressions being passed to |
1387 |
for key, value in kwargs.items(): |
95.3.21
by Gustavo Niemeyer
Implemented reference comparisons (Class.ref == obj, Class.ref == obj.id, |
1388 |
equals.append(getattr(cls, key) == value) |
80
by Gustavo Niemeyer
- Implemented support for "where" expressions being passed to |
1389 |
if equals: |
1390 |
return And(*equals) |
|
1391 |
return Undef |
|
83.1.1
by Gustavo Niemeyer
- Implemented 'autoreload' feature! |
1392 |
|
1393 |
||
1394 |
class AutoReload(LazyValue): |
|
142.3.20
by Christopher Armstrong
massive doc improvements for store.py |
1395 |
"""A marker for reloading a single value.
|
1396 |
||
1397 |
Often this will be used to specify that a specific attribute
|
|
1398 |
should be loaded from the database on the next access, like so::
|
|
1399 |
||
1400 |
storm_object.property = AutoReload
|
|
1401 |
||
1402 |
On the next access to C{storm_object.property}, the value will be
|
|
1403 |
loaded from the database.
|
|
1404 |
||
1405 |
It is also often used as a default value for a property::
|
|
1406 |
||
1407 |
class Person(object):
|
|
1408 |
__storm_table__ = "person"
|
|
1409 |
id = Int(allow_none=False, default=AutoReload)
|
|
1410 |
||
1411 |
person = store.add(Person)
|
|
1412 |
person.id # gets the attribute from the database.
|
|
1413 |
"""
|
|
83.1.1
by Gustavo Niemeyer
- Implemented 'autoreload' feature! |
1414 |
pass
|
1415 |
||
1416 |
AutoReload = AutoReload() |