Indexable

Definieren Sie Attribute für ORM-gemappte Klassen, die „Index“-Attribute für Spalten mit Indexable-Typen haben.

„Index“ bedeutet, dass das Attribut mit einem Element einer Indexable-Spalte mit dem vordefinierten Index für den Zugriff darauf verbunden ist. Zu den Indexable-Typen gehören Typen wie ARRAY, JSON und HSTORE.

Die Erweiterung indexable bietet eine Column-ähnliche Schnittstelle für jedes Element einer Spalte vom Typ Indexable. In einfachen Fällen kann sie als Column-gemapptes Attribut behandelt werden.

Zusammenfassung

Angenommen, Person ist ein Modell mit einem Primärschlüssel und einem JSON-Datenfeld. Obwohl dieses Feld beliebige codierte Elemente enthalten kann, möchten wir auf das Element namens name individuell als dediziertes Attribut verweisen, das sich wie eine eigenständige Spalte verhält.

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")

Oben verhält sich das Attribut name jetzt wie eine gemappte Spalte. Wir können eine neue Person erstellen und den Wert von name festlegen.

>>> person = Person(name="Alchemist")

Der Wert ist jetzt zugänglich.

>>> person.name
'Alchemist'

Hinter den Kulissen wurde das JSON-Feld mit einem neuen leeren Wörterbuch initialisiert und das Feld gesetzt.

>>> person.data
{'name': 'Alchemist'}

Das Feld ist direkt änderbar.

>>> person.name = "Renamed"
>>> person.name
'Renamed'
>>> person.data
{'name': 'Renamed'}

Bei Verwendung von index_property wird die Änderung, die wir an der indexierbaren Struktur vornehmen, auch automatisch als Historie verfolgt; wir müssen MutableDict nicht mehr verwenden, um diese Änderung für die Arbeitseinheit zu verfolgen.

Löschungen funktionieren ebenfalls normal.

>>> del person.name
>>> person.data
{}

Oben löscht das Löschen von person.name den Wert aus dem Wörterbuch, aber nicht das Wörterbuch selbst.

Ein fehlender Schlüssel führt zu einem AttributeError.

>>> person = Person()
>>> person.name
AttributeError: 'name'

Es sei denn, Sie legen einen Standardwert fest.

>>> 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

Die Attribute sind auch auf Klassenebene zugänglich. Im Folgenden veranschaulichen wir, wie Person.name zur Generierung einer indizierten SQL-Klausel verwendet wird.

>>> from sqlalchemy.orm import Session
>>> session = Session()
>>> query = session.query(Person).filter(Person.name == "Alchemist")

Die obige Abfrage ist äquivalent zu

>>> query = session.query(Person).filter(Person.data["name"] == "Alchemist")

Mehrere index_property-Objekte können verkettet werden, um mehrere Indizierungsebenen zu erzeugen.

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")

Oben eine Abfrage wie

q = session.query(Person).filter(Person.year == "1980")

Auf einem PostgreSQL-Backend wird die obige Abfrage wie folgt gerendert:

SELECT person.id, person.data
FROM person
WHERE person.data -> %(data_1)s -> %(param_1)s = %(param_2)s

Standardwerte

index_property enthält spezielle Verhaltensweisen, wenn die indexierte Datenstruktur nicht existiert und eine Set-Operation aufgerufen wird.

  • Für ein index_property, das einen ganzzahligen Indexwert erhält, ist die Standarddatenstruktur eine Python-Liste von None-Werten, die mindestens so lang ist wie der Indexwert; der Wert wird dann an seiner Stelle in der Liste gesetzt. Das bedeutet, dass für einen Indexwert von Null die Liste vor dem Setzen des gegebenen Werts zu [None] initialisiert wird, und für einen Indexwert von Fünf wird die Liste vor dem Setzen des fünften Elements auf den gegebenen Wert zu [None, None, None, None, None] initialisiert. Beachten Sie, dass eine vorhandene Liste **nicht** zur Laufzeit erweitert wird, um einen Wert aufzunehmen.

  • Für ein index_property, das einen anderen Indexwerttyp erhält (z. B. normalerweise Strings), wird ein Python-Wörterbuch als Standarddatenstruktur verwendet.

  • Die Standarddatenstruktur kann mit dem Parameter index_property.datatype auf einen beliebigen Python-Callable gesetzt werden, wodurch die vorherigen Regeln überschrieben werden.

Unterklassenbildung

index_property kann unterklassifiziert werden, insbesondere für den häufigen Anwendungsfall der Umwandlung von Werten oder SQL-Ausdrücken beim Zugriff. Unten ein gängiges Rezept für die Verwendung mit einem PostgreSQL JSON-Typ, bei dem wir auch die automatische Typumwandlung plus astext() einbeziehen möchten.

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)

Die obige Unterklasse kann mit der PostgreSQL-spezifischen Version von JSON verwendet werden.

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)

Das Attribut age auf Instanzebene funktioniert wie zuvor; beim Rendern von SQL wird jedoch der PostgreSQL-Operator ->> für den indizierten Zugriff verwendet, anstelle des üblichen Indexoperators ->.

>>> query = session.query(Person).filter(Person.age < 20)

Die obige Abfrage wird gerendert: .. sourcecode:: sql

SELECT person.id, person.data FROM person WHERE CAST(person.data ->> %(data_1)s AS INTEGER) < %(param_1)s

API-Referenz

Objektname Beschreibung

index_property

Ein Eigenschaften-Generator. Die generierte Eigenschaft beschreibt ein Objektattribut, das einer Indexable-Spalte entspricht.

class sqlalchemy.ext.indexable.index_property

Ein Eigenschaften-Generator. Die generierte Eigenschaft beschreibt ein Objektattribut, das einer Indexable-Spalte entspricht.

Mitglieder

__init__()

method sqlalchemy.ext.indexable.index_property.__init__(attr_name, index, default=<object object>, datatype=None, mutable=True, onebased=True)

Erstellt ein neues index_property.

Parameter:
  • attr_name – Ein Attributname einer Spalte vom Typ Indexable oder ein anderes Attribut, das eine indexierbare Struktur zurückgibt.

  • index – Der für das Abrufen und Setzen dieses Werts zu verwendende Index. Dies sollte der Python-seitige Indexwert für Ganzzahlen sein.

  • default – Ein Wert, der anstelle von AttributeError zurückgegeben wird, wenn an einem bestimmten Index kein Wert vorhanden ist.

  • datatype – Standarddatentyp, der verwendet werden soll, wenn das Feld leer ist. Standardmäßig wird dies vom Typ des verwendeten Indexes abgeleitet; eine Python-Liste für einen Ganzzahlindex oder ein Python-Wörterbuch für andere Arten von Indizes. Für eine Liste wird die Liste mit None-Werten initialisiert, die mindestens index Elemente lang ist.

  • mutable – Wenn False, sind Schreib- und Löschvorgänge für das Attribut nicht zulässig.

  • onebased – Gehen Sie davon aus, dass die SQL-Darstellung dieses Werts eindimensional ist; das heißt, der erste Index in SQL ist 1 und nicht 0.