Project

General

Profile

AQuickTutorial » History » Version 9

Aurynn Shaw, 07/20/2010 10:31 AM
Updating docs for 0.3.1.

1 9 Aurynn Shaw
h1. Simpycity for People in a Hurry
2 8 Aurynn Shaw
3 1 Aurynn Shaw
And what to expect when you use it.
4
5
6 9 Aurynn Shaw
h2. Beginning
7 1 Aurynn Shaw
8 8 Aurynn Shaw
9 9 Aurynn Shaw
The first step, once you've installed Simpycity, is to instance a Context. Everything in Simpycity derives from the base Context types, which keeps the basic connection handle consistent across all derived objects.
10 1 Aurynn Shaw
11 9 Aurynn Shaw
To initialize a context:
12 8 Aurynn Shaw
<pre>
13 9 Aurynn Shaw
>>> from simpycity.context import Context
14
>>> ctx = Context("dbname=your_db user=aurynn password=12345 host=localhost port=5432")
15
</pre>
16 1 Aurynn Shaw
17 9 Aurynn Shaw
As you can see, the startup semantics are identical to the underlying psycopg2 DSN string.
18 7 Aurynn Shaw
19 1 Aurynn Shaw
20 9 Aurynn Shaw
h2. Some Functions
21
22
The power of Simpycity comes from being able to treat database objects and queries as callables - cleanly mapping Python semantics to PostgreSQL query semantics, without all the database interaction getting in the way.
23
24
To initialize functions, simply:
25 1 Aurynn Shaw
<pre>
26 9 Aurynn Shaw
>>> f = ctx.Function("get_row",['id'])
27
>>> f_all = ctx.Function("get_rows")
28 1 Aurynn Shaw
</pre>
29
30 9 Aurynn Shaw
In this, *f_all* maps to the stored procedure "get_rows", which takes no arguments.
31 1 Aurynn Shaw
*f*, on the other hand, maps to the stored procedure "get_row", which takes a single argument, which we've named id.
32
33 9 Aurynn Shaw
Argument names are completely arbitrary - they don't need to be named based on the underlying function arguments.
34
35 1 Aurynn Shaw
To call f_all, it's a standard python function:
36
<pre>
37
>>> all_results = f_all()
38
</pre>
39
40 9 Aurynn Shaw
For get_row, it's also a standard Python call, supporting both positional and keyword arguments:
41 1 Aurynn Shaw
<pre>
42
>>> result = f(1) # get id 1
43
>>> result = f(id=1) # also get id 1
44
</pre>
45
46 9 Aurynn Shaw
Result sets are handled as a List, which raises the side effect of being possible to exhaust memory by pulling the entire result set into memory. This needs to be approached with caution.
47
48
This does allow for normal iteration to occur:
49 1 Aurynn Shaw
<pre>
50 9 Aurynn Shaw
>>> for row in f_all():
51 1 Aurynn Shaw
...  # do row stuff
52
</pre>
53
54 9 Aurynn Shaw
In the case of a single result, by default, you'll end up with that one record, ONLY. This can cause confusion if you're not expecting it:
55 1 Aurynn Shaw
<pre>
56 9 Aurynn Shaw
>>> row = f(id=1) # row is a single row, not a list.
57
</pre> 
58
59
If you always expect a list, you should do instead:
60
61
<pre>
62
>>> f_all = ctx.Function("get_rows", returns_a="list")
63
</pre>
64
65
"returns_a" coercing Simpycity into always returning a list of objects, similarly to if you'd natively gotten a list of objects.
66
67
h2. Queries
68
69
Queries behave exactly the same as Functions, and have the same arguments:
70
<pre>
71
>>> q = ctx.Raw("SELECT * FROM your_table WHERE id = %s", ['id'])
72
</pre>
73
74
In all cases, %s mapping is handled by psycopg2, which performs correct parameterization.
75
76
h2. Models
77
78
Models in Simpycity are a minimalistic construct to facilitate easy generation of complex business objects without compromising the underlying database representation.
79
80
We generally advise using a single base Model class that all other models derive from, such as:
81
82
<pre>
83
>>> base = ctx.Model()
84
>>> class myModel(base):
85
...  pass
86
</pre>
87
88
This allows for model modifications to be made globally in your application.
89
90
h3. table
91
92
The table declaration in the model describes how the model is represented, and what items can be persisted to the database:
93
94
<pre>
95
>>> class myModel(base):
96
...   table = ['id','description']
97
</pre>
98
99
The declaration is intentionally simple, and (currently) we rely on psycopg2 and PostgreSQL doing the Right Thing when it comes to raising type coercion errors.
100
101
h3. __load__
102
103
__load__ is used by the Model object to handle loading from the database when the model is instanced with arguments, such as:
104
<pre>
105
>>> class myModel(base):
106
...   table = ['id','description']
107
...   __load__ = ctx.Raw("SELECT id, description FROM your_table WHERE id = %s", ['id'])
108
...
109
>>> m = myModel(1)
110
>>> n = myModel(id=1)
111
</pre>
112
113
__load__ can use any standard Simpycity callable.
114
115
h3. __save__
116
117
__save__ allows for the ActiveRecord pattern of "m.save()" to work on Simpycity Models, allowing for utterly arbitrary control of the underlying save mechanism.
118
119
<pre>
120
>>> class myModel(base):
121
...   table = ['id','description']
122
...   __load__ = ctx.Raw("SELECT id, description FROM your_table WHERE id = %s", ['id'])
123
...   __save__ = ctx.Function("update_my_table",['id','description'])
124
>>> m = myModel(1)
125
>>> m.description = "cheese"
126
>>> m.save()
127
>>> ctx.commit()
128
</pre>
129
130
Yes, you can change the id value on the model, however, this would merely break the model and prevent it from saving as expected.
131
If your underlying sproc was sufficiently intelligent, it could be coerced into doing an *UPSERT*, thus preventing any issues from occurring.
132
133
h3. new
134
135
The final component of sensible models is the "new" structure. Simpycity does not directly support this, and requires either intelligent sprocs, or a simple pattern:
136
137
<pre>
138
>>> class myModel(base):
139
...   table = ['id','description']
140
...   __load__ = ctx.Raw("SELECT id, description FROM your_table WHERE id = %s", ['id'])
141
...   __save__ = ctx.Function("update_my_table",['id','description'])
142
...
143
>>> new = ctx.Function("new_table_row",['description'], return_type=myModel)
144
</pre>
145
146
return_type allows a Simpycity callable to declare that it returns the provided Model definition. This allows for the pattern of:
147
148
<pre>
149
>>> from models import mymodel
150
>>> m = mymodel.myModel
151
>>> m = mymodel.new("a test")
152
>>> m.description
153
'a test'
154
>>> m.description = "Test successful"
155
>>> m.save()
156
>>> ctx.commit()
157 1 Aurynn Shaw
</pre>