| 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/TR/xhtml1/strict" xml:lang="en" lang="en"> |
|---|
| 4 |
|
|---|
| 5 |
<head> |
|---|
| 6 |
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> |
|---|
| 7 |
<title>Dejavu: Introduction</title> |
|---|
| 8 |
<link href='dejavu.css' rel='stylesheet' type='text/css' /> |
|---|
| 9 |
</head> |
|---|
| 10 |
|
|---|
| 11 |
<body> |
|---|
| 12 |
|
|---|
| 13 |
<h2>Introduction</h2> |
|---|
| 14 |
|
|---|
| 15 |
<p>Dejavu is an Object-Relational Mapper for Python applications. It is |
|---|
| 16 |
designed to provide the "Model" third of an MVC application. When you build |
|---|
| 17 |
an application using Dejavu, you must supply the Controller(s) and View(s) |
|---|
| 18 |
yourself. Dejavu does not provide these, and does its best to not limit |
|---|
| 19 |
your choices regarding them.</p> |
|---|
| 20 |
|
|---|
| 21 |
<p>If you're familiar with Martin Fowler's work <a href='#fowler'>[1]</a>, |
|---|
| 22 |
you can think of Dejavu as providing a Data Source layer, plus the tools |
|---|
| 23 |
to write your own Domain layer. For the Presentation layer, you're on your |
|---|
| 24 |
own. ;)</p> |
|---|
| 25 |
|
|---|
| 26 |
<h3>Basic Structure</h3> |
|---|
| 27 |
<p>Developers build their Model (or <i>schema</i>) by creating classes |
|---|
| 28 |
which subclass the <tt>Unit</tt> class; for those of you stuck in RDBMS |
|---|
| 29 |
mindsets, each Unit subclass corresponds to a table; instances of the class |
|---|
| 30 |
correspond to the rows. Each subclass possesses a set of attributes known |
|---|
| 31 |
as "properties", which you can think of as columns in your database |
|---|
| 32 |
table. These attributes are generally formed from a <tt>UnitProperty</tt> |
|---|
| 33 |
descriptor. Any Unit data which needs to be persisted ought to be contained |
|---|
| 34 |
in a Unit property. However, Unit classes can also possess arbitrary |
|---|
| 35 |
methods and attributes which aid their use within the application.</p> |
|---|
| 36 |
|
|---|
| 37 |
<p>Unit classes can be <i>associated</i> to other Unit classes. This means |
|---|
| 38 |
that one of the properties of UnitA maps to one of the properties of UnitB. |
|---|
| 39 |
Related objects may then be looked up more easily.</p> |
|---|
| 40 |
|
|---|
| 41 |
<p>Units are managed in memory by <tt>Sandbox</tt> objects, which function |
|---|
| 42 |
as "Identity Maps" <a href='#fowler'>[1]</a>; in-memory caches of Units |
|---|
| 43 |
which keep commit conflicts to a minimum. Unit objects can be "memorized" |
|---|
| 44 |
and "recalled" from a <tt>Sandbox</tt>, using pure Python lambda expressions |
|---|
| 45 |
<a href='#cpython'>[2]</a> as a query language. The lambda is wrapped |
|---|
| 46 |
in an <tt>Expression</tt> object to make it portable.</p> |
|---|
| 47 |
|
|---|
| 48 |
<p>Sandboxes persist Unit data by <tt>StorageManager</tt> objects. Each |
|---|
| 49 |
persistence mechanism has its own subclass of the <tt>StorageManager</tt> |
|---|
| 50 |
class; for example, persisting Unit data to a Microsoft SQL Server database |
|---|
| 51 |
requires a <tt>StorageManagerADO_SQLServer</tt> object. When recalling data, |
|---|
| 52 |
Storage Managers receive Expression objects; database SM's, for example, |
|---|
| 53 |
will typically examine these Expressions and produce SQL statements from |
|---|
| 54 |
them, which they then use to retrieve data. Storage Managers also handle |
|---|
| 55 |
the creation of new Units, and their destruction.</p> |
|---|
| 56 |
|
|---|
| 57 |
<p>Finally, Dejavu provides a core <tt>Arena</tt> class which you should be |
|---|
| 58 |
able to leverage for any sort of application you are building. The Arena |
|---|
| 59 |
object functions as a top-level "Application" object, collecting the global |
|---|
| 60 |
settings for an application into one place. It doles out Sandboxes, |
|---|
| 61 |
maintains a registry of Units and their associations, and manages startup |
|---|
| 62 |
and shutdown operations.</p> |
|---|
| 63 |
|
|---|
| 64 |
|
|---|
| 65 |
<h3>Simple Example</h3> |
|---|
| 66 |
|
|---|
| 67 |
<p>Since a block of code is often worth a thousand words, here's a minimal |
|---|
| 68 |
example of a Dejavu application:</p> |
|---|
| 69 |
|
|---|
| 70 |
zookeeper.py |
|---|
| 71 |
<pre>import dejavu |
|---|
| 72 |
from dejavu.storage.storeado import StorageManagerADO_MSAccess as SM |
|---|
| 73 |
|
|---|
| 74 |
# Set up a global Arena object. |
|---|
| 75 |
arena = dejavu.Arena() |
|---|
| 76 |
conf = {u'Connect': |
|---|
| 77 |
r"PROVIDER=MICROSOFT.JET.OLEDB.4.0;DATA SOURCE=C:\zookeeper.mdb;"} |
|---|
| 78 |
arena.add_store('main', SM("mySM", arena, conf)) |
|---|
| 79 |
|
|---|
| 80 |
|
|---|
| 81 |
class Zoo(dejavu.Unit): |
|---|
| 82 |
Name = dejavu.UnitProperty('Name', unicode) |
|---|
| 83 |
Size = dejavu.UnitProperty('Size', int) |
|---|
| 84 |
|
|---|
| 85 |
def total_legs(self): |
|---|
| 86 |
return sum([x.Legs for x in self.Animal()]) |
|---|
| 87 |
|
|---|
| 88 |
class Animal(dejavu.Unit): pass |
|---|
| 89 |
Animal.set_properties({"Name": unicode, |
|---|
| 90 |
"Legs": int, |
|---|
| 91 |
"ZooID": int, |
|---|
| 92 |
}) |
|---|
| 93 |
arena.associate(Zoo, 'ID', Animal, 'ZooID')</pre> |
|---|
| 94 |
|
|---|
| 95 |
<p>The above creates the model/schema for the zookeeper application. |
|---|
| 96 |
There are three basic things happening: |
|---|
| 97 |
<ol> |
|---|
| 98 |
<li>The <tt>Zoo</tt> and <tt>Animal</tt> classes, which subclass |
|---|
| 99 |
<tt>dejavu.Unit</tt>. These will correspond to the Zoo and |
|---|
| 100 |
Animal tables within the database. Notice the two different |
|---|
| 101 |
methods of setting Unit properties. Each class also inherits |
|---|
| 102 |
an 'ID' property (an int) from <tt>dejavu.Unit</tt>.</li> |
|---|
| 103 |
<li>The setup of a dejavu <tt>Arena</tt> object, including a Storage |
|---|
| 104 |
Manager which uses a Microsoft Access (Jet) database.</li> |
|---|
| 105 |
<li>The association between the Zoo class and the Animal class.</li> |
|---|
| 106 |
</ol> |
|---|
| 107 |
</p> |
|---|
| 108 |
|
|---|
| 109 |
<p>Here's a simple interactive session which uses the above:</p> |
|---|
| 110 |
|
|---|
| 111 |
<pre>>>> import zookeeper |
|---|
| 112 |
>>> box = zookeeper.arena.new_sandbox() |
|---|
| 113 |
>>> [x for x in box.recall(zookeeper.Animal)] |
|---|
| 114 |
[<zookeeper.Animal object at 0x013281F0>, <zookeeper.Animal object at 0x01328150>, |
|---|
| 115 |
<zookeeper.Animal object at 0x01328130>, <zookeeper.Animal object at 0x01328230>] |
|---|
| 116 |
>>> [x for x in box.recall(zookeeper.Zoo)] |
|---|
| 117 |
[] |
|---|
| 118 |
>>> zoo = zookeeper.Zoo(Name='San Diego Zoo', Size='38') |
|---|
| 119 |
>>> box.memorize(zoo) |
|---|
| 120 |
>>> zoo.ID |
|---|
| 121 |
1 |
|---|
| 122 |
>>> box.unit(zookeeper.Zoo, ID=1) is zoo |
|---|
| 123 |
True |
|---|
| 124 |
>>> for creature in box.recall(zookeeper.Animal): |
|---|
| 125 |
zoo.add(creature) |
|---|
| 126 |
>>> len(list(zoo.Animal())) |
|---|
| 127 |
4</pre> |
|---|
| 128 |
|
|---|
| 129 |
|
|---|
| 130 |
<h3>Design Goals</h3> |
|---|
| 131 |
|
|---|
| 132 |
<p>Dejavu is designed to function in environments with complex integration |
|---|
| 133 |
needs, and tends to separate concerns as much as possible. In particular, |
|---|
| 134 |
Dejavu tries to avoid making decisions in the framework which are better |
|---|
| 135 |
left to developers. Some of those decisions are: |
|---|
| 136 |
<ul> |
|---|
| 137 |
<li>Which interface style to use.</li> |
|---|
| 138 |
<li>Application package architecture. You can place your application |
|---|
| 139 |
within a single Python module, or develop complete packages.</li> |
|---|
| 140 |
<li>Which types to use for <tt>Unit</tt> properties.</li> |
|---|
| 141 |
<li>Which keys to use when associating <tt>Unit</tt> classes.</li> |
|---|
| 142 |
</ul> |
|---|
| 143 |
|
|---|
| 144 |
In the same way, Dejavu tries to avoid having developers make decisions |
|---|
| 145 |
which are better left to deployers. Some of those decisions are: |
|---|
| 146 |
<ul> |
|---|
| 147 |
<li>How to specify configuration data. Dejavu is equally happy |
|---|
| 148 |
obtaining configs from Python dictionaries, .ini files, |
|---|
| 149 |
or XML files. Additional methods are easy to develop.</li> |
|---|
| 150 |
<li>Where (and how) to log messages.</li> |
|---|
| 151 |
<li>Which storage mechanism (database or ...?) to use. In particular, |
|---|
| 152 |
deployers are allowed to mix and match stores, including how |
|---|
| 153 |
and when to cache objects in memory. Dejavu tries to make |
|---|
| 154 |
it easy for <i>deployers</i> to tune applications to their |
|---|
| 155 |
particular environment.</li> |
|---|
| 156 |
</ul> |
|---|
| 157 |
</p> |
|---|
| 158 |
|
|---|
| 159 |
<p>In particular, Dejavu was designed to make complex integration easier. |
|---|
| 160 |
Unlike most generic storage wrappers, Dejavu does not <i>require</i> you to |
|---|
| 161 |
have complete control of your back end. For example, consider Mission |
|---|
| 162 |
Control, the first application built on Dejavu. Mission Control required |
|---|
| 163 |
an ORM which transparently supported two very different backends. Half of |
|---|
| 164 |
the data was to be stored in an MS Access database (which is being migrated |
|---|
| 165 |
to SQL Server), over which the application developers had full control. |
|---|
| 166 |
But half of the data was stored in a third-party application, "The Raiser's |
|---|
| 167 |
Edge" (RE) from Blackbaud. RE provides read-only database access; all writes |
|---|
| 168 |
must go through their object-oriented API. Further, reading via that API was |
|---|
| 169 |
found to be too slow. Therefore, a custom Storage Manager (about 2500 lines |
|---|
| 170 |
of code) was developed, which searches for and loads objects via SQL, but |
|---|
| 171 |
writes Unit data via the REAPI. Dejavu allows the application logic to be |
|---|
| 172 |
completely ignorant of this complex mass of storage details. If Blackbaud |
|---|
| 173 |
closed its doors tomorrow, the solution could be quickly migrated to another |
|---|
| 174 |
data store; business downtime is reduced in the face of inevitable change.<p> |
|---|
| 175 |
|
|---|
| 176 |
<h3>Obtaining and Installing</h3> |
|---|
| 177 |
|
|---|
| 178 |
<p>You can obtain Dejavu from its Subversion repository at |
|---|
| 179 |
<tt>svn://casadeamor.com/dejavu/trunk</tt>. Dejavu expects to be |
|---|
| 180 |
installed in <tt>site-packages/dejavu</tt>.</p> |
|---|
| 181 |
|
|---|
| 182 |
<p>Dejavu depends upon two additional libraries, <tt>recur.py</tt> and |
|---|
| 183 |
<tt>xray.py</tt>, which are available at <tt>svn://casadeamor.com/misc</tt>. |
|---|
| 184 |
You need to place these two modules in your site-packages directory.</p> |
|---|
| 185 |
|
|---|
| 186 |
<p>Dejavu was built using Python 2.3.2. You should probably use |
|---|
| 187 |
at least 2.3; Dejavu depends upon the <tt>datetime</tt> module. |
|---|
| 188 |
Although Dejavu <i>supports</i> additional modules like |
|---|
| 189 |
<tt>fixedpoint</tt> and <tt>decimal</tt>, it does not <i>require</i> |
|---|
| 190 |
them.</p> |
|---|
| 191 |
|
|---|
| 192 |
<p>Dejavu uses bytecode hacks, and therefore requires CPython |
|---|
| 193 |
<a href='#cpython'>[2]</a>.</p> |
|---|
| 194 |
|
|---|
| 195 |
<h3>Compared To Other Database Wrappers</h3> |
|---|
| 196 |
<p>TBW</p> |
|---|
| 197 |
|
|---|
| 198 |
<hr /> |
|---|
| 199 |
|
|---|
| 200 |
<p><a name='fowler'>[1]</a> Fowler, |
|---|
| 201 |
<a href='http://www.martinfowler.com/eaaCatalog/identityMap.html'>Patterns |
|---|
| 202 |
of Enterprise Application Architecture</a>.<br /> |
|---|
| 203 |
<a name='cpython'>[2]</a> Dejavu relies upon bytecode hacking to achieve |
|---|
| 204 |
its clean lambda syntax for data queries. Therefore, it is CPython-specific. |
|---|
| 205 |
In addition, the bytecode of Python may change from one version of Python |
|---|
| 206 |
to another; if you find your version of Python does not work with Dejavu's |
|---|
| 207 |
<tt>codewalk</tt> and <tt>logic</tt> modules, please let me know.<br /> |
|---|
| 208 |
</p> |
|---|
| 209 |
|
|---|
| 210 |
</body> |
|---|
| 211 |
</html> |
|---|