Skip to content

Commit 754f79f

Browse files
committed
Expose cursor_factory to configuration; #10
1 parent 53ec74e commit 754f79f

File tree

2 files changed

+43
-4
lines changed

2 files changed

+43
-4
lines changed

postgres.py

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,8 @@ class Postgres(object):
209209
210210
:param unicode url: A ``postgres://`` URL or a `PostgreSQL connection string <http://www.postgresql.org/docs/current/static/libpq-connect.html>`_
211211
:param int minconn: The minimum size of the connection pool
212-
:param int maxconn: The minimum size of the connection pool
212+
:param int maxconn: The maximum size of the connection pool
213+
:param cursor_factory: Defaults to :py:class:`~psycopg2.extras.RealDictCursor`
213214
:param strict_one: The default :py:attr:`strict` parameter for :py:meth:`~postgres.Postgres.one`
214215
:type strict_one: :py:class:`bool`
215216
@@ -223,6 +224,11 @@ class Postgres(object):
223224
:py:class:`~postgres.Postgres` instance is that it runs everything through
224225
its connection pool.
225226
227+
Check the :py:mod:`psycopg2` `docs
228+
<http://initd.org/psycopg/docs/extras.html#connection-and-cursor-subclasses>`_
229+
for additional :py:attr:`cursor_factories`, such as
230+
:py:class:`NamedTupleCursor`.
231+
226232
The names in our simple API, :py:meth:`~postgres.Postgres.run`,
227233
:py:meth:`~postgres.Postgres.one`, and :py:meth:`~postgres.Postgres.rows`,
228234
were chosen to be short and memorable, and to not conflict with the DB-API
@@ -242,10 +248,13 @@ class Postgres(object):
242248
243249
"""
244250

245-
def __init__(self, url, minconn=1, maxconn=10, strict_one=None):
251+
def __init__(self, url, minconn=1, maxconn=10, \
252+
cursor_factory=RealDictCursor, strict_one=None):
246253
if url.startswith("postgres://"):
247254
dsn = url_to_dsn(url)
248255

256+
Connection.cursor_factory = cursor_factory
257+
249258
self.pool = ConnectionPool( minconn=minconn
250259
, maxconn=maxconn
251260
, dsn=dsn
@@ -400,18 +409,20 @@ class Connection(psycopg2.extensions.connection):
400409
401410
- We set :py:attr:`autocommit` to :py:const:`True`.
402411
- We set the client encoding to ``UTF-8``.
403-
- We use :py:class:`psycopg2.extras.RealDictCursor`.
412+
- We use :py:attr:`self.cursor_factory`.
404413
405414
"""
406415

416+
cursor_factory = None # set this before using this object
417+
407418
def __init__(self, *a, **kw):
408419
psycopg2.extensions.connection.__init__(self, *a, **kw)
409420
self.set_client_encoding('UTF-8')
410421
self.autocommit = True
411422

412423
def cursor(self, *a, **kw):
413424
if 'cursor_factory' not in kw:
414-
kw['cursor_factory'] = RealDictCursor
425+
kw['cursor_factory'] = self.cursor_factory
415426
return psycopg2.extensions.connection.cursor(self, *a, **kw)
416427

417428

tests.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
from __future__ import unicode_literals
22

33
import os
4+
from collections import namedtuple
45
from unittest import TestCase
56

67
from postgres import Postgres, TooFew, TooMany
8+
from psycopg2.extras import NamedTupleCursor
79

810

911
DATABASE_URL = os.environ['DATABASE_URL']
@@ -259,3 +261,29 @@ def test_get_connection_gets_a_connection(self):
259261
cursor.execute("SELECT * FROM foo ORDER BY bar")
260262
actual = cursor.fetchall()
261263
assert actual == [{"bar": "baz"}, {"bar": "buz"}]
264+
265+
266+
# cursor_factory
267+
# ==============
268+
269+
class TestCursorFactory(WithData):
270+
271+
def setUp(self): # override
272+
self.db = Postgres(DATABASE_URL, cursor_factory=NamedTupleCursor)
273+
self.db.run("DROP SCHEMA IF EXISTS public CASCADE")
274+
self.db.run("CREATE SCHEMA public")
275+
self.db.run("CREATE TABLE foo (bar text)")
276+
self.db.run("INSERT INTO foo VALUES ('baz')")
277+
self.db.run("INSERT INTO foo VALUES ('buz')")
278+
279+
def test_NamedDictCursor_results_in_namedtuples(self):
280+
Record = namedtuple("Record", ["bar"])
281+
expected = [Record(bar="baz"), Record(bar="buz")]
282+
actual = self.db.rows("SELECT * FROM foo ORDER BY bar")
283+
assert actual == expected
284+
285+
def test_NamedDictCursor_results_in_namedtuples(self):
286+
Record = namedtuple("Record", ["bar"])
287+
expected = [Record(bar="baz"), Record(bar="buz")]
288+
actual = self.db.rows("SELECT * FROM foo ORDER BY bar")
289+
assert actual == expected

0 commit comments

Comments
 (0)