| 1 |
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" |
|---|
| 2 |
"http://www.w3.org/TR/xhtml1/DTD/strict.dtd"> |
|---|
| 3 |
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"> |
|---|
| 4 |
|
|---|
| 5 |
<head> |
|---|
| 6 |
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> |
|---|
| 7 |
<title>Dejavu: Modeling your Application</title> |
|---|
| 8 |
<link href='dejavu.css' rel='stylesheet' type='text/css' /> |
|---|
| 9 |
</head> |
|---|
| 10 |
|
|---|
| 11 |
<body> |
|---|
| 12 |
|
|---|
| 13 |
<h2>Application Designers: Using Dejavu to Construct a Domain Model</h2> |
|---|
| 14 |
|
|---|
| 15 |
<h3>Units</h3> |
|---|
| 16 |
<p>When constructing a Domain Model for your application, you will want |
|---|
| 17 |
to distinguish between objects that will be persisted and objects that |
|---|
| 18 |
will not. By registering a subclass of <tt>dejavu.Unit</tt>, you allow |
|---|
| 19 |
instances of that subclass to be persisted.</p> |
|---|
| 20 |
|
|---|
| 21 |
<p>Before you can register your Unit class, you must create it: |
|---|
| 22 |
<pre>import dejavu |
|---|
| 23 |
class Printer(dejavu.Unit): pass</pre> |
|---|
| 24 |
This is all you need for a fully-functioning Unit class. There are |
|---|
| 25 |
no methods or attributes that you are required to override; simply |
|---|
| 26 |
subclass from <tt>Unit</tt>. However, this is a fairly uninteresting |
|---|
| 27 |
class. It automatically has an ID property, but doesn't provide any |
|---|
| 28 |
functionality other than what <tt>Unit</tt> already provides. The first |
|---|
| 29 |
thing we will probably want to add to our new class is persistent data.</p> |
|---|
| 30 |
|
|---|
| 31 |
<h4>UnitProperty</h4> |
|---|
| 32 |
<p>Once you have defined a persistent class (by subclassing <tt>Unit</tt>), |
|---|
| 33 |
you need to make another decision. Rather than persist the entire object |
|---|
| 34 |
<tt>__dict__</tt>, you specify a subset of persistent attributes by using |
|---|
| 35 |
<tt>UnitProperty</tt>, a data descriptor. If you've used Python's builtin |
|---|
| 36 |
property() construct, you've used descriptors before.</p> |
|---|
| 37 |
|
|---|
| 38 |
<p>We might enhance our Printer example thusly: |
|---|
| 39 |
<pre>from dejavu import Unit, UnitProperty |
|---|
| 40 |
class Printer(Unit): |
|---|
| 41 |
Manufacturer = UnitProperty(unicode) |
|---|
| 42 |
ColorCopies = UnitProperty(bool) |
|---|
| 43 |
PPM = UnitProperty(float)</pre> |
|---|
| 44 |
This adds three persistent attributes to our <tt>Printer</tt> objects, |
|---|
| 45 |
each with a different datatype. In addition, every subclass of <tt>Unit</tt> |
|---|
| 46 |
inherits an 'ID' property, an int.</p> |
|---|
| 47 |
|
|---|
| 48 |
<p>When you get and set <tt>UnitProperty</tt> attributes, they behave just |
|---|
| 49 |
like any other attributes: |
|---|
| 50 |
<pre>>>> p = Printer() |
|---|
| 51 |
>>> p.PPM = 25 |
|---|
| 52 |
>>> p.PPM |
|---|
| 53 |
25.0</pre> |
|---|
| 54 |
However, you will notice right away that the int value we provided has been |
|---|
| 55 |
coerced to a float behind the scenes. This is because we specified the PPM |
|---|
| 56 |
attribute as a 'float' type when we created it. The value of a Unit |
|---|
| 57 |
Property is restricted to the type which you specify. The only other valid |
|---|
| 58 |
value for a Unit Property is None; any Property may be None at any time, |
|---|
| 59 |
and in fact, all Properties are None until you assign values to them: |
|---|
| 60 |
<pre>>>> p.ColorCopies is None |
|---|
| 61 |
True</pre></p> |
|---|
| 62 |
|
|---|
| 63 |
<h5>datetime.datetime</h5> |
|---|
| 64 |
<p>If you use <tt>datetime.datetime</tt> for the type of a UnitProperty, |
|---|
| 65 |
most StorageManagers will throw away the microseconds. This is |
|---|
| 66 |
an unfortunate oversight that should be corrected in 1.5.</p> |
|---|
| 67 |
|
|---|
| 68 |
<h4>Unit ID's</h4> |
|---|
| 69 |
<p>All Units possess an <tt>identifiers</tt> attribute, a tuple of |
|---|
| 70 |
their UnitProperties which define the uniqueness of a Unit. The |
|---|
| 71 |
<tt>Unit</tt> base class possesses a single Unit Property, an int |
|---|
| 72 |
named 'ID', and its <tt>identifiers</tt> attribute is therefore |
|---|
| 73 |
<tt>(ID,)</tt>. That's not a string in the tuple; it's a reference |
|---|
| 74 |
to the actual UnitProperty class. If you wish to use identifiers |
|---|
| 75 |
of a different number, types, or names, simply replace the |
|---|
| 76 |
<tt>identifiers</tt> attribute in your subclass:</p> |
|---|
| 77 |
|
|---|
| 78 |
<pre>class Printer(Unit): |
|---|
| 79 |
# Set ID to None to remove the ID property from this subclass. |
|---|
| 80 |
ID = None |
|---|
| 81 |
|
|---|
| 82 |
Model = UnitProperty(unicode) |
|---|
| 83 |
UnitNumber = UnitProperty(int) |
|---|
| 84 |
identifiers = (Model, UnitNumber) |
|---|
| 85 |
</pre> |
|---|
| 86 |
|
|---|
| 87 |
<p>Every Unit must possess at least one identifier. This ensures that |
|---|
| 88 |
each Unit within the system is unique. You should consider any |
|---|
| 89 |
UnitProperty which is one of the identifiers to be read-only |
|---|
| 90 |
after a Unit has been memorized.</p> |
|---|
| 91 |
|
|---|
| 92 |
<h4>Creating and Populating Properties</h4> |
|---|
| 93 |
<p>In addition to defining Unit Properties within your class body, |
|---|
| 94 |
you can define them after the class body has been executed via |
|---|
| 95 |
the classmethod <tt class='def'>Unit.set_property()</tt>. For example, |
|---|
| 96 |
the following two classes are equivalent: |
|---|
| 97 |
<pre>class Book(Unit): |
|---|
| 98 |
Content = UnitProperty(unicode) |
|---|
| 99 |
|
|---|
| 100 |
class Book(Unit): pass |
|---|
| 101 |
Book.set_property('Content', unicode)</pre> |
|---|
| 102 |
|
|---|
| 103 |
Declarations outside of the class body allow more dynamic setting of |
|---|
| 104 |
Unit properties. You can define multiple properties at once via |
|---|
| 105 |
the <tt class='def'>Unit.set_properties()</tt> classmethod: |
|---|
| 106 |
|
|---|
| 107 |
<pre>class Book(Unit): pass |
|---|
| 108 |
Book.set_properties({'Content': unicode, |
|---|
| 109 |
'Publisher': unicode, |
|---|
| 110 |
'Year': int, |
|---|
| 111 |
})</pre> |
|---|
| 112 |
</p> |
|---|
| 113 |
|
|---|
| 114 |
<p>You also have options when populating Unit Properties. The standard way |
|---|
| 115 |
is simply to reference them as normal Python instance attributes. However, |
|---|
| 116 |
you may also use the <tt class='def'>adjust()</tt> method to modify |
|---|
| 117 |
multiple properties at once; pass in keyword arguments which match the |
|---|
| 118 |
properties you wish to modify. Keyword arguments also work when |
|---|
| 119 |
instantiating the object. For example, the following three code |
|---|
| 120 |
snippets are equivalent: |
|---|
| 121 |
|
|---|
| 122 |
<pre>pub = Book() |
|---|
| 123 |
pub.Publisher = 'Walter J. Black' |
|---|
| 124 |
pub.Year = 1928 |
|---|
| 125 |
|
|---|
| 126 |
pub = Book() |
|---|
| 127 |
pub.adjust(Publisher='Walter J. Black', Year=1928) |
|---|
| 128 |
|
|---|
| 129 |
pub = Book(Publisher='Walter J. Black', Year=1928)</pre> |
|---|
| 130 |
</p> |
|---|
| 131 |
|
|---|
| 132 |
<h4>Unit Properties are First-Class Objects</h4> |
|---|
| 133 |
<p>Like many descriptors, Unit Properties behave differently when you access |
|---|
| 134 |
them from the class, rather than from an instance as above. When calling |
|---|
| 135 |
them from the class, you receive the <tt>UnitProperty</tt> object itself, |
|---|
| 136 |
rather than its value for a given instance. That is, |
|---|
| 137 |
<pre>>>> c = Printer.ColorCopies |
|---|
| 138 |
>>> c |
|---|
| 139 |
<dejavu.UnitProperty object at 0x01112970></pre> |
|---|
| 140 |
This is significant, because it allows us to store metadata about the |
|---|
| 141 |
property itself: |
|---|
| 142 |
<pre>>>> c.type, c.index, c.hints, c.key |
|---|
| 143 |
(<type 'bool'>, False, {}, 'ColorCopies')</pre> |
|---|
| 144 |
|
|---|
| 145 |
When you define a UnitProperty instance, you can pass in these extra |
|---|
| 146 |
attributes. Its signature is <tt class='def'>UnitProperty(type=unicode, |
|---|
| 147 |
index=False, hints={}, key=None, default=None)</tt>. Supply any, all, |
|---|
| 148 |
or none of them as |
|---|
| 149 |
needed. The <tt>key</tt> attribute is merely the property's canonical name, |
|---|
| 150 |
and is usually set for you. The <tt>index</tt> value tells database Storage |
|---|
| 151 |
Managers whether or not to index the column (if they do any indexing). The |
|---|
| 152 |
<tt>type</tt> attribute limits property values to instances of that type |
|---|
| 153 |
(or <tt>None</tt>). Finally, the <tt>hints</tt> dictionary provides hints |
|---|
| 154 |
to Storage Managers to help optimize storage. If you write a custom Storage |
|---|
| 155 |
Manager, you may define and use your own hints. Here are the ones that most |
|---|
| 156 |
builtin SM's understand:</p> |
|---|
| 157 |
|
|---|
| 158 |
<table> |
|---|
| 159 |
<tr><th>Key</th><th>Values</th><th>Description</th></tr> |
|---|
| 160 |
<tr> |
|---|
| 161 |
<td>bytes</td> |
|---|
| 162 |
<td>>= 0</td> |
|---|
| 163 |
<td>Inform SMs that would usually store unicode strings as strings of |
|---|
| 164 |
unlimited length, that a particular value should be a smaller |
|---|
| 165 |
object. A value of 0 implies no limit.</td> |
|---|
| 166 |
</tr> |
|---|
| 167 |
<tr> |
|---|
| 168 |
<td>scale</td> |
|---|
| 169 |
<td>>= 0</td> |
|---|
| 170 |
<td>Scale is the number of digits to the right of the decimal point |
|---|
| 171 |
in a NUMERIC (fixedpoint or decimal) field. This hint informs SMs |
|---|
| 172 |
that would usually store such data at a default scale (usually 2), |
|---|
| 173 |
that the property should use a different scale.</td> |
|---|
| 174 |
</tr> |
|---|
| 175 |
<tr> |
|---|
| 176 |
<td>precision</td> |
|---|
| 177 |
<td>>= 0</td> |
|---|
| 178 |
<td>Precision is the total number of digits in a NUMERIC (fixedpoint or |
|---|
| 179 |
decimal) field. This hint informs SMs that would usually store such |
|---|
| 180 |
data at maximum precision, that the property should be a smaller |
|---|
| 181 |
object. A value of 0 (or no hint) implies the maximum that the SM |
|---|
| 182 |
can supply. PostgreSQL, for example, can handle 1000 digits. Note |
|---|
| 183 |
that the <tt>fixedpoint</tt> module uses the word "precision" where |
|---|
| 184 |
we use the word "scale"; it actually has unlimited precision (as |
|---|
| 185 |
we use the word). The <tt>decimal</tt> module, in contrast, has |
|---|
| 186 |
limited precision but no scale.</td> |
|---|
| 187 |
</tr> |
|---|
| 188 |
</table> |
|---|
| 189 |
|
|---|
| 190 |
|
|---|
| 191 |
<h4>Triggers</h4> |
|---|
| 192 |
<p>Triggers are behaviors which fire when the value of a Unit Property is |
|---|
| 193 |
changed. You can override a UnitProperty's __set__ method to achieve this |
|---|
| 194 |
in Dejavu. For example: |
|---|
| 195 |
<pre>class DatedProperty(UnitProperty): |
|---|
| 196 |
def __set__(self, unit, value): |
|---|
| 197 |
UnitProperty.__set__(self, unit, value) |
|---|
| 198 |
unit.Date = datetime.datetime.now().replace(microsecond=0) |
|---|
| 199 |
parent = unit.Forum() |
|---|
| 200 |
if parent: |
|---|
| 201 |
parent.Date = unit.Date |
|---|
| 202 |
|
|---|
| 203 |
class Topic(Unit): |
|---|
| 204 |
Date = UnitProperty(datetime.date) |
|---|
| 205 |
Content = DatedProperty() |
|---|
| 206 |
ForumID = UnitProperty(int) |
|---|
| 207 |
|
|---|
| 208 |
class Forum(Unit): |
|---|
| 209 |
Date = UnitProperty(datetime.date) |
|---|
| 210 |
|
|---|
| 211 |
Topic.many_to_one('ForumID', Forum, 'ID')</pre> |
|---|
| 212 |
In this example, whenever Topic().Content is set, the <tt>__set__</tt> |
|---|
| 213 |
method will be called and the object's <tt>Date</tt> attribute will |
|---|
| 214 |
be modified. Then, the Topic's parent Forum is looked up and <i>its</i> |
|---|
| 215 |
<tt>Date</tt> is modified.</p> |
|---|
| 216 |
|
|---|
| 217 |
<p>As with any trigger system, you need to be careful not to have triggers |
|---|
| 218 |
called out of order. For example, if a user changes both the ForumID and |
|---|
| 219 |
Content properties in a single operation (like a web page submit), the old |
|---|
| 220 |
Forum will be incorrectly modified if the Content property is applied |
|---|
| 221 |
first. I don't have any cool tools built into Dejavu to help you with |
|---|
| 222 |
this, but I'm open to suggestions.</p> |
|---|
| 223 |
|
|---|
| 224 |
<h5>TriggerProperty</h5> |
|---|
| 225 |
<p>Dejavu 1.4 has a new <tt class='def'>TriggerProperty</tt> class, |
|---|
| 226 |
which overrides <tt>__set__</tt> for you. If the value in question |
|---|
| 227 |
changes (and the unit has a sandbox), then the |
|---|
| 228 |
<tt class='def'>on_set(unit, oldvalue)</tt> method will be called. |
|---|
| 229 |
Override it in your subclass like this: |
|---|
| 230 |
<pre> |
|---|
| 231 |
class NoteContentProperty(TriggerProperty): |
|---|
| 232 |
|
|---|
| 233 |
def on_set(self, unit, oldvalue): |
|---|
| 234 |
unit.LastModified = datetime.date.today() |
|---|
| 235 |
</pre> |
|---|
| 236 |
|
|---|
| 237 |
Note that, if you need to know what the <i>new</i> value is, it's |
|---|
| 238 |
already been set on the unit.</p> |
|---|
| 239 |
|
|---|
| 240 |
<h4>Registration of Unit Classes</h4> |
|---|
| 241 |
<p>In addition to defining your Unit class, you must also register that |
|---|
| 242 |
class with your application's <tt>Arena</tt> object. Each class which |
|---|
| 243 |
you want Dejavu to manage must be passed to |
|---|
| 244 |
<tt class='def'>Arena.register(cls)</tt>. |
|---|
| 245 |
If you create a module with multiple classes, you can register them all |
|---|
| 246 |
at once with <tt class='def'>Arena.register_all(globals())</tt>. It will |
|---|
| 247 |
grab any Unit subclasses out of your module's globals() (or any other |
|---|
| 248 |
mapping you pass to <tt>register_all</tt>) and register them.</p> |
|---|
| 249 |
|
|---|
| 250 |
<p>The register and register_all methods also register any Associations |
|---|
| 251 |
you have defined between Units.</p> |
|---|
| 252 |
|
|---|
| 253 |
<h4>Subclasses and Inheritance</h4> |
|---|
| 254 |
<p>Starting in 1.4, you can use superclasses to recall all subclasses. |
|---|
| 255 |
For example: |
|---|
| 256 |
|
|---|
| 257 |
<pre>class Payment(Unit): pass |
|---|
| 258 |
class Cash(Payment): pass |
|---|
| 259 |
class Credit(Payment): pass |
|---|
| 260 |
|
|---|
| 261 |
...time passes and instances of each class are memorized... |
|---|
| 262 |
|
|---|
| 263 |
sandbox.recall(Payment) |
|---|
| 264 |
[<bill.Payment object at 0x01158E10>, |
|---|
| 265 |
<bill.Cash object at 0x0118B350>, |
|---|
| 266 |
<bill.Credit object at 0x0118B170>] |
|---|
| 267 |
</pre> |
|---|
| 268 |
|
|---|
| 269 |
<p>This also works for the <tt>xrecall</tt> and <tt>unit</tt> methods, but |
|---|
| 270 |
only when providing a single class (none of them retrieve subclasses when |
|---|
| 271 |
recalling joined classes yet). Currently, you cannot reference a property |
|---|
| 272 |
(in an Expression) which is not present in both the superclass and all of |
|---|
| 273 |
the subclasses.</p> |
|---|
| 274 |
|
|---|
| 275 |
<h3>Sandboxes</h3> |
|---|
| 276 |
<p>During the life of a client connection, your application should create |
|---|
| 277 |
and use a <tt>Sandbox</tt> to manage the set of "live" Units. A Sandbox |
|---|
| 278 |
manages the in-memory lifecycle of Units: creation, identity, mutation, and |
|---|
| 279 |
destruction. Sandboxes route persistence operations on Units to the correct |
|---|
| 280 |
Storage Manager.</p> |
|---|
| 281 |
|
|---|
| 282 |
<p>You can create Sandbox objects directly. They take a single argument, the |
|---|
| 283 |
top-level <tt>Arena</tt> object. Arenas also provide a convenience function, |
|---|
| 284 |
<tt class='def'>new_sandbox</tt>, which does this for you. The following |
|---|
| 285 |
lines are equivalent: |
|---|
| 286 |
<pre>box = dejavu.Sandbox(myArena) |
|---|
| 287 |
|
|---|
| 288 |
box = myArena.new_sandbox()</pre> |
|---|
| 289 |
You might often choose the latter when you have a reference to the Arena |
|---|
| 290 |
object, and would rather avoid importing dejavu yet again just to obtain |
|---|
| 291 |
the Sandbox class.</p> |
|---|
| 292 |
|
|---|
| 293 |
<a name='memorize'><h4>Memorizing Units</h4></a> |
|---|
| 294 |
<p>When you create a Unit instance, it exists in isolation. There is no |
|---|
| 295 |
connection between that Unit and storage; your Unit will not be persisted, |
|---|
| 296 |
because Dejavu doesn't yet possess a reference to your Unit. To provide |
|---|
| 297 |
that link, you <i>memorize</i> your Unit (or rather, you tell your Sandbox |
|---|
| 298 |
to memorize it): |
|---|
| 299 |
<pre>class Publisher(Unit): |
|---|
| 300 |
City = UnitProperty(unicode) |
|---|
| 301 |
|
|---|
| 302 |
p = Publisher(ID='Walter J. Black') |
|---|
| 303 |
box.memorize(p)</pre></p> |
|---|
| 304 |
|
|---|
| 305 |
<p>Memorization does several things. First, it places your new Unit into |
|---|
| 306 |
your Arena. That Unit instance will now be persisted by the appropriate |
|---|
| 307 |
Storage Manager. It can be recalled from storage when needed, using the |
|---|
| 308 |
built-in Expression syntax. It may have been given an ID (see |
|---|
| 309 |
<u>Sequencing</u>, below). Memorization also makes your Unit |
|---|
| 310 |
<i>concrete</i>; that is, your Unit will now possess a <tt>sandbox</tt> |
|---|
| 311 |
attribute. Units whose <tt>sandbox</tt> attribute is not set (is None) |
|---|
| 312 |
have no relationships, and their Unit Property triggers (if any) will |
|---|
| 313 |
not fire.</p> |
|---|
| 314 |
|
|---|
| 315 |
<p>You may define special methods on your Units to provide start-of-life |
|---|
| 316 |
behaviors. If a Unit possesses an <tt>on_memorize</tt> method, it will |
|---|
| 317 |
be called after the Unit has been 'reserved' in storage, and after the |
|---|
| 318 |
Unit has been placed in the Sandbox cache.</p> |
|---|
| 319 |
|
|---|
| 320 |
<h4>Sequencing</h4> |
|---|
| 321 |
<p>Every <tt>Unit</tt> has one or more identifiers. The default ID property |
|---|
| 322 |
is of type <tt>int</tt>; however, you can override that to whatever type |
|---|
| 323 |
you like. As long as you provide your own identifier values for Units, |
|---|
| 324 |
nothing will break--you can memorize and recall Units without problems. |
|---|
| 325 |
However, if you memorize a Unit with an ID of <tt>None</tt>, the Sandbox |
|---|
| 326 |
may attempt to provide an ID for it.</p> |
|---|
| 327 |
|
|---|
| 328 |
<p>The <tt>Unit</tt> base class possesses a <tt>sequencer</tt> attribute |
|---|
| 329 |
to help Sandboxes generate new IDs. The default value is an instance of |
|---|
| 330 |
<tt>UnitSequencerInteger</tt>, which examines all existing Units, finds |
|---|
| 331 |
the maximum integer ID, adds 1, and uses that value for the new ID.</p> |
|---|
| 332 |
|
|---|
| 333 |
<p>The other useful Sequencer is <tt>UnitSequencerNull</tt>, which simply |
|---|
| 334 |
raises an error when asked to generate an ID. If you set <tt>ClassA.ID</tt> |
|---|
| 335 |
to a string or unicode type, you'll probably want to set |
|---|
| 336 |
<tt>ClassA.sequencer = dejavu.UnitSequencerNull()</tt>, and form ID values |
|---|
| 337 |
in your own code.</p> |
|---|
| 338 |
|
|---|
| 339 |
<h4>Recalling</h4> |
|---|
| 340 |
<p>Once you have memorized a Unit or two, you will probably want to |
|---|
| 341 |
recall them at some point. Sandboxes possess four member functions to |
|---|
| 342 |
accomplish this.</p> |
|---|
| 343 |
|
|---|
| 344 |
<h5>recall()</h5> |
|---|
| 345 |
<p>First, the appropriately named <tt class='def'>recall(cls, expr)</tt> function. |
|---|
| 346 |
This is the full-blown query method. As a first argument, you pass it the |
|---|
| 347 |
class (<b>not</b> the name of the class, but the actual class) of which you |
|---|
| 348 |
expect to retrieve instances. The second argument should be an instance |
|---|
| 349 |
of <tt>dejavu.logic.Expression</tt>, an object which encapsulates your |
|---|
| 350 |
specific query (see <a href='managing.html#Querying'>Querying</a>). |
|---|
| 351 |
An example recall operation: |
|---|
| 352 |
<pre>>>> e = logic.Expression(lambda x: x.Year == 1928) |
|---|
| 353 |
>>> units = box.recall(Book, e) |
|---|
| 354 |
>>> [x.Title for x in units] |
|---|
| 355 |
[u'The Giant Horse of Oz', u'Kai Lung Unrolls His Mat', |
|---|
| 356 |
u'Tarzan, The Lord of the Jungle'] |
|---|
| 357 |
</pre> |
|---|
| 358 |
If you do not supply an Expression, all Units of the given Unit class |
|---|
| 359 |
will be retrieved in a list.</p> |
|---|
| 360 |
|
|---|
| 361 |
<p>If your Unit class defines an <tt>on_recall()</tt> method, it will be |
|---|
| 362 |
called when each Unit has been loaded from storage (at the end of the |
|---|
| 363 |
recall process). Once the unit is loaded into a Sandbox, however, |
|---|
| 364 |
<tt>on_recall</tt> will not be called; it's only called at the Sandbox/SM |
|---|
| 365 |
boundary. If <tt>on_recall</tt> raises <tt>UnrecallableError</tt>, the |
|---|
| 366 |
unit will not be yielded back to the caller, nor placed in the Sandbox |
|---|
| 367 |
cache.</p> |
|---|
| 368 |
|
|---|
| 369 |
<h5>Recalling multiple classes at once (JOINs)</h5> |
|---|
| 370 |
|
|---|
| 371 |
<p>In addition to providing a single class to <tt>recall</tt>, you have |
|---|
| 372 |
the option of providing a tree of classes, a nested set of |
|---|
| 373 |
<tt class='def'>UnitJoin(class1, class2, leftbiased=None)</tt> |
|---|
| 374 |
instances.</p> |
|---|
| 375 |
|
|---|
| 376 |
|
|---|
| 377 |
<p>The "leftbiased" argument specifies how the results will be |
|---|
| 378 |
joined:</p> |
|---|
| 379 |
|
|---|
| 380 |
<table> |
|---|
| 381 |
<tr><th>leftbiased</th><th>Join Type</th><th>Description</th><th>Operator</th></tr> |
|---|
| 382 |
<tr> |
|---|
| 383 |
<td>None</td> |
|---|
| 384 |
<td>Inner Join</td> |
|---|
| 385 |
<td>All related pairs of both classes will be returned.</td> |
|---|
| 386 |
<td><tt>&</tt> or <tt>+</tt></td> |
|---|
| 387 |
</tr> |
|---|
| 388 |
<tr> |
|---|
| 389 |
<td>True</td> |
|---|
| 390 |
<td>Left Join</td> |
|---|
| 391 |
<td>All related pairs of both classes will be returned. In addition, |
|---|
| 392 |
if any Unit in class1 has no match in class2, we return a single |
|---|
| 393 |
row with Unit1 and a "null Unit" (a Unit, all of whose properties |
|---|
| 394 |
are None).</td> |
|---|
| 395 |
<td><tt><<</tt></td> |
|---|
| 396 |
</tr> |
|---|
| 397 |
<tr> |
|---|
| 398 |
<td>False</td> |
|---|
| 399 |
<td>Right Join</td> |
|---|
| 400 |
<td>All related pairs of both classes will be returned. In addition, |
|---|
| 401 |
if any Unit in class2 has no match in class1, we return a single |
|---|
| 402 |
row with a "null Unit" (a Unit, all of whose properties are None) |
|---|
| 403 |
and Unit2.</td> |
|---|
| 404 |
<td><tt>>></tt></td> |
|---|
| 405 |
</tr> |
|---|
| 406 |
</table> |
|---|
| 407 |
|
|---|
| 408 |
<p>Look hard? Fear not. There's a <b>much</b> easier way to join units than |
|---|
| 409 |
writing a big tree of UnitJoins. Use the &, <<, and >> |
|---|
| 410 |
operators directly with Unit classes:</p> |
|---|
| 411 |
|
|---|
| 412 |
<pre>tree = (Book << Publisher) & Author</pre> |
|---|
| 413 |
|
|---|
| 414 |
<p>This example will automatically produce a UnitJoin tree for you, |
|---|
| 415 |
with Book 'left joined' to Publisher, and then 'inner joined' to |
|---|
| 416 |
Author.</p> |
|---|
| 417 |
|
|---|
| 418 |
<p>When you provide multiple classes, the <tt>recall</tt> method returns |
|---|
| 419 |
a list of rows. Each row will be a list of units, one per class in the |
|---|
| 420 |
<tt>classes</tt> arg. The <tt>expr</tt> arg should be a |
|---|
| 421 |
<tt>logic.Expression</tt> which can evaluate all of the |
|---|
| 422 |
units in any given row at once. |
|---|
| 423 |
|
|---|
| 424 |
<pre>pubs = box.recall(Publisher & Book, |
|---|
| 425 |
logic.Expression(lambda p, b: p.ID == 4))</pre> |
|---|
| 426 |
|
|---|
| 427 |
This example will retrieve a series of [Publisher, Book] pairs. |
|---|
| 428 |
Note that all three constructs (the UnitJoins, the lambda, and |
|---|
| 429 |
the resulting rows) have the same classes listed in order from |
|---|
| 430 |
left to right.</p> |
|---|
| 431 |
|
|---|
| 432 |
<p>In database terminology, this technique performs a series of joins |
|---|
| 433 |
between each pair of classes in your UnitJoin tree. However, repeated units |
|---|
| 434 |
in the results will reference the same object; in the example above, each |
|---|
| 435 |
Publisher unit will be the same object, since we limited that expression to |
|---|
| 436 |
a single Publisher. So we might examine multiple rows in the "pubs" list, |
|---|
| 437 |
but the first unit in each row will be the same unit instance.</p> |
|---|
| 438 |
|
|---|
| 439 |
<p>The relationships (joins) between each class are specified by |
|---|
| 440 |
Unit Associations (see <a href='#associations'>below</a>).</p> |
|---|
| 441 |
|
|---|
| 442 |
<h5>xrecall()</h5> |
|---|
| 443 |
<p>Just like recall, but returns an iterator instead of a list. Use xrecall |
|---|
| 444 |
to load Units in a more lazy fashion.</p> |
|---|
| 445 |
|
|---|
| 446 |
<h5>unit()</h5> |
|---|
| 447 |
<p>The <tt>recall</tt> method can be verbose. When you want a one-liner |
|---|
| 448 |
and only expect a single Unit, use the <tt class='def'>unit(cls, **kw)</tt> method |
|---|
| 449 |
of Sandboxes. Again, you pass the class of Units you wish to retrieve |
|---|
| 450 |
as the first argument. Then, supply keyword arguments of the form |
|---|
| 451 |
"property_name=value". The method will form an equivalent Expression |
|---|
| 452 |
for you from the keyword args. For example: |
|---|
| 453 |
<pre>>>> book = box.unit(Book, ID=1) |
|---|
| 454 |
>>> if book: |
|---|
| 455 |
... print book.Title |
|---|
| 456 |
u'Ladies in Hades'</pre> |
|---|
| 457 |
If a Unit is not found that matches the criteria, None is returned. |
|---|
| 458 |
If multiple Units match the criteria, only the first one is returned |
|---|
| 459 |
(although the rest are probably loaded into memory).</p> |
|---|
| 460 |
|
|---|
| 461 |
<h5>"Magic recaller" methods</h5> |
|---|
| 462 |
<p>For each class you have registered with your Arena, the Sandbox will |
|---|
| 463 |
have a "magic recaller" method of the same name, to make single-unit |
|---|
| 464 |
lookups easier. Instead of the above example for <tt>box.unit()</tt>, |
|---|
| 465 |
we might just as well have written: |
|---|
| 466 |
<pre>>>> book = box.Book(1)</pre> |
|---|
| 467 |
Note that for the magic methods, unlike for the <tt>unit</tt> method, |
|---|
| 468 |
you may pass identifiers as positional arguments. If the class has |
|---|
| 469 |
multiple identifiers, you should probably stick to keyword arguments; |
|---|
| 470 |
otherwise, you must remember the order of the class' identifiers tuple. |
|---|
| 471 |
</p> |
|---|
| 472 |
|
|---|
| 473 |
<h4>Forgetting and Repressing</h4> |
|---|
| 474 |
<p>To <i>forget</i> a Unit is to destroy it forever. You have two options |
|---|
| 475 |
for forgetting Units: you can call <tt>Sandbox().forget(unit)</tt> or |
|---|
| 476 |
the simpler version, <tt class='def'>Unit().forget()</tt>. Either of these will clear |
|---|
| 477 |
the Unit from the Sandbox' cache, and the Sandbox will tell the appropriate |
|---|
| 478 |
Storage Manager to destroy the stored Unit data. If a Unit has not yet |
|---|
| 479 |
been memorized, you do not need to forget it.</p> |
|---|
| 480 |
|
|---|
| 481 |
<p>In some circumstances, you may wish to only clear the Unit from the |
|---|
| 482 |
Sandbox without destroying it. You can do this by calling either |
|---|
| 483 |
<tt>Sandbox().repress(unit)</tt> or the simpler version, |
|---|
| 484 |
<tt class='def'>Unit().repress()</tt>.</p> |
|---|
| 485 |
|
|---|
| 486 |
<p>You may define special methods on your Units to provide end-of-life |
|---|
| 487 |
behaviors. If a Unit possesses an <tt>on_forget</tt> method, it will |
|---|
| 488 |
be called after the Unit has been destroyed. If a Unit possesses an |
|---|
| 489 |
<tt>on_repress</tt> method, it will be called <i>before</i> the Unit |
|---|
| 490 |
has been repressed. I'm sure there was a good reason for this |
|---|
| 491 |
disparity, but I've forgotten (or perhaps repressed) it.</p> |
|---|
| 492 |
|
|---|
| 493 |
<p>Be aware that many of the things you put in an <tt>on_repress</tt> |
|---|
| 494 |
handler might also need to go into <tt>on_forget</tt>. The one doesn't |
|---|
| 495 |
call the other automatically, because sometimes you <i>don't</i> want |
|---|
| 496 |
the same behavior.</p> |
|---|
| 497 |
|
|---|
| 498 |
|
|---|
| 499 |
<h4>Flushing Sandboxes</h4> |
|---|
| 500 |
<p>When the client connection has closed, you should <i>flush</i> the |
|---|
| 501 |
Sandbox caches. In general, a single call to |
|---|
| 502 |
<tt class='def'>Sandbox().flush_all()</tt> |
|---|
| 503 |
will do the trick. Notice that <tt>flush_all()</tt> calls any |
|---|
| 504 |
<tt>on_repress()</tt> handler for each Unit in the Sandbox.</p> |
|---|
| 505 |
|
|---|
| 506 |
<p><b>Warning:</b> You should <b>NOT</b> call <tt>flush_all()</tt> |
|---|
| 507 |
indiscriminately. You will rapidly get into concurrency trouble. You |
|---|
| 508 |
can stay out of trouble following an easy rule: call <tt>flush_all</tt> |
|---|
| 509 |
only at the end of your client's connection.</p> |
|---|
| 510 |
|
|---|
| 511 |
<p>If you want the "hard" rule, here it is. If you flush any Unit class, |
|---|
| 512 |
then any instances of that class hanging around need to be re-recalled. If |
|---|
| 513 |
you don't re-recall them, then any changes you make to the old instance |
|---|
| 514 |
won't be saved on the next flush, since flushing only iterates through |
|---|
| 515 |
units in the sandbox. For example, if you do this:</p> |
|---|
| 516 |
|
|---|
| 517 |
<pre>box = arena.new_sandbox() |
|---|
| 518 |
thing = box.unit(Thing) |
|---|
| 519 |
box.flush_all() |
|---|
| 520 |
thing.Size += 12 |
|---|
| 521 |
box.flush_all() |
|---|
| 522 |
</pre> |
|---|
| 523 |
|
|---|
| 524 |
<p>...then the change you make to "Size" won't be persisted, since the |
|---|
| 525 |
Thing object is no longer in the sandbox--it's been flushed out. You have |
|---|
| 526 |
to recall it somehow to get it stuck in the sandbox again. You could go |
|---|
| 527 |
through all kinds of gyrations to save the old units directly to storage, |
|---|
| 528 |
but don't bother. Just get new references to them and save yourself a lot |
|---|
| 529 |
of headache.</p> |
|---|
| 530 |
|
|---|
| 531 |
<h4>Views</h4> |
|---|
| 532 |
<p>Sandboxes provide a <tt class='def'>view(cls, attrs, expr=None)</tt> function. |
|---|
| 533 |
This returns values, rather than Units. Put simply, it yields all values |
|---|
| 534 |
for the given attribute(s) of the Unit class provided; each unit will |
|---|
| 535 |
yield a tuple of its values in the same order as the <tt>attrs</tt> |
|---|
| 536 |
sequence you provide. Providing an expr argument (an <tt>Expression</tt> |
|---|
| 537 |
object, see below) will filter the set of Units before obtaining the |
|---|
| 538 |
value tuples.</p> |
|---|
| 539 |
|
|---|
| 540 |
<pre>>>> v = sandbox.view(zoo.Animal, ['Name', 'Lifespan']) |
|---|
| 541 |
>>> [row for row in v] # or list(v), or iterate over v... |
|---|
| 542 |
[('Leopard', 73.5), |
|---|
| 543 |
('Slug', .75), |
|---|
| 544 |
('Tiger', None), |
|---|
| 545 |
('Lion', None), |
|---|
| 546 |
('Bear', None), |
|---|
| 547 |
('Ostrich', 103.2), |
|---|
| 548 |
('Centipede', None), |
|---|
| 549 |
('Emperor Penguin', None), |
|---|
| 550 |
('Adelie Penguin', None), |
|---|
| 551 |
('Millipede', None) |
|---|
| 552 |
] |
|---|
| 553 |
</pre> |
|---|
| 554 |
|
|---|
| 555 |
<p>In this example (pulled from the "zoo" test suite), we grab the name |
|---|
| 556 |
and Lifespan for each Animal. The <tt>attrs</tt> argument must always |
|---|
| 557 |
be an iterable.</p> |
|---|
| 558 |
|
|---|
| 559 |
<p>Sandboxes also provide a <tt class='def'>distinct(cls, attrs, expr=None)</tt> |
|---|
| 560 |
function. This works just like <tt>view()</tt>, but returns distinct |
|---|
| 561 |
tuples rather than all tuples.</p> |
|---|
| 562 |
|
|---|
| 563 |
<p>The <tt>distinct</tt> function can also be used as a <tt>count</tt> |
|---|
| 564 |
function by passing <tt>attrs = [prop.key for prop in cls.identifiers]</tt>. |
|---|
| 565 |
Sandboxes provide a <tt class='def'>count(cls, expr)</tt> method which does |
|---|
| 566 |
just this.</p> |
|---|
| 567 |
|
|---|
| 568 |
|
|---|
| 569 |
<a name='associations'><h3>Associations between Unit Classes</h3></a> |
|---|
| 570 |
<p>Once you've put together some Unit classes, chances are you're going to |
|---|
| 571 |
want to associate them. Generally, this is accomplished by creating a |
|---|
| 572 |
property in the Unit_B class which stores IDs of Unit_A objects (which |
|---|
| 573 |
might be called <i>foreign keys</i> in a database context). |
|---|
| 574 |
<pre>class Archaeologist(Unit): |
|---|
| 575 |
Height = UnitProperty(float) |
|---|
| 576 |
|
|---|
| 577 |
class Biography(Unit): |
|---|
| 578 |
ArchID = UnitProperty(int) |
|---|
| 579 |
PubDate = UnitProperty(datetime.date)</pre> |
|---|
| 580 |
In this example, each <tt>Biography</tt> object will have an <tt>ArchID</tt> |
|---|
| 581 |
attribute, which will equal the <tt>ID</tt> of some <tt>Archaeologist</tt>. |
|---|
| 582 |
In Dejavu terms, we say that there is a <i>near class</i> (with a <i>near |
|---|
| 583 |
key</i>) and a <i>far class</i> (with a <i>far key</i>). Associations in |
|---|
| 584 |
Dejavu are not one-way, so it doesn't matter which class you choose for the |
|---|
| 585 |
"near" one and which for the "far" one.</p> |
|---|
| 586 |
|
|---|
| 587 |
<p>You could stop at this point in your design, and simply remember what |
|---|
| 588 |
these keys are and how they relate, and manipulate them accordingly. But |
|---|
| 589 |
Dejavu allows you to explicitly declare these associations: |
|---|
| 590 |
<pre>Archaeologist.one_to_many('ID', Biography, 'ArchID')</pre> |
|---|
| 591 |
You pass in the the near key, the far class, and the far key. |
|---|
| 592 |
There are similar methods for one_to_one and many_to_one. In addition, |
|---|
| 593 |
there is a Unit.associate method which allows you to use your own |
|---|
| 594 |
relationship objects.</p> |
|---|
| 595 |
|
|---|
| 596 |
<p>What does an explicit association buy for you? First, you can associate |
|---|
| 597 |
Units without having to remember which keys are related. Second, Arenas |
|---|
| 598 |
discover associations and fill the <tt>Arena.associations</tt> registry, so |
|---|
| 599 |
that smart consumer code (like <a href='managing.html#unitenginerules'>Unit |
|---|
| 600 |
Engine Rules</a>) can automatically follow association paths for you. |
|---|
| 601 |
Third, each Unit class has a private <tt>_associations</tt> attribute, |
|---|
| 602 |
a <tt>dict</tt>. Each Unit involved in in the association gains an entry |
|---|
| 603 |
in that dict: the key is the far class name, |
|---|
| 604 |
and the value is a UnitAssociation instance, a non-data (method) descriptor, |
|---|
| 605 |
with additional nearClass, nearKey, farClass, farKey, and to_many attributes.</p> |
|---|
| 606 |
|
|---|
| 607 |
<h4><tt>Unit.add()</tt></h4> |
|---|
| 608 |
<p>Once two classes have been associated, you attach Unit <i>instances</i> |
|---|
| 609 |
to each other by equating their associated properties. That was a |
|---|
| 610 |
mouthful. Here's an example: |
|---|
| 611 |
<pre>>>> evbio = Biography() |
|---|
| 612 |
>>> evbio.ArchID = Eversley.ID |
|---|
| 613 |
</pre> |
|---|
| 614 |
The two unit <i>instances</i> (evbio and Eversley) are now associated |
|---|
| 615 |
(only their <i>classes</i> were before). Keep in mind that many Unit |
|---|
| 616 |
instances need to be memorized in order to obtain an ID.</p> |
|---|
| 617 |
|
|---|
| 618 |
<p>Rather than forcing you to remember all of the related classes and keys, |
|---|
| 619 |
Dejavu Units all have an <tt>add</tt> method, which does the same thing: |
|---|
| 620 |
<pre>>>> evbio = Biography() |
|---|
| 621 |
>>> evbio.add(Eversley) |
|---|
| 622 |
</pre> |
|---|
| 623 |
The <tt>add</tt> method works in either direction, so you could just as |
|---|
| 624 |
well write: |
|---|
| 625 |
<pre>>>> evbio = Biography() |
|---|
| 626 |
>>> Eversley.add(evbio) |
|---|
| 627 |
</pre> |
|---|
| 628 |
The <tt>add</tt> method will take any number of unit instances as |
|---|
| 629 |
arguments, and add each one in turn. That is: |
|---|
| 630 |
<pre> |
|---|
| 631 |
>>> evbio1 = Biography() |
|---|
| 632 |
>>> evbio2 = Biography() |
|---|
| 633 |
>>> evbio3 = Biography() |
|---|
| 634 |
>>> Eversley.add(evbio1, evbio2, evbio3) |
|---|
| 635 |
</pre> |
|---|
| 636 |
</p> |
|---|
| 637 |
|
|---|
| 638 |
<h4>"Related units" methods</h4> |
|---|
| 639 |
<p>To make querying easier, each of the two Unit classes will gain a new |
|---|
| 640 |
"related units" method which simplifies looking up related instances |
|---|
| 641 |
of the other class. The new method for Unit_B will have the name of Unit_A, |
|---|
| 642 |
and vice-versa. In our example: |
|---|
| 643 |
<pre>>>> Archaeologist.Biography |
|---|
| 644 |
<unbound method Archaeologist.related_units> |
|---|
| 645 |
>>> Eversley = Archaeologist(Height=6.417) |
|---|
| 646 |
>>> Eversley.Biography |
|---|
| 647 |
<bound method Archaeologist.related_units of <__main__.Archaeologist |
|---|
| 648 |
object at 0x011A1930>> |
|---|
| 649 |
>>> bios = Eversley.Biography() |
|---|
| 650 |
>>> bios |
|---|
| 651 |
[<arch.Biography object at 0x01158E10>, |
|---|
| 652 |
<arch.Biography object at 0x0118B350>, |
|---|
| 653 |
<arch.Biography object at 0x0118B170>] |
|---|
| 654 |
>>> evbio1.Archaeologist() |
|---|
| 655 |
<__main__.Archaeologist object at 0x011A1930> |
|---|
| 656 |
</pre> |
|---|
| 657 |
We've only created three Biographies at this point, so we can print the list |
|---|
| 658 |
easily. At the other extreme (when you have hundreds of Biographies to filter), |
|---|
| 659 |
you can pass an optional <tt>Expression</tt> object to the "related units" method. |
|---|
| 660 |
When you do, the list of associated Units will be filtered accordingly.</p> |
|---|
| 661 |
|
|---|
| 662 |
<p>Notice that, because our relationship is one-to-many, <b>the two |
|---|
| 663 |
"related units" methods behave differently</b>. The "one" |
|---|
| 664 |
(Archaeologist) which is retrieving the "many" (Biography) retrieves |
|---|
| 665 |
a list. The "many" retrieving the "one" retrieves a single Unit. |
|---|
| 666 |
When retrieving "to-one", the result will always be a single Unit |
|---|
| 667 |
(or None if there is no matching Unit). When retrieving "to-many", |
|---|
| 668 |
the result will always be a list, (it will be empty if there are |
|---|
| 669 |
no matches). This is new for Dejavu 1.4 (previously, they both |
|---|
| 670 |
would have returned lists).</p> |
|---|
| 671 |
|
|---|
| 672 |
<p>Because the "related units" method names are formed automatically, you need |
|---|
| 673 |
to take care not to use the names of Unit classes for your Unit properties. |
|---|
| 674 |
In our example, we used "ArchID" for the name of our "foreign key". If we |
|---|
| 675 |
had used "Archaeologist" instead, we would have had problems; when we |
|---|
| 676 |
associated the classes, the <i>property</i> named "Archaeologist" would |
|---|
| 677 |
have collided with the <i>"related units" method</i> named "Archaeologist". |
|---|
| 678 |
Be careful when naming your properties, and plan for the future. The best |
|---|
| 679 |
approach is probably to end your property name with "ID" every time.</p> |
|---|
| 680 |
|
|---|
| 681 |
<p>Unlike some other ORM's, Dejavu doesn't cache far Units within the near |
|---|
| 682 |
Unit. Each time you call the "related units" method, the data is recalled |
|---|
| 683 |
from your Sandbox. It is quite probable that those far Units are still |
|---|
| 684 |
sitting in memory in the Sandbox, but they're not going to persist in |
|---|
| 685 |
the near Unit itself in any way.</p> |
|---|
| 686 |
|
|---|
| 687 |
<p>Finally, some of you may want to override the default "related units" |
|---|
| 688 |
methods. Feel free; <tt>Unit.associate</tt> takes two optional arguments, |
|---|
| 689 |
which should be subclasses of the UnitAssociation descriptor. See the |
|---|
| 690 |
source code for more information.</p> |
|---|
| 691 |
|
|---|
| 692 |
<h4>Custom Unit Associations</h4> |
|---|
| 693 |
|
|---|
| 694 |
<p>Sometimes you need an association between two classes that is more complicated. |
|---|
| 695 |
For example, you might have an Archaeologist object and want to retrieve |
|---|
| 696 |
just their <i>last</i> Biography. Here's an example of how to do this: |
|---|
| 697 |
<pre>class LastBiographyAssociation(dejavu.UnitAssociation): |
|---|
| 698 |
"""Unit Association to relate an Archaeologist to their last Biography.""" |
|---|
| 699 |
|
|---|
| 700 |
to_many = False |
|---|
| 701 |
register = False |
|---|
| 702 |
|
|---|
| 703 |
def related(self, unit, expr=None): |
|---|
| 704 |
bios = unit.Biography() |
|---|
| 705 |
if bios: |
|---|
| 706 |
bios.sort(dejavu.sort("PubDate")) |
|---|
| 707 |
return bios[-1] |
|---|
| 708 |
return None |
|---|
| 709 |
|
|---|
| 710 |
descriptor = LastBiographyAssociation(u'ID', Biography, u'ID') |
|---|
| 711 |
descriptor.nearClass = Archaeologist |
|---|
| 712 |
Archaeologist._associations["Last Biography"] = descriptor</pre> |
|---|
| 713 |
|
|---|
| 714 |
There are a couple of things to note, here. We are basically doing by |
|---|
| 715 |
hand what the <tt>associate</tt> method does for you automatically, but |
|---|
| 716 |
that method makes <i>two</i> associations (one in each direction), and |
|---|
| 717 |
we're only making one. The <tt class='def'>related(unit, expr)</tt> |
|---|
| 718 |
method is overridden to do the actual lookup of far units. Because the |
|---|
| 719 |
<tt>to_many</tt> attribute is False, <tt>related</tt> returns a single |
|---|
| 720 |
Unit, or None. Finally, the <tt>register</tt> attribute, when False, |
|---|
| 721 |
keeps the Arena from registering this association in its graph |
|---|
| 722 |
(see <a href='#registering'>Registering</a>, below).</p> |
|---|
| 723 |
|
|---|
| 724 |
<h3>The Arena Object</h3> |
|---|
| 725 |
<p>The topmost class in Dejavu is the <tt>Arena</tt> class. When building |
|---|
| 726 |
a Dejavu application, you must first create an instance of this class, |
|---|
| 727 |
and must find a way to persist this object across client connections. |
|---|
| 728 |
This can be achieved in multiple ways; web applications, for example, |
|---|
| 729 |
will typically create a single process to serve all requests. Desktop |
|---|
| 730 |
applications will probably create a single Arena object for each |
|---|
| 731 |
running instance of the program.</p> |
|---|
| 732 |
|
|---|
| 733 |
<h4>Loading Stores</h4> |
|---|
| 734 |
<p>You <b>may</b> manually set up Storage Managers by calling |
|---|
| 735 |
<tt>Arena().add_store(name, store, options)</tt>. But, you |
|---|
| 736 |
probably shouldn't. Instead, allow your deployers to decide for |
|---|
| 737 |
themselves which storage solution(s) to use. You can do this by calling |
|---|
| 738 |
<tt>load(filename)</tt>; pass it the filename of an INI-style file |
|---|
| 739 |
which your deployers can tweak without screwing up your Python code. |
|---|
| 740 |
The <a href='storage.html'>next chapter</a> in this reference is completely |
|---|
| 741 |
devoted to educating deployers; point them to it or copy/modify it in your |
|---|
| 742 |
own release docs.</p> |
|---|
| 743 |
|
|---|
| 744 |
<a name='registering'><h4>Registering Unit Classes</h4></a> |
|---|
| 745 |
<p>The <tt>Arena</tt> object maintains a registry of Unit classes |
|---|
| 746 |
(<tt>._registered_classes</tt>). You shouldn't manipulate this structure |
|---|
| 747 |
on your own; instead, use the <tt>register</tt> or <tt>register_all</tt> |
|---|
| 748 |
methods to register each Unit class.</p> |
|---|
| 749 |
|
|---|
| 750 |
<p>The <tt>Arena</tt> object also manages the associations between Unit |
|---|
| 751 |
classes in its <tt>associations</tt> attribute, which is a simple, |
|---|
| 752 |
unweighted, undirected graph. Whenever you register a class, the Arena |
|---|
| 753 |
will add its associations to this graph. The only other common operation |
|---|
| 754 |
is to call <tt>.associations.shortest_path(start, end)</tt>, to retrieve |
|---|
| 755 |
the chain of associations between two Unit classes.</p> |
|---|
| 756 |
|
|---|
| 757 |
<h4>Managing Schema Changes</h4> |
|---|
| 758 |
<p>The <tt>Schema</tt> class helps you manage changes to your Dejavu model |
|---|
| 759 |
throughout its lifetime. For example, let's say that we deploy our |
|---|
| 760 |
Archaeology-Biography application at various libraries around the world. |
|---|
| 761 |
After a year, one of the developers wishes to implement a new reporting |
|---|
| 762 |
feature; however, it would be easiest to build if the Unit Property names |
|---|
| 763 |
could be exposed to the users. Unfortunately, our "ArchID" property on the |
|---|
| 764 |
Biography class isn't very informative. It would be better if we could |
|---|
| 765 |
rename that to "ArchaeologistID":</p> |
|---|
| 766 |
|
|---|
| 767 |
<pre>class ArchBioSchema(dejavu.Schema): |
|---|
| 768 |
|
|---|
| 769 |
guid = 'da39a3ee5e6b4b0d3255bfef95601890afd80709' |
|---|
| 770 |
latest = 2 |
|---|
| 771 |
|
|---|
| 772 |
def upgrade_to_2(self): |
|---|
| 773 |
self.arena.rename_property(Biography, "ArchID", "ArchaeologistID") |
|---|
| 774 |
|
|---|
| 775 |
abs = ArchBioSchema(arena) |
|---|
| 776 |
abs.upgrade() |
|---|
| 777 |
</pre> |
|---|
| 778 |
|
|---|
| 779 |
<p>Assuming we've already made the change to our model, the above example |
|---|
| 780 |
renames the property in the persistence layer (the database). There are |
|---|
| 781 |
also <tt class='def'>add_property(cls, name)</tt> and |
|---|
| 782 |
<tt class='def'>drop_property(cls, name)</tt> methods. There may be more |
|---|
| 783 |
methods in future versions of Dejavu; this feature is pretty new.</p> |
|---|
| 784 |
|
|---|
| 785 |
<p>The example also declares this change to be "version 2" of our schema. |
|---|
| 786 |
If you examine the base Schema class, you will see that it already has an |
|---|
| 787 |
<tt>upgrade_to_0</tt> method. The "zeroth" upgrade makes no schema changes; |
|---|
| 788 |
it merely marks all deployed databases with "version 0". I skipped version |
|---|
| 789 |
1 in the example, just in case I need some setup code in the future ;).</p> |
|---|
| 790 |
|
|---|
| 791 |
<p>If you call <tt class='def'>upgrade(version)</tt> with a version argument, |
|---|
| 792 |
then your deployment will be upgraded to that version. If no argument is |
|---|
| 793 |
given, the installation will be upgraded to <tt>Schema.latest</tt>. You |
|---|
| 794 |
can even skip steps (i.e. remove methods for broken steps) if it comes to |
|---|
| 795 |
that.</p> |
|---|
| 796 |
|
|---|
| 797 |
<p>The Schema superclass also has a <tt class='def'>stage</tt> attribute. |
|---|
| 798 |
While an upgrade is in process, this value will be an int, the same number |
|---|
| 799 |
as that of the upgrade method. That is, while upgrade_to_2 is running, |
|---|
| 800 |
<tt>stage</tt> will be 2. If no upgrade method is running, <tt>stage</tt> |
|---|
| 801 |
will be None.</p> |
|---|
| 802 |
|
|---|
| 803 |
<p>After you run <tt>upgrade</tt>, you can call the |
|---|
| 804 |
<tt class='def'>assert_storage</tt> method to tell Dejavu to create storage |
|---|
| 805 |
(tables in your database) for all the Unit classes registered in your arena. |
|---|
| 806 |
If storage already exists for a given class, it is skipped.</p> |
|---|
| 807 |
|
|---|
| 808 |
<p><b>Please note:</b> the installed version defaults to "latest". This |
|---|
| 809 |
allows new installs to skip all the upgrade steps, and just use the latest |
|---|
| 810 |
class definitions when they call <tt>assert_storage</tt>. However, it means |
|---|
| 811 |
that if you deploy your apps for a while without a Schema, and then |
|---|
| 812 |
introduce one later, you must manually decrement DeployedVersion from |
|---|
| 813 |
"latest" to the actual deployed version *before* running your app for the |
|---|
| 814 |
first time (or things will break due to the difference between the latest |
|---|
| 815 |
and deployed schema).</p> |
|---|
| 816 |
|
|---|
| 817 |
<h5>The DeployedVersion Unit</h5> |
|---|
| 818 |
|
|---|
| 819 |
<p>The <tt>Schema</tt> class uses a magic table in the database to keep |
|---|
| 820 |
track of each deployment's schema version. The Unit class is called |
|---|
| 821 |
"DeployedVersion", and it has ID and Version attributes.</p> |
|---|
| 822 |
|
|---|
| 823 |
<p>The ID attribute will be set to whatever your <tt>Schema.guid</tt> |
|---|
| 824 |
is. It's a simple way to isolate multiple installed Dejavu applications. |
|---|
| 825 |
A given application should use the same guid throughout its lifetime. |
|---|
| 826 |
I used <tt>sha.new().hexdigest()</tt> to generate the example. Feel free |
|---|
| 827 |
to use sha.new, a guid generator, a descriptive name, or whatever you |
|---|
| 828 |
like.</p> |
|---|
| 829 |
|
|---|
| 830 |
|
|---|
| 831 |
<hr /> |
|---|
| 832 |
|
|---|
| 833 |
</body> |
|---|
| 834 |
</html> |
|---|