.. code:: python
from typing import Any, Sequence
- from psycopg import AnyCursor
+ from psycopg import Cursor
class DictRowFactory:
- def __init__(self, cursor: AnyCursor[dict[str, Any]]):
+ def __init__(self, cursor: Cursor[dict[str, Any]]):
self.fields = [c.name for c in cursor.description]
def __call__(self, values: Sequence[Any]) -> dict[str, Any]:
.. code:: python
- def dict_row_factory(
- cursor: AnyCursor[dict[str, Any]]
- ) -> Callable[[Sequence[Any]], dict[str, Any]]:
+ def dict_row_factory(cursor: Cursor[dict[str, Any]]) -> RowMaker[dict[str, Any]]:
fields = [c.name for c in cursor.description]
def make_row(values: Sequence[Any]) -> dict[str, Any]:
controls what type of record is returned by the fetch methods of the cursors.
The default `tuple_row()` returns a generic tuple as return type (`Tuple[Any,
...]`). This information can be used for type checking using a static analyzer
-such as Mypy_.
+such as mypy_.
-.. _Mypy: https://mypy.readthedocs.io/
+.. _mypy: https://mypy.readthedocs.io/
.. __: https://mypy.readthedocs.io/en/stable/generics.html
.. code:: python
---------------------------------------------
Using Pydantic_ it is possible to enforce static typing at runtime. Using a
-Pydantic model factory the code can be checked statically using Mypy and
+Pydantic model factory the code can be checked statically using mypy and
querying the database will raise an exception if the resultset is not
compatible with the model.
dob: Optional[date]
class PersonFactory:
- def __init__(self, cur: psycopg.AnyCursor[Person]):
+ def __init__(self, cur: psycopg.Cursor[Person]):
assert cur.description
self.fields = [c.name for c in cur.description]
print(f"{p.first_name} was born in {p.dob.year}")
else:
print(f"Who knows when {p.first_name} was born")
+
+
+Another level of generic
+^^^^^^^^^^^^^^^^^^^^^^^^
+
+Note that, in the example above, the `!PersonFactory` implementation has
+nothing specific to the `!Person` class, apart from the returned type itself.
+This suggests that it's actually possible to create a... factory of factories:
+a function that, given a Pydantic model, returns a RowFactory that can be used
+to annotate connections and cursor statically.
+
+In the example above, the `!PersonFactory` class can be implemented as a
+function:
+
+.. code:: python
+
+ def person_factory(cursor: Cursor[Person]) -> RowMaker[Person]:
+ assert cursor.description
+ fields = [c.name for c in cursor.description]
+
+ def person_factory_(values: Sequence[Any]) -> Person:
+ return Person(**dict(zip(fields, values)))
+
+ return person_factory_
+
+The function `!person_factory()` is a `!RowFactory[Person]`. We can introduce
+a generic `M`, which can be any Pydantic model, and write a function returning
+`!RowFactory[M]`:
+
+.. code:: python
+
+ M = TypeVar("M", bound=BaseModel)
+
+ def model_factory(model: Type[M]) -> RowFactory[M]:
+ def model_factory_(cursor: Cursor[M]) -> RowMaker[M]:
+ assert cursor.description
+ fields = [c.name for c in cursor.description]
+
+ def model_factory__(values: Sequence[Any]) -> M:
+ return model(**dict(zip(fields, values)))
+
+ return model_factory__
+
+ return model_factory_
+
+which can be used to declare the types of connections and cursors:
+
+.. code:: python
+
+ conn = psycopg.connect()
+ cur = conn.cursor(row_factory=model_factory(Person))
+ x = cur.fetchone()
+ # the type of x is Optional[Person]
.. autoattribute:: cursor_factory
- The type, of factory function, returned by `cursor()` and `execute()`.
+ The type, or factory function, returned by `cursor()` and `execute()`.
Default is `psycopg.Cursor`.
.. autoattribute:: server_cursor_factory
- The type, of factory function, returned by `cursor()` when a name is
+ The type, or factory function, returned by `cursor()` when a name is
specified.
Default is `psycopg.ServerCursor`.
+ .. autoattribute:: row_factory
+
+ The row factory defining the type of rows returned by
+ `~Cursor.fetchone()` and the other cursor fetch methods.
+
+ The default is `~psycopg.rows.tuple_row`, which means that the fetch
+ methods will return simple tuples.
+
+ .. seealso:: See :ref:`row-factories` for details about defining the
+ objects returned by cursors.
+
.. automethod:: execute(query, params=None, prepare=None) -> Cursor
:param query: The query to execute.
See :ref:`query-parameters` for all the details about executing
queries.
- .. autoattribute:: row_factory
-
- See :ref:`row-factories` for details.
-
.. rubric:: Transaction management methods
For details see :ref:`transactions`.
Default is `psycopg.AsyncServerCursor`.
+ .. autoattribute:: row_factory
+
.. automethod:: execute(query, params=None, prepare=None) -> AsyncCursor
.. automethod:: commit
.. automethod:: rollback