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> |