Декларативные расширения

Расширения, специфичные для API отображения Declarative.

Изменено в версии 1.4: Большая часть расширения Declarative теперь интегрирована в SQLAlchemy ORM и импортируется из пространства имен sqlalchemy.orm. Новую документацию смотрите по адресу Декларативное отображение. Обзор изменений приведен в разделе Декларативность теперь интегрирована в ORM с новыми возможностями.

Object Name Description

AbstractConcreteBase

Вспомогательный класс для «конкретных» декларативных отображений.

ConcreteBase

Вспомогательный класс для «конкретных» декларативных отображений.

DeferredReflection

Вспомогательный класс для построения отображений на основе отложенного шага отражения.

class sqlalchemy.ext.declarative.AbstractConcreteBase

Вспомогательный класс для «конкретных» декларативных отображений.

AbstractConcreteBase будет автоматически использовать функцию polymorphic_union() для всех таблиц, отображенных как подкласс на этот класс. Функция вызывается через функцию __declare_first__(), которая по сути является крючком для события before_configured().

AbstractConcreteBase применяет Mapper для своего непосредственно наследуемого класса, как и для любого другого декларативно сопоставленного класса. Однако Mapper не сопоставляется с каким-либо конкретным объектом Table. Вместо этого он сопоставляется непосредственно с «полиморфным» selectable, порожденным polymorphic_union(), и не выполняет никаких операций сохранения самостоятельно. Сравните с ConcreteBase, который сопоставляет свой непосредственно наследуемый класс с реальным Table, хранящим строки напрямую.

Примечание

AbstractConcreteBase задерживает создание маппера базового класса до тех пор, пока не будут определены все подклассы, поскольку необходимо создать отображение на selectable, включающее все таблицы подклассов. Для этого необходимо дождаться события mapper configuration, после чего просмотреть все сконфигурированные подклассы и создать отображение, которое будет запрашивать сразу все подклассы.

Хотя обычно это событие вызывается автоматически, в случае AbstractConcreteBase может потребоваться его явный вызов после определения всех отображений подклассов, если первой операцией будет запрос к этому базовому классу. Для этого после настройки всех нужных классов можно вызвать метод registry.configure() на используемом registry, который доступен по отношению к конкретному декларативному базовому классу:

Base.registry.configure()

Пример:

from sqlalchemy.orm import DeclarativeBase
from sqlalchemy.ext.declarative import AbstractConcreteBase

class Base(DeclarativeBase):
    pass

class Employee(AbstractConcreteBase, Base):
    pass

class Manager(Employee):
    __tablename__ = 'manager'
    employee_id = Column(Integer, primary_key=True)
    name = Column(String(50))
    manager_data = Column(String(40))

    __mapper_args__ = {
        'polymorphic_identity':'manager',
        'concrete':True
    }

Base.registry.configure()

Абстрактный базовый класс обрабатывается в declarative особым образом: во время конфигурирования класса он ведет себя как декларативный миксин или базовый класс __abstract__. После того как классы сконфигурированы и созданы отображения, он сам становится отображаемым, но после всех своих потомков. Это очень уникальная система отображения, не встречающаяся ни в одной другой возможности SQLAlchemy API.

Используя этот подход, мы можем задавать столбцы и свойства, которые будут иметь место у отображаемых подклассов, так, как это обычно делается в Миксины и пользовательские базовые классы:

from sqlalchemy.ext.declarative import AbstractConcreteBase

class Company(Base):
    __tablename__ = 'company'
    id = Column(Integer, primary_key=True)

class Employee(AbstractConcreteBase, Base):
    strict_attrs = True

    employee_id = Column(Integer, primary_key=True)

    @declared_attr
    def company_id(cls):
        return Column(ForeignKey('company.id'))

    @declared_attr
    def company(cls):
        return relationship("Company")

class Manager(Employee):
    __tablename__ = 'manager'

    name = Column(String(50))
    manager_data = Column(String(40))

    __mapper_args__ = {
        'polymorphic_identity':'manager',
        'concrete':True
    }

Base.registry.configure()

Однако, когда мы используем наши отображения, и Manager, и Employee будут иметь независимо используемый атрибут .company:

session.execute(
    select(Employee).filter(Employee.company.has(id=5))
)
Параметры:

strict_attrs – при указании на базовый класс включается «строгий» режим работы с атрибутами, который пытается ограничить отображаемые в ORM атрибуты базового класса только теми, которые присутствуют непосредственно, сохраняя при этом «полиморфное» поведение при загрузке. … версия добавлена:: 2.0

class sqlalchemy.ext.declarative.ConcreteBase

Вспомогательный класс для «конкретных» декларативных отображений.

ConcreteBase будет автоматически использовать функцию polymorphic_union() для всех таблиц, отображенных как подкласс на этот класс. Функция вызывается через функцию __declare_last__(), которая по сути является крючком для события after_configured().

ConcreteBase создает таблицу отображения для самого класса. Сравните с AbstractConcreteBase, который этого не делает.

Пример:

from sqlalchemy.ext.declarative import ConcreteBase

class Employee(ConcreteBase, Base):
    __tablename__ = 'employee'
    employee_id = Column(Integer, primary_key=True)
    name = Column(String(50))
    __mapper_args__ = {
                    'polymorphic_identity':'employee',
                    'concrete':True}

class Manager(Employee):
    __tablename__ = 'manager'
    employee_id = Column(Integer, primary_key=True)
    name = Column(String(50))
    manager_data = Column(String(40))
    __mapper_args__ = {
                    'polymorphic_identity':'manager',
                    'concrete':True}

По умолчанию имя столбца-дискриминатора, используемого в polymorphic_union(), имеет имя type. Для того чтобы удовлетворить требованиям отображения, в котором реальный столбец в отображаемой таблице уже имеет имя type, имя дискриминатора может быть сконфигурировано путем установки атрибута _concrete_discriminator_name:

class Employee(ConcreteBase, Base):
    _concrete_discriminator_name = '_concrete_discriminator'

Добавлено в версии 1.3.19: В ConcreteBase добавлен атрибут _concrete_discriminator_name для того, чтобы можно было настроить имя столбца виртуального дискриминатора.

Изменено в версии 1.4.2: Атрибут _concrete_discriminator_name необходимо поместить только в самый базовый класс, чтобы он корректно действовал для всех подклассов. При конфликте имен отображаемых столбцов с именами дискриминаторов теперь выдается явное сообщение об ошибке, тогда как в версии 1.3.x выдавались предупреждения, а затем формировался неиспользуемый запрос.

class sqlalchemy.ext.declarative.DeferredReflection

Вспомогательный класс для построения отображений на основе отложенного шага отражения.

Обычно декларативное отображение можно использовать с отражением, задав в качестве атрибута __table__ декларативного класса объект Table, использующий autoload_with=engine. Оговорка заключается в том, что Table должен быть полностью отражен или, по крайней мере, иметь колонку первичного ключа на момент построения обычного декларативного отображения, то есть Engine должен быть доступен во время объявления класса.

Миксин DeferredReflection переносит построение отображателей на более поздний момент, после вызова определенного метода, который сначала отражает все созданные на данный момент объекты Table. Классы могут определять его следующим образом:

from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.ext.declarative import DeferredReflection
Base = declarative_base()

class MyClass(DeferredReflection, Base):
    __tablename__ = 'mytable'

Выше указано, что MyClass еще не сопоставлен. После определения ряда классов описанным выше способом все таблицы могут быть отражены и созданы отображения с помощью prepare():

engine = create_engine("someengine://...")
DeferredReflection.prepare(engine)

Миксин DeferredReflection может применяться к отдельным классам, использоваться в качестве основы для декларативной базы или использоваться в пользовательском абстрактном классе. Использование абстрактной базы позволяет подготовить только подмножество классов для конкретного шага подготовки, что необходимо для приложений, использующих более одного движка. Например, если приложение имеет два движка, то можно использовать две базы и готовить каждую отдельно, например:

class ReflectedOne(DeferredReflection, Base):
    __abstract__ = True

class ReflectedTwo(DeferredReflection, Base):
    __abstract__ = True

class MyClass(ReflectedOne):
    __tablename__ = 'mytable'

class MyOtherClass(ReflectedOne):
    __tablename__ = 'myothertable'

class YetAnotherClass(ReflectedTwo):
    __tablename__ = 'yetanothertable'

# ... etc.

Выше иерархии классов для ReflectedOne и ReflectedTwo могут быть настроены отдельно:

ReflectedOne.prepare(engine_one)
ReflectedTwo.prepare(engine_two)

Members

prepare()

classmethod sqlalchemy.ext.declarative.DeferredReflection.prepare(bind: Union[Engine, Connection], **reflect_kw: Any) None

Отразить все Table объекты для всех текущих DeferredReflection подклассов

Параметры:
Вернуться на верх