Source code for codegen_database.ext.postgis
"""PostGIS extension for codegen_database.
:class:`PostGISExtension` wires PostGIS-suite support into the
codegen_database lifecycle. Register it on a
:class:`~codegen_database.config.CodegenDatabaseConfig` to enable the
PostGIS column types (currently
:class:`~codegen_database.types.postgis.STDADDR`, the ``stdaddr``
composite produced by the ``address_standardizer`` extension).
Usage::
from codegen_database.config import CodegenDatabaseConfig
from codegen_database.ext.postgis import PostGISExtension
config = CodegenDatabaseConfig()
config.use(PostGISExtension())
``PostGISExtension`` is the grouping for the whole PostGIS suite; which
PostgreSQL extensions it actually installs are controlled by boolean
flags. By default it declares only ``address_standardizer`` (which
provides ``stdaddr``), since that installs independently of the heavy
``postgis`` core. Enable the core when you need geometry::
config.use(PostGISExtension(postgis=True))
Enable the TIGER geocoder when you need to turn a free-form address
string into ranked candidate addresses with coordinates (the engine
behind :func:`fsh_lib.geocode.find_address_candidates`)::
config.use(PostGISExtension(tiger_geocoder=True))
``postgis_tiger_geocoder`` is declared ``CASCADE`` because it depends on
``postgis`` and ``fuzzystrmatch``; ``CASCADE`` installs those
automatically. (It does **not** pull in ``address_standardizer`` -- that
is a sibling extension for ``STDADDR``, installed by the default
``address_standardizer`` flag above; the geocoder does not require it.)
The extension only ships the ``geocode`` machinery -- it does **not**
load the TIGER census data the geocoder matches against. That data load
(``Loader_Generate_Nation_Script`` / ``Loader_Generate_Census_Script``
per state, US-only) is a separate operational step; ``geocode`` returns
no rows until it has run. See ``tiger-loader/`` for the loader image
that performs it.
After registration the extension auto-declares the selected PostgreSQL
extensions in metadata, so the Alembic comparator emits
``CREATE EXTENSION IF NOT EXISTS ...`` for any that are missing from the
database. Columns then use the type directly::
from sqlalchemy import Column
from codegen_database.ext.postgis import STDADDR, StdAddr
Column("address", STDADDR(), nullable=True)
# store StdAddr(house_num="123", name="MAIN", suftype="ST")
Convenience re-exports::
from codegen_database.ext.postgis import (
PostGISExtension,
STDADDR,
StdAddr,
PGExtension,
register_pg_extension,
)
"""
from __future__ import annotations
from dataclasses import dataclass
from typing import TYPE_CHECKING
from codegen_database.extension import CodegenDatabaseExtension
from codegen_database.pg_extension import (
PGExtension,
register_pg_extension,
)
from codegen_database.types.postgis import STDADDR, StdAddr
if TYPE_CHECKING:
from sqlalchemy import MetaData
__all__ = [
"STDADDR",
"PGExtension",
"PostGISExtension",
"StdAddr",
"register_pg_extension",
]
_POSTGIS_EXTENSION = PGExtension("postgis", cascade=True)
_GEOGRAPHY_EXTENSION = PGExtension("geography")
_ADDRESS_STANDARDIZER_EXTENSION = PGExtension("address_standardizer")
# CASCADE so its required deps (postgis, fuzzystrmatch) install with it
# rather than failing on a missing prerequisite. address_standardizer is
# NOT a dep of the geocoder -- it rides the separate flag above.
_TIGER_GEOCODER_EXTENSION = PGExtension("postgis_tiger_geocoder", cascade=True)
[docs]
@dataclass
class PostGISExtension(CodegenDatabaseExtension):
"""Wire PostGIS-suite support into the codegen_database lifecycle.
The extension groups the PostGIS suite; the boolean flags select
which PostgreSQL extensions it installs.
Args:
name: Extension name. Defaults to ``"postgis"``.
address_standardizer: Important for ``STDADDR``
geography: Important for ``COORDINATE``
postgis: Important for...... everything else
tiger_geocoder: Important for free-string geocoding
(``geocode``); ``CASCADE`` pulls in its dependencies.
"""
name: str = "postgis"
postgis: bool = False
address_standardizer: bool = True
geography: bool = False
tiger_geocoder: bool = False
[docs]
def configure_metadata(self, metadata: MetaData) -> None:
"""Declare the selected PostgreSQL extensions."""
if self.postgis:
register_pg_extension(metadata, _POSTGIS_EXTENSION)
if self.address_standardizer:
register_pg_extension(metadata, _ADDRESS_STANDARDIZER_EXTENSION)
if self.geography:
register_pg_extension(metadata, _GEOGRAPHY_EXTENSION)
if self.tiger_geocoder:
register_pg_extension(metadata, _TIGER_GEOCODER_EXTENSION)