Skip to content

Commit fe8d3e4

Browse files
committed
Rename one_or_zero to one; #20
1 parent abd4ed8 commit fe8d3e4

File tree

3 files changed

+155
-153
lines changed

3 files changed

+155
-153
lines changed

postgres/__init__.py

Lines changed: 122 additions & 121 deletions
Original file line numberDiff line numberDiff line change
@@ -32,22 +32,22 @@
3232
>>> db.run("INSERT INTO foo VALUES ('buz', 42)")
3333
>>> db.run("INSERT INTO foo VALUES ('bit', 537)")
3434
35+
Use :py:meth:`~postgres.Postgres.one` to run SQL and fetch one result or
36+
:py:class:`None`:
37+
38+
>>> db.one("SELECT * FROM foo WHERE bar='buz'")
39+
Record(bar='buz', baz=42)
40+
>>> db.one("SELECT * FROM foo WHERE bar='blam'")
41+
3542
Use :py:meth:`~postgres.Postgres.all` to run SQL and fetch all results:
3643
3744
>>> db.all("SELECT * FROM foo ORDER BY bar")
3845
[Record(bar='bit', baz=537), Record(bar='buz', baz=42)]
3946
40-
Use :py:meth:`~postgres.Postgres.one_or_zero` to run SQL and fetch one result
41-
or :py:class:`None`:
42-
43-
>>> db.one_or_zero("SELECT * FROM foo WHERE bar='buz'")
44-
Record(bar='buz', baz=42)
45-
>>> db.one_or_zero("SELECT * FROM foo WHERE bar='blam'")
46-
4747
If your queries return one column then you get just the value or a list of
4848
values instead of a record or list of records:
4949
50-
>>> db.one_or_zero("SELECT baz FROM foo WHERE bar='buz'")
50+
>>> db.one("SELECT baz FROM foo WHERE bar='buz'")
5151
42
5252
>>> db.all("SELECT baz FROM foo ORDER BY bar")
5353
[537, 42]
@@ -65,7 +65,7 @@
6565
against `SQL injection`_. (This is inspired by old-style Python string
6666
formatting, but it is not the same.)
6767
68-
>>> db.one_or_zero("SELECT * FROM foo WHERE bar=%(bar)s", {"bar": "buz"})
68+
>>> db.one("SELECT * FROM foo WHERE bar=%(bar)s", {"bar": "buz"})
6969
Record(bar='buz', baz=42)
7070
7171
Never build SQL strings out of user input!
@@ -77,10 +77,10 @@
7777
++++++++++++++++
7878
7979
Eighty percent of your database usage should be covered by the simple
80-
:py:meth:`~postgres.Postgres.run`, :py:meth:`~postgres.Postgres.all`,
81-
:py:meth:`~postgres.Postgres.one_or_zero` API introduced above. For the other
82-
20%, :py:mod:`postgres` provides two context managers for working at
83-
increasingly lower levels of abstraction. The lowest level of abstraction in
80+
:py:meth:`~postgres.Postgres.run`, :py:meth:`~postgres.Postgres.one`,
81+
:py:meth:`~postgres.Postgres.all` API introduced above. For the other 20%,
82+
:py:mod:`postgres` provides two context managers for working at increasingly
83+
lower levels of abstraction. The lowest level of abstraction in
8484
:py:mod:`postgres` is a :py:mod:`psycopg2` `connection pool
8585
<http://initd.org/psycopg/docs/pool.html>`_ that we configure and manage for
8686
you. Everything in :py:mod:`postgres`, both the simple API and the context
@@ -294,27 +294,28 @@ class Postgres(object):
294294
:py:mod:`psycopg2` behavior, which is to return tuples. Whatever default
295295
you set here, you can override that default on a per-call basis by passing
296296
:py:attr:`record_type` or :py:attr:`cursor_factory` to
297-
:py:meth:`~postgres.Postgres.all`,
298-
:py:meth:`~postgres.Postgres.one_or_zero`, and
297+
:py:meth:`~postgres.Postgres.one`, :py:meth:`~postgres.Postgres.all`, and
299298
:py:meth:`~postgres.Postgres.get_transaction`.
300299
301300
The names in our simple API, :py:meth:`~postgres.Postgres.run`,
302-
:py:meth:`~postgres.Postgres.all`, and
303-
:py:meth:`~postgres.Postgres.one_or_zero`, were chosen to be short and
304-
memorable, and to not conflict with the DB-API 2.0 :py:meth:`execute`,
305-
:py:meth:`fetchall`, and :py:meth:`fetchone` methods, which have slightly
306-
different semantics (under DB-API 2.0 you call :py:meth:`execute` on a
307-
cursor and then call one of the :py:meth:`fetch*` methods on the same
308-
cursor to retrieve records; with our simple API there is no second
309-
:py:meth:`fetch` step). See `this ticket`_ for more of the rationale behind
310-
these names. The context managers on this class are named starting with
311-
:py:meth:`get_` to set them apart from the simple-case API. Note that when
312-
working inside a block under one of the context managers, you're using
313-
DB-API 2.0 (:py:meth:`execute` + :py:meth:`fetch*`), not our simple API
314-
(:py:meth:`~postgres.Postgres.run`, :py:meth:`~postgres.Postgres.all`,
315-
:py:meth:`~postgres.Postgres.one_or_zero`).
316-
317-
.. _this ticket: https://github.com/gittip/postgres.py/issues/16
301+
:py:meth:`~postgres.Postgres.one`, and :py:meth:`~postgres.Postgres.all`,
302+
were chosen to be short and memorable, and to not directly conflict with
303+
the DB-API 2.0 :py:meth:`execute`, :py:meth:`fetchone`, and
304+
:py:meth:`fetchall` methods, which have slightly different semantics (under
305+
DB-API 2.0 you call :py:meth:`execute` on a cursor and then call one of the
306+
:py:meth:`fetch*` methods on the same cursor to retrieve records; with our
307+
simple API there is no second :py:meth:`fetch` step, and we also provide
308+
automatic dereferencing). See issues `16`_ and `20`_ for more of the
309+
rationale behind these names. The context managers on this class are named
310+
starting with :py:meth:`get_` to set them apart from the simple-case API.
311+
Note that when working inside a block under one of the context managers,
312+
you're using DB-API 2.0 (:py:meth:`execute` + :py:meth:`fetch*`, with no
313+
automatic dereferencing), not our simple API
314+
(:py:meth:`~postgres.Postgres.run`, :py:meth:`~postgres.Postgres.one`,
315+
:py:meth:`~postgres.Postgres.all`).
316+
317+
.. _16: https://github.com/gittip/postgres.py/issues/16
318+
.. _20: https://github.com/gittip/postgres.py/issues/20
318319
319320
"""
320321

@@ -366,73 +367,7 @@ def run(self, sql, parameters=None, *a, **kw):
366367
txn.execute(sql, parameters)
367368

368369

369-
def all(self, sql, parameters=None, record_type=None, *a, **kw):
370-
"""Execute a query and return all results.
371-
372-
:param string sql: the SQL statement to execute
373-
:param parameters: the bind parameters for the SQL statement
374-
:type parameters: dict or tuple
375-
:param record_type: the type of record to return
376-
:type record_type: type or string
377-
:param a: passed through to
378-
:py:meth:`~postgres.Postgres.get_transaction`
379-
:param kw: passed through to
380-
:py:meth:`~postgres.Postgres.get_transaction`
381-
:returns: :py:class:`list` of records or :py:class:`list` of single
382-
values
383-
384-
>>> db.all("SELECT * FROM foo ORDER BY bar")
385-
[Record(bar='bit', baz=537), Record(bar='buz', baz=42)]
386-
387-
You can use :py:attr:`record_type` to override the type associated with
388-
the default :py:attr:`cursor_factory` for your
389-
:py:class:`~postgres.Postgres` instance:
390-
391-
>>> db.default_cursor_factory
392-
<class 'psycopg2.extras.NamedTupleCursor'>
393-
>>> db.all("SELECT * FROM foo ORDER BY bar", record_type=dict)
394-
[{'bar': 'bit', 'baz': 537}, {'bar': 'buz', 'baz': 42}]
395-
396-
That's a convenience so you don't have to go to the trouble of
397-
remembering where :py:class:`~psycopg2.extras.RealDictCursor` lives and
398-
importing it in order to get dictionaries back. If you do need more
399-
control (maybe you have a custom cursor class), you can pass
400-
:py:attr:`cursor_factory` explicitly, and that will override any
401-
:py:attr:`record_type`:
402-
403-
>>> from psycopg2.extensions import cursor
404-
>>> db.all( "SELECT * FROM foo ORDER BY bar"
405-
... , record_type=dict
406-
... , cursor_factory=cursor
407-
... )
408-
[('bit', 537), ('buz', 42)]
409-
410-
If the query results in records with a single column, we return a list
411-
of the values in that column rather than a list of records of values.
412-
413-
>>> db.all("SELECT baz FROM foo ORDER BY bar")
414-
[537, 42]
415-
416-
This works for record types that are mappings (anything with a
417-
:py:meth:`__len__` and a :py:meth:`values` method) as well those that
418-
are sequences:
419-
420-
>>> db.all("SELECT baz FROM foo ORDER BY bar", record_type=dict)
421-
[537, 42]
422-
423-
"""
424-
with self.get_transaction(record_type=record_type, *a, **kw) as txn:
425-
txn.execute(sql, parameters)
426-
recs = txn.fetchall()
427-
if recs and len(recs[0]) == 1: # dereference
428-
if hasattr(recs[0], 'values'): # mapping
429-
recs = [list(rec.values())[0] for rec in recs]
430-
else: # sequence
431-
recs = [rec[0] for rec in recs]
432-
return recs
433-
434-
435-
def one_or_zero(self, sql, parameters=None, record_type=None, zero=None, \
370+
def one(self, sql, parameters=None, record_type=None, zero=None, \
436371
*a, **kw):
437372
"""Execute a query and return a single result or a default value.
438373
@@ -453,12 +388,12 @@ def one_or_zero(self, sql, parameters=None, record_type=None, zero=None, \
453388
Use this for the common case where there should only be one record, but
454389
it may not exist yet.
455390
456-
>>> db.one_or_zero("SELECT * FROM foo WHERE bar='buz'")
391+
>>> db.one("SELECT * FROM foo WHERE bar='buz'")
457392
Record(bar='buz', baz=42)
458393
459394
If the record doesn't exist, we return :py:class:`None`:
460395
461-
>>> record = db.one_or_zero("SELECT * FROM foo WHERE bar='blam'")
396+
>>> record = db.one("SELECT * FROM foo WHERE bar='blam'")
462397
>>> if record is None:
463398
... print("No blam yet.")
464399
...
@@ -467,7 +402,7 @@ def one_or_zero(self, sql, parameters=None, record_type=None, zero=None, \
467402
If you pass :py:attr:`zero` we'll return that instead of
468403
:py:class:`None`:
469404
470-
>>> db.one_or_zero("SELECT * FROM foo WHERE bar='blam'", zero=False)
405+
>>> db.one("SELECT * FROM foo WHERE bar='blam'", zero=False)
471406
False
472407
473408
We specifically don't support passing lambdas or other callables for
@@ -481,9 +416,9 @@ def one_or_zero(self, sql, parameters=None, record_type=None, zero=None, \
481416
482417
>>> db.default_cursor_factory
483418
<class 'psycopg2.extras.NamedTupleCursor'>
484-
>>> db.one_or_zero( "SELECT * FROM foo WHERE bar='buz'"
485-
... , record_type=dict
486-
... )
419+
>>> db.one( "SELECT * FROM foo WHERE bar='buz'"
420+
... , record_type=dict
421+
... )
487422
{'bar': 'buz', 'baz': 42}
488423
489424
That's a convenience so you don't have to go to the trouble of
@@ -494,31 +429,31 @@ def one_or_zero(self, sql, parameters=None, record_type=None, zero=None, \
494429
:py:attr:`record_type`:
495430
496431
>>> from psycopg2.extensions import cursor
497-
>>> db.one_or_zero( "SELECT * FROM foo WHERE bar='buz'"
498-
... , record_type=dict
499-
... , cursor_factory=cursor
500-
... )
432+
>>> db.one( "SELECT * FROM foo WHERE bar='buz'"
433+
... , record_type=dict
434+
... , cursor_factory=cursor
435+
... )
501436
('buz', 42)
502437
503438
If the query result has only one column, then we dereference that for
504439
you.
505440
506-
>>> db.one_or_zero("SELECT baz FROM foo WHERE bar='buz'")
441+
>>> db.one("SELECT baz FROM foo WHERE bar='buz'")
507442
42
508443
509444
And if the dereferenced value is :py:class:`None`, we return the value
510445
of :py:attr:`zero`:
511446
512-
>>> db.one_or_zero("SELECT sum(baz) FROM foo WHERE bar='nope'", zero=0)
447+
>>> db.one("SELECT sum(baz) FROM foo WHERE bar='nope'", zero=0)
513448
0
514449
515450
Dereferencing will use :py:meth:`.values` if it exists on the record,
516451
so it should work for both mappings and sequences.
517452
518-
>>> db.one_or_zero( "SELECT sum(baz) FROM foo WHERE bar='nope'"
519-
... , record_type=dict
520-
... , zero=0
521-
... )
453+
>>> db.one( "SELECT sum(baz) FROM foo WHERE bar='nope'"
454+
... , record_type=dict
455+
... , zero=0
456+
... )
522457
0
523458
524459
"""
@@ -537,12 +472,78 @@ def one_or_zero(self, sql, parameters=None, record_type=None, zero=None, \
537472
return out
538473

539474

475+
def all(self, sql, parameters=None, record_type=None, *a, **kw):
476+
"""Execute a query and return all results.
477+
478+
:param string sql: the SQL statement to execute
479+
:param parameters: the bind parameters for the SQL statement
480+
:type parameters: dict or tuple
481+
:param record_type: the type of record to return
482+
:type record_type: type or string
483+
:param a: passed through to
484+
:py:meth:`~postgres.Postgres.get_transaction`
485+
:param kw: passed through to
486+
:py:meth:`~postgres.Postgres.get_transaction`
487+
:returns: :py:class:`list` of records or :py:class:`list` of single
488+
values
489+
490+
>>> db.all("SELECT * FROM foo ORDER BY bar")
491+
[Record(bar='bit', baz=537), Record(bar='buz', baz=42)]
492+
493+
You can use :py:attr:`record_type` to override the type associated with
494+
the default :py:attr:`cursor_factory` for your
495+
:py:class:`~postgres.Postgres` instance:
496+
497+
>>> db.default_cursor_factory
498+
<class 'psycopg2.extras.NamedTupleCursor'>
499+
>>> db.all("SELECT * FROM foo ORDER BY bar", record_type=dict)
500+
[{'bar': 'bit', 'baz': 537}, {'bar': 'buz', 'baz': 42}]
501+
502+
That's a convenience so you don't have to go to the trouble of
503+
remembering where :py:class:`~psycopg2.extras.RealDictCursor` lives and
504+
importing it in order to get dictionaries back. If you do need more
505+
control (maybe you have a custom cursor class), you can pass
506+
:py:attr:`cursor_factory` explicitly, and that will override any
507+
:py:attr:`record_type`:
508+
509+
>>> from psycopg2.extensions import cursor
510+
>>> db.all( "SELECT * FROM foo ORDER BY bar"
511+
... , record_type=dict
512+
... , cursor_factory=cursor
513+
... )
514+
[('bit', 537), ('buz', 42)]
515+
516+
If the query results in records with a single column, we return a list
517+
of the values in that column rather than a list of records of values.
518+
519+
>>> db.all("SELECT baz FROM foo ORDER BY bar")
520+
[537, 42]
521+
522+
This works for record types that are mappings (anything with a
523+
:py:meth:`__len__` and a :py:meth:`values` method) as well those that
524+
are sequences:
525+
526+
>>> db.all("SELECT baz FROM foo ORDER BY bar", record_type=dict)
527+
[537, 42]
528+
529+
"""
530+
with self.get_transaction(record_type=record_type, *a, **kw) as txn:
531+
txn.execute(sql, parameters)
532+
recs = txn.fetchall()
533+
if recs and len(recs[0]) == 1: # dereference
534+
if hasattr(recs[0], 'values'): # mapping
535+
recs = [list(rec.values())[0] for rec in recs]
536+
else: # sequence
537+
recs = [rec[0] for rec in recs]
538+
return recs
539+
540+
540541
def _some(self, sql, parameters, lo, hi, record_type, *a, **kw):
541542

542-
# This is undocumented (and largely untested) because I think it's a
543-
# rare case where this is wanted directly. It was added to make one and
544-
# one_or_zero DRY when we had one. Help yourself to it now that you've
545-
# found it. :^)
543+
# This is undocumented because I think it's a rare case where this is
544+
# wanted directly. It was added to make one and one_or_zero DRY when we
545+
# had those two methods. Help yourself to _some now that you've found
546+
# it. :^)
546547

547548
with self.get_transaction(record_type=record_type, *a, **kw) as txn:
548549
txn.execute(sql, parameters)
@@ -620,9 +621,9 @@ def register_model(self, ModelSubclass):
620621
if getattr(ModelSubclass, 'typname', None) is None:
621622
raise NoTypeSpecified(ModelSubclass)
622623

623-
n = self.one_or_zero( "SELECT count(*) FROM pg_type WHERE typname=%s"
624-
, (ModelSubclass.typname,)
625-
)
624+
n = self.one( "SELECT count(*) FROM pg_type WHERE typname=%s"
625+
, (ModelSubclass.typname,)
626+
)
626627
if n < 1:
627628
# Could be more than one since we don't constrain by typnamespace.
628629
# XXX What happens then?

0 commit comments

Comments
 (0)