Индексируемый¶
Определите атрибуты на ORM-сопоставленных классах, которые имеют атрибуты «index» для столбцов с типом Indexable
.
«индекс» означает, что атрибут связан с элементом столбца Indexable
с заранее определенным индексом для доступа к нему. К типам Indexable
относятся такие типы, как ARRAY
, JSON
и HSTORE
.
Расширение indexable
предоставляет Column
-подобный интерфейс для любого элемента Indexable
типизированного столбца. В простых случаях оно может рассматриваться как Column
- сопоставленный атрибут.
Синопсис¶
В качестве модели с первичным ключом и JSON-полем данных дается Person
. Хотя в этом поле может быть закодировано любое количество элементов, мы хотели бы обращаться к элементу name
отдельно, как к выделенному атрибуту, который ведет себя как отдельный столбец:
from sqlalchemy import Column, JSON, Integer
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.ext.indexable import index_property
Base = declarative_base()
class Person(Base):
__tablename__ = 'person'
id = Column(Integer, primary_key=True)
data = Column(JSON)
name = index_property('data', 'name')
Выше атрибут name
теперь ведет себя как сопоставленный столбец. Мы можем составить новый Person
и установить значение name
:
>>> person = Person(name='Alchemist')
Теперь доступно значение:
>>> person.name
'Alchemist'
За кулисами JSON-поле было инициализировано новым пустым словарем, а поле было установлено:
>>> person.data
{"name": "Alchemist'}
Поле является изменяемым по месту:
>>> person.name = 'Renamed'
>>> person.name
'Renamed'
>>> person.data
{'name': 'Renamed'}
При использовании index_property
изменение, которое мы вносим в индексируемую структуру, также автоматически отслеживается как история; нам больше не нужно использовать MutableDict
для того, чтобы отследить это изменение для единицы работы.
Удаление также работает нормально:
>>> del person.name
>>> person.data
{}
Выше, удаление person.name
удаляет значение из словаря, но не сам словарь.
Отсутствие ключа приводит к появлению AttributeError
:
>>> person = Person()
>>> person.name
...
AttributeError: 'name'
Если не задано значение по умолчанию:
>>> class Person(Base):
>>> __tablename__ = 'person'
>>>
>>> id = Column(Integer, primary_key=True)
>>> data = Column(JSON)
>>>
>>> name = index_property('data', 'name', default=None) # See default
>>> person = Person()
>>> print(person.name)
None
Атрибуты также доступны на уровне класса. Ниже показано использование Person.name
для генерации индексированного SQL-критерия:
>>> from sqlalchemy.orm import Session
>>> session = Session()
>>> query = session.query(Person).filter(Person.name == 'Alchemist')
Приведенный выше запрос эквивалентен:
>>> query = session.query(Person).filter(Person.data['name'] == 'Alchemist')
Несколько объектов index_property
могут быть соединены в цепочку для получения нескольких уровней индексации:
from sqlalchemy import Column, JSON, Integer
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.ext.indexable import index_property
Base = declarative_base()
class Person(Base):
__tablename__ = 'person'
id = Column(Integer, primary_key=True)
data = Column(JSON)
birthday = index_property('data', 'birthday')
year = index_property('birthday', 'year')
month = index_property('birthday', 'month')
day = index_property('birthday', 'day')
Выше приведен запрос типа:
q = session.query(Person).filter(Person.year == '1980')
На внутреннем сервере PostgreSQL приведенный выше запрос будет выглядеть так:
SELECT person.id, person.data
FROM person
WHERE person.data -> %(data_1)s -> %(param_1)s = %(param_2)s
Значения по умолчанию¶
index_property
включает специальное поведение для случая, когда индексируемая структура данных не существует, и вызывается операция set:
Для
index_property
, которому присвоено целочисленное значение индекса, структурой данных по умолчанию будет Python-список значенийNone
, длина которого не меньше значения индекса; значение устанавливается на свое место в списке. Это означает, что при нулевом значении индекса список будет инициализирован до значения[None]
перед установкой заданного значения, а при значении индекса, равном пяти, список будет инициализирован до значения[None, None, None, None, None]
перед установкой пятого элемента в заданное значение. Обратите внимание, что существующий список не расширяется на месте для получения значения.Для
index_property
, которому задано любое другое значение индекса (например, обычно строки), в качестве структуры данных по умолчанию используется словарь Python.Структура данных по умолчанию может быть установлена в любой вызываемый элемент Python с помощью параметра
index_property.datatype
, что отменяет предыдущие правила.
Подклассификация¶
index_property
может быть подклассифицирован, в частности, для такого распространенного случая, как обеспечение коэрцитивности значений или SQL-выражений при обращении к ним. Ниже приведен общий рецепт для использования с типом PostgreSQL JSON, где мы хотим также включить автоматическое приведение плюс astext()
:
class pg_json_property(index_property):
def __init__(self, attr_name, index, cast_type):
super(pg_json_property, self).__init__(attr_name, index)
self.cast_type = cast_type
def expr(self, model):
expr = super(pg_json_property, self).expr(model)
return expr.astext.cast(self.cast_type)
Приведенный подкласс может быть использован с версией JSON
, специфичной для PostgreSQL:
from sqlalchemy import Column, Integer
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.dialects.postgresql import JSON
Base = declarative_base()
class Person(Base):
__tablename__ = 'person'
id = Column(Integer, primary_key=True)
data = Column(JSON)
age = pg_json_property('data', 'age', Integer)
Атрибут age
на уровне экземпляра работает как и раньше, однако при визуализации SQL для индексированного доступа будет использоваться оператор PostgreSQL ->>
, а не обычный индексный оператор ->
:
>>> query = session.query(Person).filter(Person.age < 20)
Приведенный выше запрос выдает:
SELECT person.id, person.data
FROM person
WHERE CAST(person.data ->> %(data_1)s AS INTEGER) < %(param_1)s
Справочник по API¶
Object Name | Description |
---|---|
Генератор свойств. Сгенерированное свойство описывает атрибут объекта, соответствующий столбцу |
- class sqlalchemy.ext.indexable.index_property¶
Генератор свойств. Сгенерированное свойство описывает атрибут объекта, соответствующий столбцу
Indexable
.См.также
Members
Классическая подпись.
класс
sqlalchemy.ext.indexable.index_property
(sqlalchemy.ext.hybrid.hybrid_property
)-
method
sqlalchemy.ext.indexable.index_property.
__init__(attr_name, index, default=<object object>, datatype=None, mutable=True, onebased=True)¶ Создайте новый
index_property
.- Параметры:
attr_name – Имя атрибута типизированного столбца Indexable или другого атрибута, возвращающего индексируемую структуру.
index – Индекс, который будет использоваться для получения и установки данного значения. Это должно быть значение индекса на стороне Python для целых чисел.
default – Значение, которое будет возвращено вместо AttributeError в случае отсутствия значения по заданному индексу.
datatype – тип данных по умолчанию, используемый, когда поле пусто. По умолчанию он определяется типом используемого индекса: список Python для целочисленного индекса или словарь Python для любого другого типа индекса. Для списка список будет инициализирован списком значений None длиной не менее
index
элементов.mutable – если False, то записи и удаления в атрибут будут запрещены.
onebased – предположить, что SQL-представление этого значения является однобазовым, т.е. первый индекс в SQL равен 1, а не нулю.
-
method