mirror of
https://github.com/psycopg/psycopg2.git
synced 2024-11-22 08:56:34 +03:00
Added documentation for range types and adaptation
This commit is contained in:
parent
b1953640d2
commit
c756d580f2
1
NEWS
1
NEWS
|
@ -1,6 +1,7 @@
|
|||
What's new in psycopg 2.4.6
|
||||
---------------------------
|
||||
|
||||
- Added support for PostgreSQL 9.2 range types.
|
||||
- Added support for backward scrollable cursors. Thanks to Jon Nelson
|
||||
for the initial patch (ticket #108).
|
||||
- connection.reset() implemented using DISCARD ALL on server versions
|
||||
|
|
|
@ -211,6 +211,89 @@ requires no adapter registration.
|
|||
|
||||
|
||||
|
||||
.. index::
|
||||
pair: range; Data types
|
||||
|
||||
Range data types
|
||||
^^^^^^^^^^^^^^^^
|
||||
|
||||
.. versionadded:: 2.4.6
|
||||
|
||||
Psycopg offers a `Range` Python type and supports adaptation between them and
|
||||
PostgreSQL |range|_ types. Builtin |range| types are supported out-of-the-box;
|
||||
user-defined |range| types can be adapted using `register_range()`.
|
||||
|
||||
.. |range| replace:: :sql:`range`
|
||||
.. _range: http://www.postgresql.org/docs/current/static/rangetypes.html
|
||||
|
||||
.. autoclass:: Range
|
||||
|
||||
This Python type is only used to pass and retrieve range values to and
|
||||
from PostgreSQL and doesn't attempt to replicate the PostgreSQL range
|
||||
features: it doesn't perform normalization and doesn't implement all the
|
||||
operators__ supported by the database.
|
||||
|
||||
.. __: http://www.postgresql.org/docs/current/static/functions-range.html#RANGE-OPERATORS-TABLE
|
||||
|
||||
`!Range` objects are immutable, hashable, and support the ``in`` operator
|
||||
(checking if an element is within the range). They can be tested for
|
||||
equivalence but not for ordering. Empty ranges evaluate to `!False` in
|
||||
boolean context, nonempty evaluate to `!True`.
|
||||
|
||||
Although it is possible to instantiate `!Range` objects, the class doesn't
|
||||
have an adapter registered, so you cannot normally pass these instances as
|
||||
query arguments. To use range objects as query arguments you can either
|
||||
use one of the provided subclasses, such as `NumericRange` or create a
|
||||
custom subclass using `register_range()`.
|
||||
|
||||
Object attributes:
|
||||
|
||||
.. autoattribute:: isempty
|
||||
.. autoattribute:: lower
|
||||
.. autoattribute:: upper
|
||||
.. autoattribute:: lower_inc
|
||||
.. autoattribute:: upper_inc
|
||||
.. autoattribute:: lower_inf
|
||||
.. autoattribute:: upper_inf
|
||||
|
||||
|
||||
The following `Range` subclasses map builtin PostgreSQL |range| types to
|
||||
Python objects: they have an adapter registered so their instances can be
|
||||
passed as query arguments. |range| values read from database queries are
|
||||
automatically casted into instances of these classes.
|
||||
|
||||
.. autoclass:: NumericRange
|
||||
.. autoclass:: DateRange
|
||||
.. autoclass:: DateTimeRange
|
||||
.. autoclass:: DateTimeTZRange
|
||||
|
||||
Custom |range| types (created with |CREATE TYPE|_ :sql:`... AS RANGE`) can be
|
||||
adapted to a custom `Range` subclass:
|
||||
|
||||
.. autofunction:: register_range
|
||||
|
||||
.. autoclass:: RangeCaster
|
||||
|
||||
Object attributes:
|
||||
|
||||
.. attribute:: range
|
||||
|
||||
The `!Range` subclass adapted.
|
||||
|
||||
.. attribute:: adapter
|
||||
|
||||
The `~psycopg2.extensions.ISQLQuote` responsible to adapt `!range`.
|
||||
|
||||
.. attribute:: typecaster
|
||||
|
||||
The object responsible for casting.
|
||||
|
||||
.. attribute:: array_typecaster
|
||||
|
||||
The object responsible for casting arrays, if available, else `!None`.
|
||||
|
||||
|
||||
|
||||
.. index::
|
||||
pair: UUID; Data types
|
||||
|
||||
|
|
|
@ -31,26 +31,13 @@ from psycopg2.extensions import ISQLQuote, adapt, register_adapter
|
|||
from psycopg2.extensions import new_type, new_array_type, register_type
|
||||
|
||||
class Range(object):
|
||||
"""Python representation for a PostgreSQL range type.
|
||||
"""Python representation for a PostgreSQL |range|_ type.
|
||||
|
||||
:param lower: lower bound for the range. None means unbound
|
||||
:param upper: upper bound for the range. None means unbound
|
||||
:param lower: lower bound for the range. `!None` means unbound
|
||||
:param upper: upper bound for the range. `!None` means unbound
|
||||
:param bounds: one of the literal strings ``()``, ``[)``, ``(]``, ``[]``,
|
||||
representing whether the lower or upper bounds are included
|
||||
:param empty: if true, the range is empty
|
||||
|
||||
TODO: move this to the docs
|
||||
|
||||
This Python type is only used to pass and retrieve range values to and
|
||||
from PostgreSQL and doesn't attempt to replicate the PostgreSQL range
|
||||
features: it doesn't perform normalization and doesn't implement all the
|
||||
operators supported.
|
||||
|
||||
Although it is possible to instantiate `!Range` objects, the class doesn't
|
||||
have an adapter so you cannot normally pass these instances as query
|
||||
arguments. To use range objects as query arguments you can either use one
|
||||
of the provided subclasses, such as [TODO: the other] `IntRange` or create
|
||||
a custom one using `register_range()`.
|
||||
:param empty: if `!True`, the range is empty
|
||||
|
||||
"""
|
||||
__slots__ = ('_lower', '_upper', '_bounds')
|
||||
|
@ -75,41 +62,41 @@ class Range(object):
|
|||
|
||||
@property
|
||||
def lower(self):
|
||||
"""The lower bound of the range. None if empty or unbound."""
|
||||
"""The lower bound of the range. `!None` if empty or unbound."""
|
||||
return self._lower
|
||||
|
||||
@property
|
||||
def upper(self):
|
||||
"""The upper bound of the range. None if empty or unbound."""
|
||||
"""The upper bound of the range. `!None` if empty or unbound."""
|
||||
return self._upper
|
||||
|
||||
@property
|
||||
def isempty(self):
|
||||
"""True if the range is empty."""
|
||||
"""`!True` if the range is empty."""
|
||||
return self._bounds is None
|
||||
|
||||
@property
|
||||
def lower_inf(self):
|
||||
"""True if the range doesn't have a lower bound."""
|
||||
"""`!True` if the range doesn't have a lower bound."""
|
||||
if self._bounds is None: return False
|
||||
return self._lower is None
|
||||
|
||||
@property
|
||||
def upper_inf(self):
|
||||
"""True if the range doesn't have an upper bound."""
|
||||
"""`!True` if the range doesn't have an upper bound."""
|
||||
if self._bounds is None: return False
|
||||
return self._upper is None
|
||||
|
||||
@property
|
||||
def lower_inc(self):
|
||||
"""True if the lower bound is included in the range."""
|
||||
"""`!True` if the lower bound is included in the range."""
|
||||
if self._bounds is None: return False
|
||||
if self._lower is None: return False
|
||||
return self._bounds[0] == '['
|
||||
|
||||
@property
|
||||
def upper_inc(self):
|
||||
"""True if the upper bound is included in the range."""
|
||||
"""`!True` if the upper bound is included in the range."""
|
||||
if self._bounds is None: return False
|
||||
if self._upper is None: return False
|
||||
return self._bounds[1] == ']'
|
||||
|
@ -151,19 +138,30 @@ class Range(object):
|
|||
|
||||
|
||||
def register_range(pgrange, pyrange, conn_or_curs, globally=False):
|
||||
"""Register a typecaster and an adapter for range a range type.
|
||||
"""Create and register an adapter and the typecasters to convert between
|
||||
a PostgreSQL |range|_ type and a PostgreSQL `Range` subclass.
|
||||
|
||||
:param pgrange: the name of the PostgreSQL range type
|
||||
:param pyrange: a Range (strict) subclass, or just the name to give it
|
||||
(the class will be available as the `!range` attribute of the returned
|
||||
`RangeCaster`.
|
||||
:param pgrange: the name of the PostgreSQL |range| type. Can be
|
||||
schema-qualified
|
||||
:param pyrange: a `Range` strict subclass, or just a name to give to a new
|
||||
class
|
||||
:param conn_or_curs: a connection or cursor used to find the oid of the
|
||||
range and its subtype; the typecaster is registered in a scope limited
|
||||
to this object, unless *globally* is set to `!True`
|
||||
:param globally: if `!False` (default) register the typecaster only on
|
||||
*conn_or_curs*, otherwise register it globally
|
||||
:return: the registered `RangeCaster` instance responsible for the
|
||||
conversion
|
||||
:return: `RangeCaster` instance responsible for the conversion
|
||||
|
||||
If a string is passed to *pyrange*, a new `Range` subclass is created
|
||||
with such name and will be available as the `~RangeCaster.range` attribute
|
||||
of the returned `RangeCaster` object.
|
||||
|
||||
The function queries the database on *conn_or_curs* to inspect the
|
||||
*pgrange* type. Raise `~psycopg2.ProgrammingError` if the type is not
|
||||
found. If querying the database is not advisable, use directly the
|
||||
`RangeCaster` class and register the adapter and typecasters using the
|
||||
provided functions.
|
||||
|
||||
"""
|
||||
caster = RangeCaster._from_db(pgrange, pyrange, conn_or_curs)
|
||||
caster._register(not globally and conn_or_curs or None)
|
||||
|
@ -219,7 +217,12 @@ class RangeAdapter(object):
|
|||
|
||||
|
||||
class RangeCaster(object):
|
||||
"""Helper class to convert between `Range` and PostgreSQL range types."""
|
||||
"""Helper class to convert between `Range` and PostgreSQL range types.
|
||||
|
||||
Objects of this class are usually created by `register_range()`. Manual
|
||||
creation could be useful if querying the database is not advisable: in
|
||||
this case the oids must be provided.
|
||||
"""
|
||||
def __init__(self, pgrange, pyrange, oid, subtype_oid, array_oid=None):
|
||||
self.subtype_oid = subtype_oid
|
||||
self._create_ranges(pgrange, pyrange)
|
||||
|
@ -237,7 +240,9 @@ class RangeCaster(object):
|
|||
def _create_ranges(self, pgrange, pyrange):
|
||||
"""Create Range and RangeAdapter classes if needed."""
|
||||
# if got a string create a new RangeAdapter concrete type (with a name)
|
||||
# else take it as an adapter.
|
||||
# else take it as an adapter. Passing an adapter should be considered
|
||||
# an implementation detail and is not documented. It is currently used
|
||||
# for the numeric ranges.
|
||||
self.adapter = None
|
||||
if isinstance(pgrange, basestring):
|
||||
self.adapter = type(pgrange, (RangeAdapter,), {})
|
||||
|
@ -378,7 +383,11 @@ where typname = %s and ns.nspname = %s;
|
|||
|
||||
|
||||
class NumericRange(Range):
|
||||
"""A `Range` suitable to pass Python numeric types to a PostgreSQL range."""
|
||||
"""A `Range` suitable to pass Python numeric types to a PostgreSQL range.
|
||||
|
||||
PostgreSQL types :sql:`int4range`, :sql:`int8range`, :sql:`numrange` are
|
||||
casted into `!NumericRange` instances.
|
||||
"""
|
||||
pass
|
||||
|
||||
class DateRange(Range):
|
||||
|
|
Loading…
Reference in New Issue
Block a user