Skip to content

Commit 4134156

Browse files
committed
Expand ORM docs
1 parent 1f51739 commit 4134156

File tree

1 file changed

+60
-12
lines changed

1 file changed

+60
-12
lines changed

postgres/orm.py

Lines changed: 60 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,31 @@
1-
""":py:mod:`postgres` implements an object-relational mapper based on
2-
:py:mod:`psycopg2` composite casters. The fundamental technique, introduced by
3-
`Michael Robbelard at PyOhio 2013`_, is to write SQL queries that typecast
4-
results to table types, and then use a :py:mod:`psycopg2`
5-
:py:class:`~psycopg2.extra.CompositeCaster` to map these to Python objects.
6-
This means we get to define our schema in SQL, and we get to write our queries
7-
in SQL, and we get to explicitly indicate in our SQL queries how Python should
8-
map the results to objects, and then we can write Python objects that contain
9-
only business logic and not schema definitions.
1+
"""
2+
3+
It's somewhat of a fool's errand to introduce a Python ORM in 2013, with
4+
`SQLAlchemy`_ ascendant (`Django's ORM`_ not-withstanding). And yet here we
5+
are. SQLAlchemy is mature and robust and full-featured. This makes it complex,
6+
difficult to learn, and kind of scary. The ORM we introduce here is simpler: it
7+
targets PostgreSQL only, it depends on raw SQL (it has no object model for
8+
schema definition nor one for query construction), and it never updates your
9+
database for you. You are in full, direct control of your application's
10+
database usage.
11+
12+
.. _SQLAlchemy: http://www.sqlalchemy.org/
13+
.. _Django's ORM: http://www.djangobook.com/en/2.0/chapter05.html
14+
15+
The fundamental technique we employ, introduced by `Michael Robbelard at PyOhio
16+
2013`_, is to write SQL queries that typecast results to table types, and then
17+
use a :py:mod:`psycopg2` :py:class:`~psycopg2.extra.CompositeCaster` to map
18+
these to Python objects. This means we get to define our schema in SQL, and we
19+
get to write our queries in SQL, and we get to explicitly indicate in our SQL
20+
queries how Python should map the results to objects, and then we can write
21+
Python objects that contain only business logic and not schema definitions.
1022
1123
.. _Michael Robbelard at PyOhio 2013: https://www.youtube.com/watch?v=Wz1_GYc4GmU#t=25m06s
1224
25+
26+
Introducing Table Types
27+
-----------------------
28+
1329
Every table in PostgreSQL has a type associated with it, which is the column
1430
definition for that table. These are composite types just like any other
1531
composite type in PostgreSQL, meaning we can use them to cast query results.
@@ -72,6 +88,8 @@
7288
:py:mod:`postgres.orm`. We map based on types, not tables.
7389
7490
91+
.. _orm-tutorial:
92+
7593
ORM Tutorial
7694
------------
7795
@@ -106,15 +124,45 @@
106124
>>> rec.bar
107125
'blam'
108126
109-
As usual, if your query only returns one column, then
127+
And as usual, if your query only returns one column, then
110128
:py:meth:`~postgres.Postgres.all` and :py:meth:`~postgres.Postgres.one_or_zero`
111129
will do the dereferencing for you:
112130
131+
>>> [foo.bar for foo in db.all("SELECT foo.*::foo FROM foo")]
132+
['blam', 'whit']
113133
>>> foo = db.one_or_zero("SELECT foo.*::foo FROM foo WHERE bar='blam'")
114134
>>> foo.bar
115135
'blam'
116-
>>> [foo.bar for foo in db.all("SELECT foo.*::foo FROM foo")]
117-
['blam', 'whit']
136+
137+
To update your database, add a method to your model:
138+
139+
>>> db.unregister_model(Foo)
140+
>>> class Foo(Model):
141+
...
142+
... typname = "foo"
143+
...
144+
... def set_baz(self, baz):
145+
... self.db.run( "UPDATE foo SET baz=%s WHERE bar=%s"
146+
... , (baz, self.bar)
147+
... )
148+
... self.update_attributes(baz=baz)
149+
...
150+
>>> db.register_model(Foo)
151+
152+
Then use that method to update the database:
153+
154+
>>> db.one_or_zero("SELECT baz FROM foo WHERE bar='blam'")
155+
>>> foo = db.one_or_zero("SELECT foo.*::foo FROM foo WHERE bar='blam'")
156+
42
157+
>>> foo.set_baz(90210)
158+
>>> foo.baz
159+
90210
160+
>>> db.one_or_zero("SELECT baz FROM foo WHERE bar='blam'")
161+
90210
162+
163+
We never update your database for you. We also never sync your objects for you:
164+
note the use of the :py:meth:`~postgres.orm.Model.update_attributes` method to
165+
sync our instance after modifying the database.
118166
119167
120168
The Model Base Class

0 commit comments

Comments
 (0)