From fa9393b5870f07d6fb3ac55f5d90ffd8e06fe678 Mon Sep 17 00:00:00 2001 From: Daniele Varrazzo Date: Sat, 22 Sep 2012 02:01:04 +0100 Subject: [PATCH] Added documentation about CompositeCaster subclassing --- NEWS | 3 +++ doc/src/extras.rst | 57 ++++++++++++++++++++++++++++++++++++++++++++-- lib/extras.py | 35 ++++++++-------------------- 3 files changed, 67 insertions(+), 28 deletions(-) diff --git a/NEWS b/NEWS index 3753462c..33ea5c27 100644 --- a/NEWS +++ b/NEWS @@ -3,6 +3,9 @@ What's new in psycopg 2.4.6 - Added support for backward scrollable cursors. Thanks to Jon Nelson for the initial patch (ticket #108). + - Added a simple way to customize casting of composite types into Python + objects other than namedtuples. Many thanks to Ronan Dunklau and + Tobias Oberstein for the feature development. - connection.reset() implemented using DISCARD ALL on server versions supporting it. - Fixed 'cursor()' arguments propagation in connection subclasses diff --git a/doc/src/extras.rst b/doc/src/extras.rst index f3f10b12..9532485b 100644 --- a/doc/src/extras.rst +++ b/doc/src/extras.rst @@ -189,8 +189,8 @@ after a table row type) into a Python named tuple, or into a regular tuple if >>> cur.fetchone()[0] card(value=8, suit='hearts') -Nested composite types are handled as expected, but the type of the composite -components must be registered as well. +Nested composite types are handled as expected, provided that the type of the +composite components are registered as well. .. doctest:: @@ -205,10 +205,63 @@ components must be registered as well. Adaptation from Python tuples to composite types is automatic instead and requires no adapter registration. + +.. _custom-composite: + +.. Note:: + + If you want to convert PostgreSQL composite types into something different + than a `!namedtuple` you can subclass the `CompositeCaster` overriding + `~CompositeCaster.make()`. For example, if you want to convert your type + into a Python dictionary you can use:: + + >>> class DictComposite(psycopg2.extras.CompositeCaster): + ... def make(self, values): + ... return dict(zip(self.attnames, values)) + + >>> psycopg2.extras.register_composite('card', cur, + ... factory=DictComposite) + + >>> cur.execute("select (8, 'hearts')::card") + >>> cur.fetchone()[0] + {'suit': 'hearts', 'value': 8} + + .. autofunction:: register_composite .. autoclass:: CompositeCaster + .. automethod:: make + + .. versionadded:: 2.4.6 + + Object attributes: + + .. attribute:: name + + The name of the PostgreSQL type. + + .. attribute:: oid + + The oid of the PostgreSQL type. + + .. attribute:: array_oid + + The oid of the PostgreSQL array type, if available. + + .. attribute:: type + + The type of the Python objects returned. If :py:func:`collections.namedtuple()` + is available, it is a named tuple with attributes equal to the type + components. Otherwise it is just the `!tuple` object. + + .. attribute:: attnames + + List of component names of the type to be casted. + + .. attribute:: atttypes + + List of component type oids of the type to be casted. .. index:: diff --git a/lib/extras.py b/lib/extras.py index 90652f4c..23679315 100644 --- a/lib/extras.py +++ b/lib/extras.py @@ -802,32 +802,6 @@ class CompositeCaster(object): querying the database at registration time is not desirable (such as when using an :ref:`asynchronous connections `). - .. attribute:: name - - The name of the PostgreSQL type. - - .. attribute:: oid - - The oid of the PostgreSQL type. - - .. attribute:: array_oid - - The oid of the PostgreSQL array type, if available. - - .. attribute:: type - - The type of the Python objects returned. If :py:func:`collections.namedtuple()` - is available, it is a named tuple with attributes equal to the type - components. Otherwise it is just the `!tuple` object. - - .. attribute:: attnames - - List of component names of the type to be casted. - - .. attribute:: atttypes - - List of component type oids of the type to be casted. - """ def __init__(self, name, oid, attrs, array_oid=None): self.name = name @@ -860,6 +834,15 @@ class CompositeCaster(object): return self.make(values) def make(self, values): + """Return a new Python object representing the data being casted. + + *values* is the list of attributes, already casted into their Python + representation. + + You can subclass this method to :ref:`customize the composite cast + `. + """ + return self._ctor(values) _re_tokenize = regex.compile(r"""