Source code for codegen_database.views.view

"""Generic view factories."""

from __future__ import annotations

from typing import TYPE_CHECKING

from sqlalchemy import Column, MetaData, Table
from sqlalchemy import types as sa_types
from sqlalchemy_declarative_extensions import View, register_view

from codegen_database.utils.query import compile_query

if TYPE_CHECKING:
    from sqlalchemy import Select
    from sqlalchemy.sql.type_api import TypeEngine


def _column_type(col: object) -> TypeEngine:
    """Extract the SA type from a selected column."""
    return getattr(col, "type", sa_types.NullType())


def _construct_view_proxy_table(
    name: str,
    schema: str,
    query: Select,
) -> Table:
    """Build a joinable ``Table`` proxy from a query.

    The table lives on a private ``MetaData`` so it does not
    interfere with Alembic autogeneration.
    """
    cols = [
        Column(c.key, _column_type(c))
        for c in query.selected_columns
        if c.key is not None
    ]
    return Table(name, MetaData(), *cols, schema=schema)


[docs] class CodegenDatabasePlainView: """Create a plain PostgreSQL view from a SQLAlchemy select. Prefer the declarative :class:`~codegen_database.declarative.CodegenDatabaseView` base class for new views; this class exists for cases where an imperative registration with a ``self.table`` handle is needed. After construction, ``self.table`` is a joinable SQLAlchemy ``Table`` whose columns mirror the query. Args: name: View name. schema: PostgreSQL schema for the view. metadata: SQLAlchemy ``MetaData`` to register on. query: A SQLAlchemy ``Select`` defining the view body. """ def __init__( self, name: str, schema: str, metadata: MetaData, query: Select, ) -> None: """Create and register the view.""" definition = compile_query(query) self.view = View(name, definition, schema=schema) register_view(metadata, self.view) self.name = name self.schema = schema self.metadata = metadata self.table = _construct_view_proxy_table(name, schema, query)
[docs] class CodegenDatabaseMaterializedView: """Create a materialized view with an auto-generated refresh. After construction, ``self.table`` is a joinable SQLAlchemy ``Table`` whose columns mirror the query. Args: name: View name. schema: PostgreSQL schema for the view. metadata: SQLAlchemy ``MetaData`` to register on. query: A SQLAlchemy ``Select`` defining the view body. """ def __init__( self, name: str, schema: str, metadata: MetaData, query: Select, ) -> None: """Create and register the materialized view.""" definition = compile_query(query) self.view = View( name, definition, schema=schema, materialized=True, ) register_view(metadata, self.view) self.name = name self.schema = schema self.metadata = metadata self.table = _construct_view_proxy_table(name, schema, query)