Сопоставление столбцов таблицы¶
Вводная информация по отображению на колонки относится к теме конфигурации Table
; общая форма относится к одной из трех форм:
Объекты Декларативная таблица -
Column
ассоциируются сTable
, а также с отображением ORM за один шаг путем объявления их inline как атрибутов класса.Декларатив с императивной таблицей (также известный как гибридный декларатив) - объекты
Column
ассоциируются непосредственно со своим объектомTable
, как подробно описано в Описание баз данных с помощью метаданных; колонки затем отображаются декларативным процессом путем ассоциированияTable
с отображаемым классом через атрибут__table__
.Императивное картирование - подобно «Imperative Table», объекты
Column
ассоциируются непосредственно с объектомTable
; колонки затем отображаются процессом Imperative с помощьюregistry.map_imperatively()
.
Во всех описанных выше случаях конструктор mapper
в конечном итоге вызывается с заполненным объектом Table
, переданным в качестве выбираемой единицы для отображения. Поведение mapper
заключается в том, чтобы собрать все столбцы в отображаемом Table
в атрибуты отображаемого объекта, каждый из которых назван в соответствии с именем самого столбца (в частности, атрибут key
в Column
). Это поведение может быть изменено несколькими способами.
Именование столбцов отличается от имен атрибутов¶
Сопоставление по умолчанию использует то же имя для Column
, что и для сопоставленного атрибута - в частности, оно соответствует атрибуту Column.key
на Column
, который по умолчанию совпадает с Column.name
.
Имя, присвоенное атрибуту Python, который отображается на Column
, может отличаться от Column.name
или Column.key
, просто присвоив его таким образом, как мы иллюстрируем здесь в декларативном отображении:
class User(Base):
__tablename__ = "user"
id = Column("user_id", Integer, primary_key=True)
name = Column("user_name", String(50))
Где выше User.id
разрешается в колонку с именем user_id
, а User.name
разрешается в колонку с именем user_name
.
При сопоставлении с существующей таблицей на объект Column
можно ссылаться напрямую:
class User(Base):
__table__ = user_table
id = user_table.c.user_id
name = user_table.c.user_name
Соответствующая техника для отображения imperative заключается в том, чтобы поместить нужный ключ в словарь mapper.properties
с нужным ключом:
mapper_registry.map_imperatively(
User,
user_table,
properties={
"id": user_table.c.user_id,
"name": user_table.c.user_name,
},
)
Автоматизация схем именования столбцов из отраженных таблиц¶
В предыдущем разделе Именование столбцов отличается от имен атрибутов мы показали, как Column
, явно отображенный на класс, может иметь имя атрибута, отличное от имени столбца. Но что если мы не перечисляем объекты Column
явно, а вместо этого автоматизируем создание объектов Table
с помощью отражения (т.е. как описано в Отражение объектов базы данных)? В этом случае мы можем использовать событие DDLEvents.column_reflect()
для перехвата производства Column
объектов и предоставления им Column.key
по нашему выбору. Событие проще всего связать с используемым объектом MetaData
, например, ниже мы используем событие, связанное с экземпляром declarative_base
:
@event.listens_for(Base.metadata, "column_reflect")
def column_reflect(inspector, table, column_info):
# set column.key = "attr_<lower_case_name>"
column_info["key"] = "attr_%s" % column_info["name"].lower()
С помощью вышеуказанного события отражение объектов Column
будет перехвачено нашим событием, добавляющим новый элемент «.key», например, в отображении, как показано ниже:
class MyClass(Base):
__table__ = Table("some_table", Base.metadata, autoload_with=some_engine)
Этот подход также работает как с базовым классом DeferredReflection
, так и с расширением Automap. Что касается конкретно automap, смотрите раздел automap_intercepting_columns для справки.
См.также
Декларативное отображение с помощью отраженных таблиц
DDLEvents.column_reflect()
automap_intercepting_columns - в документации Automap
Использование свойства column_property для опций уровня столбца¶
Опции могут быть указаны при отображении Column
с помощью функции column_property()
. Эта функция явно создает ColumnProperty
, используемый mapper()
для отслеживания Column
; обычно mapper()
создает его автоматически. Используя column_property()
, мы можем передать дополнительные аргументы о том, как мы хотим, чтобы Column
был отображен. Ниже мы передаем опцию active_history
, которая указывает, что изменение значения этого столбца должно привести к тому, что сначала будет загружено прежнее значение:
from sqlalchemy.orm import column_property
class User(Base):
__tablename__ = "user"
id = Column(Integer, primary_key=True)
name = column_property(Column(String(50)), active_history=True)
column_property()
также используется для сопоставления одного атрибута с несколькими столбцами. Этот случай возникает при сопоставлении с join()
, который имеет атрибуты, приравненные друг к другу:
class User(Base):
__table__ = user.join(address)
# assign "user.id", "address.user_id" to the
# "id" attribute
id = column_property(user_table.c.id, address_table.c.user_id)
Другие примеры использования этой функции см. в разделе Сопоставление класса с несколькими таблицами.
Еще одно место, где требуется column_property()
- это указание SQL-выражений в качестве сопоставленных атрибутов, как показано ниже, где мы создаем атрибут fullname
, который является строковой конкатенацией столбцов firstname
и lastname
:
class User(Base):
__tablename__ = "user"
id = Column(Integer, primary_key=True)
firstname = Column(String(50))
lastname = Column(String(50))
fullname = column_property(firstname + " " + lastname)
Смотрите примеры такого использования в Выражения SQL как сопоставленные атрибуты.
Сопоставление с явным набором столбцов первичного ключа¶
Конструкция Mapper
для успешного отображения таблицы всегда требует, чтобы по крайней мере один столбец был определен как «первичный ключ» для этой выбираемой таблицы. Это необходимо для того, чтобы при загрузке или сохранении объекта ORM его можно было поместить в identity map с соответствующим identity key.
Для поддержки этого варианта использования все объекты FromClause
(где FromClause
является общей базой для таких объектов, как Table
, Join
, Subquery
и т.д.) имеют атрибут , который возвращает коллекцию тех объектов , которые указывают на то, что они являются частью «первичного ключа». ) имеют атрибут FromClause.primary_key
, который возвращает коллекцию тех объектов Column
, которые указывают, что они являются частью «первичного ключа», который является производным от каждого объекта Column
, являющегося членом коллекции PrimaryKeyConstraint
, связанной с Table
, от которого они в конечном итоге происходят.
В тех случаях, когда сопоставляемый selectable не включает столбцы, которые явно являются частью ограничения первичного ключа в родительской таблице, необходимо определить набор столбцов первичного ключа, определяемый пользователем. Для этого используется параметр mapper.primary_key
.
Учитывая следующий пример отображения Imperative Table на существующий объект Table
, как это происходит в сценарии, например, когда Table
были reflected из существующей базы данных, где таблица не имеет объявленного первичного ключа, мы можем отобразить такую таблицу, как в следующем примере:
from sqlalchemy import Column
from sqlalchemy import MetaData
from sqlalchemy import String
from sqlalchemy import Table
from sqlalchemy import UniqueConstraint
from sqlalchemy.orm import declarative_base
metadata = MetaData()
group_users = Table(
"group_users",
metadata,
Column("user_id", String(40), nullable=False),
Column("group_id", String(40), nullable=False),
UniqueConstraint("user_id", "group_id"),
)
Base = declarative_base()
class GroupUsers(Base):
__table__ = group_users
__mapper_args__ = {"primary_key": [group_users.c.user_id, group_users.c.group_id]}
Выше, таблица group_users
представляет собой некую ассоциативную таблицу со строковыми столбцами user_id
и group_id
, но первичный ключ не установлен; вместо этого есть только UniqueConstraint
, устанавливающий, что эти два столбца представляют собой уникальный ключ. Mapper
не проверяет автоматически уникальные ограничения для первичных ключей; вместо этого мы используем параметр mapper.primary_key
, передавая коллекцию [group_users.c.user_id, group_users.c.group_id]
, указывающую, что эти два столбца должны быть использованы для построения ключа идентификации для экземпляров класса GroupUsers
.
Сопоставление подмножества столбцов таблицы¶
Иногда объект Table
был доступен с помощью процесса отражения, описанного в Отражение объектов базы данных для загрузки структуры таблицы из базы данных. Для такой таблицы, имеющей много столбцов, на которые не нужно ссылаться в приложении, аргументы include_properties
или exclude_properties
могут указывать, что отображать следует только подмножество столбцов. Например:
class User(Base):
__table__ = user_table
__mapper_args__ = {"include_properties": ["user_id", "user_name"]}
…сопоставит класс User
с таблицей user_table
, включая только столбцы user_id
и user_name
- на остальные столбцы ссылки нет. Аналогично:
class Address(Base):
__table__ = address_table
__mapper_args__ = {"exclude_properties": ["street", "city", "state", "zip"]}
…сопоставит класс Address
с таблицей address_table
, включая все присутствующие столбцы, кроме street
, city
, state
и zip
.
Когда используется это отображение, на столбцы, которые не включены, не будет ссылок ни в каких операторах SELECT, выдаваемых Query
, ни в каком-либо сопоставленном атрибуте сопоставленного класса, который представляет столбец; присвоение атрибута с таким именем не будет иметь никакого эффекта, кроме обычного присвоения атрибута в Python.
В некоторых случаях несколько столбцов могут иметь одинаковое имя, например, при сопоставлении с объединением двух или более таблиц, имеющих общее имя столбца. Объекты include_properties
и exclude_properties
также могут содержать объекты Column
для более точного описания того, какие столбцы должны быть включены или исключены:
class UserAddress(Base):
__table__ = user_table.join(addresses_table)
__mapper_args__ = {
"exclude_properties": [address_table.c.id],
"primary_key": [user_table.c.id],
}
Примечание
функции вставки и обновления по умолчанию, настроенные на отдельные объекты Column
, т.е. те, которые описаны в Колонки INSERT/UPDATE по умолчанию, включая те, которые настроены параметрами Column.default
, Column.onupdate
, Column.server_default
и Column.server_onupdate
, будут продолжать нормально функционировать, даже если эти объекты Column
не отображены. Это происходит потому, что в случае Column.default
и Column.onupdate
объект Column
все еще присутствует на базовом Table
, что позволяет функциям по умолчанию работать, когда ORM выдает INSERT или UPDATE, а в случае Column.server_default
и Column.server_onupdate
сама реляционная база данных выдает эти параметры по умолчанию как поведение на стороне сервера.