Contact: fumanchu@aminus.org

Log in as guest/geniusql to create tickets

root/trunk/geniusql/doc/intro.html

Revision 196 (checked in by fumanchu, 5 years ago)

Doc update (db.create).

Line 
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>Geniusql: Introduction</title>
8     <link href='geniusql.css' rel='stylesheet' type='text/css' />
9 </head>
10
11 <body>
12
13 <h2>Introduction</h2>
14
15 <p>Geniusql is a thread-safe Object-Relational Mapper for Python applications.
16 It is designed to provide the "Model" third of an MVC application. When you
17 build an application using Geniusql, you must supply the Controller(s) and
18 View(s) yourself. Geniusql does not provide these, and does its best to not
19 limit 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 Geniusql 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. ;) It primarily uses a Row Data Gateway architecture.</p>
25
26 <h3>Basic Structure</h3>
27 <p>Developers build their Model by creating instances of Schema, Table,
28 Column, and Index classes. For OLTP applications, Table objects expose
29 the methods for managing individual rows: insert, select, save, and delete.
30 </p>
31
32 <p>For OLAP applications, the Database exposes more generic select methods.
33 Table instances can be <i>associated</i> to other Tables. This means
34 that one of the Columns of TableA maps to one of the Columns of TableB.
35 Related data may then be looked up more easily.</p>
36
37 <p>The results of a select call are usually dicts, where each Column in the
38 result has a corresponding key/value pair in the dict. Complex queries can
39 be written as pure Python lambdas, and Geniusql will decompile them into SQL.
40 </p>
41
42
43 <h3>Simple Example</h3>
44
45 <p>Since a block of code is often worth a thousand words, here's a minimal
46 example of a Geniusql application:</p>
47
48 zookeeper.py
49 <pre>import geniusql
50
51 # Set up a global Schema and Database object.
52 conf = {'connections.Connect':
53         r"PROVIDER=MICROSOFT.JET.OLEDB.4.0;DATA SOURCE=C:\zookeeper.mdb;"}
54 db = geniusql.db("msaccess", **conf)
55 db.create()
56 schema = db.schema("main")
57 schema.create()
58
59 Zoo = schema.table('Zoo')
60 Zoo['ID'] = schema.column(int, autoincrement=True, key=True)
61 Zoo.add_index('ID')
62 Zoo['Name'] = schema.column()
63 Zoo['Size'] = schema.column(int)
64 Zoo['Opens'] = schema.column(datetime.time)
65 Zoo['LastEscape'] = schema.column(datetime.datetime)
66 schema['Zoo'] = Zoo
67
68 Animal = schema.table('Animal')
69 Animal['Legs'] = schema.column(int, default=4)
70 Animal['Name'] = schema.column()
71 Animal['ZooID'] = schema.column(int)
72 Animal.references['Zoo'] = ('ZooID', 'Zoo', 'ID')
73 schema['Animal'] = Animal
74 </pre>
75
76 <p>The above creates the model for the zookeeper application.
77 There are three basic things happening:
78     <ol>
79         <li>The <tt>Zoo</tt> and <tt>Animal</tt> tables, which are instances
80             of <tt>geniusql.Table</tt>. These will correspond to the Zoo and
81             Animal tables within the database.</li>
82         <li>The association between the Animal class and the Zoo class.</li>
83         <li>The setup of a geniusql <tt>Schema</tt> object, including a
84             <tt>Database</tt> which uses Microsoft Access (Jet).</li>
85     </ol>
86 </p>
87
88 <p>Here's a simple interactive session which uses the above (assume that
89 tables have been created and populated elsewhere):</p>
90
91 <pre>>>> import zookeeper
92 >>> Animal.select_all()
93 [{'ID': 1, 'Name': 'Lion', 'Legs': 4, 'ZooID': 1},
94  {'ID': 2, 'Name': 'Leopard', 'Legs': 4, 'ZooID': 1},
95  {'ID': 3, 'Name': 'Centipede', 'Legs': 100, 'ZooID': 13},
96  {'ID': 4, 'Name': 'Slug', 'Legs': 1, 'ZooID': 5},
97  ]
98 >>> Zoo.select_all()
99 []
100 >>> sdz = Zoo.insert(Name='San Diego Zoo', Size='38')
101 >>> sdz['ID']
102 1
103 >>> for creature in Animal.select_all():
104         Zoo.save(ID=creature['ID'], ZooID=sdz['ID'])
105 >>> len(Animal.select_all(ZooID=sdz['ID']))
106 4</pre>
107
108
109 <h3>Design Goals</h3>
110
111 <p>Geniusql is designed to function in environments with complex integration
112 needs, and tends to separate concerns as much as possible. In particular,
113 Geniusql tries to avoid making decisions in the framework which are better
114 left to developers. Some of those decisions are:
115     <ul>
116         <li>User interface. Geniusql works well in all sorts of applications,
117             whether desktop, thin-client or web.</li>
118         <li>Application package architecture. You can place your application
119             within a single Python module, develop complete packages,
120             or use Geniusql inside a larger framework.</li>
121         <li>Which types to use for Columns. Builtin types
122             are fully supported out of the box, including datetime and
123             decimal. Tim Peters' excellent <tt>fixedpoint</tt> module
124             is also available. New types are easily added.</li>
125         <li>Which keys to use when associating Tables.</li>
126         <li>What to name identifiers.</li>
127     </ul>
128
129 In the same way, Geniusql tries to avoid having developers make decisions
130 which are better left to deployers. Some of those decisions are:
131     <ul>
132         <li>Where (and how) to log error messages.</li>
133         <li>Which database(s) to use.</li>
134     </ul>
135 </p>
136
137
138 <h3>Obtaining and Installing</h3>
139
140 <p>You can obtain Geniusql from its Subversion repository at
141 <tt>http://projects.amor.org/geniusql/svn/trunk</tt>. Geniusql is designed
142 to be installed in <tt>site-packages/geniusql</tt> or some other root
143 python path.</p>
144
145 <p>Geniusql was built using Python 2.4.2. You should probably use
146 at least 2.3; Geniusql depends upon the <tt>datetime</tt> module.
147 Although Geniusql <i>supports</i> additional modules like
148 <tt>fixedpoint</tt> and <tt>decimal</tt>, it does not <i>require</i>
149 them.</p>
150
151 <p>Geniusql uses bytecode hacks, and therefore requires CPython
152 <a href='#cpython'>[2]</a>.</p>
153
154 <h3>Compared To Other Database Wrappers</h3>
155 <h4>SQLObject</h4>
156 <p>No matter what project I start on, odds are I'll discover that Ian
157 Bicking has already done the same thing, usually better.
158 <br />See http://blog.ianbicking.org/another-less-sleepy-alternative-to-hibernate.html
159 <br />Which was a reply to Ruby's ActiveRecord:
160 http://www.loudthinking.com/arc/000297.html
161 <br />Which was a reply to Java's Hibernate:
162 http://informit.com/guides/content.asp?g=java&seqNum=127&f1=rss</p>
163
164 <p>Using Geniusql, the application developer supplies the following code
165 to define the Tables and their relationships:</p>
166
167 <pre>from geniusql import *
168 import fixedpoint   # or decimal, for Python 2.4+
169 import datetime
170
171 conf = {'connections.Connect':
172         r"host=localhost dbname=bookstore user=postgres password=****"}
173 db = geniusql.db("postgres", **conf)
174 db.create()
175 schema = db.schema("main")
176 schema.create()
177
178 Book = schema.table('Book')
179 Book['ID'] = schema.column(int, autoincrement=True, key=True)
180 Book['title'] = schema.column(str)
181 Book['price'] = schema.column(fixedpoint.Fixedpoint)
182 Book['publishDate'] = schema.column(datetime.datetime)
183 Book['publisher'] = schema.column(int)
184
185 def addAuthor(book, author):
186     Authorship.insert(authorID=author['ID'], bookID=book['ID'])
187
188 Publisher = schema.table('Publisher')
189 Publisher['ID'] = schema.column(int, autoincrement=True, key=True)
190 Publisher['name'] = schema.column(str)
191
192 Author = schema.table('Author')
193 Author['ID'] = schema.column(int, autoincrement=True, key=True)
194 Author['name'] = schema.column(str)
195
196 Authorship = schema.table('Authorship')
197 Authorship['ID'] = schema.column(int, autoincrement=True, key=True)
198 Authorship['authorID'] = schema.column(int)
199 Authorship['bookID'] = schema.column(int)
200
201 Book.references['Publisher'] = ('publisher', 'Publisher', 'ID')
202 Authorship.references['Book'] = ('bookID', 'Book', 'ID')
203 Authorship.references['Author'] = ('authorID', 'Author', 'ID')
204 </pre>
205
206 <p>To create the tables, attach them to the schema object:</p>
207 <pre>for t in (Book, Publisher, Author, Authorship):
208     schema[t.name] = t</pre>
209
210 <p>The app developer's runtime code reads as follows:</p>
211 <pre>
212 ppython = Book.insert(title='Programming Python', price=20,
213                       publishDate=datetime.datetime(2001, 3, 1))
214 print ppython['title'] # output: 'Programming Python'
215
216 mlutz = Author.insert(name = 'Mark Lutz')
217 addAuthor(ppython, mlutz)
218
219 print len(Authorship.select_all(bookID=ppython['ID'])) # output: 1
220
221 def author_names(book):
222     names = [name for (name, )
223              in db.select_all(Authorship >> Author,
224                               [None, ('name', )],
225                               lambda aship, a: aship.bookID == ppython['ID']
226                               )]
227     return u', '.join(names)
228
229 print author_names(ppython) # output: 'Mark Lutz'
230
231 oreilly = Publisher.insert(name="O'Reilly")
232
233 Book.save(ID=ppython['ID'], publisher=oreilly['ID'])
234 print Publisher.select(ID=ppython['publisher'])['name'] # output: "O'Reilly"
235
236 print len(Book.select_all(authorID=oreilly['ID'])) # output: 1
237
238 print 'Hi,', author_names(Book.select(authorID=oreilly['ID'])) # output: "Hi, Mark Lutz"
239 </pre>
240 </p>
241
242 <hr />
243
244 <p><a name='fowler'>[1]</a> Fowler,
245 <a href='http://www.martinfowler.com/eaaCatalog/identityMap.html'>Patterns
246 of Enterprise Application Architecture</a>.<br />
247 <a name='cpython'>[2]</a> Geniusql relies upon bytecode hacking to achieve
248 its clean lambda syntax for data queries. Therefore, it is CPython-specific.
249 In addition, the bytecode of Python may change from one version of Python
250 to another; if you find your version of Python does not work with Geniusql's
251 <tt>codewalk</tt> and <tt>logic</tt> modules, please let me know.<br />
252 </p>
253
254 </body>
255 </html>
Note: See TracBrowser for help on using the browser.