1
1
""":py:mod:`postgres` implements an object-relational mapper based on
2
2
:py:mod:`psycopg2` composite casters. The fundamental technique, introduced by
3
3
`Michael Robbelard at PyOhio 2013`_, is to write SQL queries that typecast
4
- results to table types, and then use :py:mod:`psycopg2`
4
+ results to table types, and then use a :py:mod:`psycopg2`
5
5
:py:class:`~psycopg2.extra.CompositeCaster` to map these to Python objects.
6
6
This means we get to define our schema in SQL, and we get to write our queries
7
7
in SQL, and we get to explicitly indicate in our SQL queries how Python should
8
- map the result to an object , and then we can write Python objects that contain
8
+ map the results to objects , and then we can write Python objects that contain
9
9
only business logic and not schema definitions.
10
10
11
11
.. _Michael Robbelard at PyOhio 2013: https://www.youtube.com/watch?v=Wz1_GYc4GmU#t=25m06s
12
12
13
13
Every table in PostgreSQL has a type associated with it, which is the column
14
- definition for that table. These are composite types just like any other in
15
- PostgreSQL, meaning we can use them to cast query results. When we do, we get a
16
- single field that contains our query data, nested one level::
14
+ definition for that table. These are composite types just like any other
15
+ composite type in PostgreSQL, meaning we can use them to cast query results.
16
+ When we do, we get a single field that contains our query result, nested one
17
+ level::
17
18
18
19
test=# CREATE TABLE foo (bar text, baz int);
19
20
CREATE TABLE
68
69
:py:mod:`psycopg2` provides a :py:func:`~psycopg2.extras.register_composite`
69
70
function that lets us map PostgreSQL composite types to Python objects. This
70
71
includes table and view types, and that is the basis for
71
- :py:mod:`postgres.orm`.
72
+ :py:mod:`postgres.orm`. We map based on types, not tables.
72
73
73
74
74
75
ORM Tutorial
82
83
...
83
84
84
85
Your model must have a :py:attr:`typname` attribute, which is the name of the
85
- PostgreSQL type that this class is intended to be an object mapping for.
86
- (``typname`` is the name of the relevant column in the ``pg_type`` table in
87
- your database.)
86
+ PostgreSQL type for which this class is an object mapping. (``typname``,
87
+ spelled without an "e," is the name of the relevant column in the ``pg_type``
88
+ table in your database.)
88
89
89
90
Second, register your model with your :py:class:`~postgres.Postgres` instance:
90
91
91
92
>>> db.register_model(Foo)
92
93
93
94
That will plug your model into the :py:mod:`psycopg2` composite casting
94
- machinery, and you'll get instances of your model back from
95
- :py:meth:`~postgres.Postgres.all` and
96
- :py:meth:`~postgres.Postgres.one_or_zero`. If your query returns more than one
97
- column, you'll need to dereference the column containing the model just as with
98
- any other query:
95
+ machinery, and you'll now get instances of your model back from
96
+ :py:meth:`~postgres.Postgres.all` and :py:meth:`~postgres.Postgres.one_or_zero`
97
+ when you cast your query results to the relevant type. If your query returns
98
+ more than one column, you'll need to dereference the column containing the
99
+ model just as with any other query:
99
100
100
101
>>> rec = db.one_or_zero("SELECT foo.*::foo, bar.* "
101
102
... "FROM foo JOIN bar ON foo.bar = bar.bar "
105
106
>>> rec.bar
106
107
'blam'
107
108
108
- As a special case , if your query only returns one column, and it's a
109
- :py:class :`~postgres.orm.Model`, then :py:meth:`~postgres.Postgres.all` and
110
- :py:meth:`~postgres.Postgres.one_or_zero` will do the dereferencing for you:
109
+ As usual , if your query only returns one column, then
110
+ :py:meth :`~postgres.Postgres.all` and :py:meth:`~postgres.Postgres.one_or_zero`
111
+ will do the dereferencing for you:
111
112
112
113
>>> foo = db.one_or_zero("SELECT foo.*::foo FROM foo WHERE bar='blam'")
113
114
>>> foo.bar
116
117
['blam', 'whit']
117
118
118
119
120
+ The Model Base Class
121
+ --------------------
119
122
120
123
"""
121
124
from __future__ import print_function , unicode_literals
@@ -151,7 +154,17 @@ def __str__(self):
151
154
# =====
152
155
153
156
class Model (object ):
154
- """This is the base class for models under :py:mod:`postgres.orm`.
157
+ """
158
+
159
+ This is the base class for models in :py:mod:`postgres.orm`. Instances of
160
+ subclasses of :py:class:`~postgres.orm.Model` will have an attribute for
161
+ each field in the composite type for which the subclass is registered (for
162
+ table and view types, these will be the columns of the table or view).
163
+ These attributes are read-only. We don't update your database. You are
164
+ expected to do that yourself in methods on your subclass. To keep instance
165
+ attributes in sync after a database update, use the
166
+ :py:meth:`~postgres.orm.Model.update_attributes` helper.
167
+
155
168
"""
156
169
157
170
typname = None # an entry in pg_type
@@ -173,16 +186,18 @@ def __setattr__(self, name, value):
173
186
raise ReadOnly (name )
174
187
return super (Model , self ).__setattr__ (name , value )
175
188
176
- def update_local (self , ** kw ):
177
- """Update self.__dict__ with kw .
189
+ def update_attributes (self , ** kw ):
190
+ """Update instance attributes, according to :py:attr:`kw` .
178
191
179
192
:raises: :py:exc:`~postgres.orm.UnknownAttributes`
180
193
181
194
Call this when you update state in the database and you want to keep
182
- the attributes of your object in sync. Note that the only attributes we
183
- can update here are the ones that were given to us by the
195
+ instance attributes in sync. Note that the only attributes we can
196
+ update here are the ones that were given to us by the
184
197
:py:mod:`psycopg2` composite caster machinery when we were first
185
- instantiated.
198
+ instantiated. These will be the fields of the composite type for which
199
+ we were registered, which will be column names for table and view
200
+ types.
186
201
187
202
"""
188
203
unknown = []
0 commit comments