ORM Beispiele

Die SQLAlchemy-Distribution enthält eine Vielzahl von Codebeispielen, die eine ausgewählte Reihe von Mustern veranschaulichen, einige typisch und einige nicht so typisch. Alle sind lauffähig und befinden sich im Verzeichnis /examples der Distribution. Beschreibungen und Quellcode für alle finden Sie hier.

Zusätzliche SQLAlchemy-Beispiele, einige von Benutzern beigesteuert, sind im Wiki unter https://sqlalchemy.de/trac/wiki/UsageRecipes verfügbar.

Mapping Rezepte

Nachbarlistenmodell

Ein Beispiel für eine Wörterbuch-von-Wörterbüchern-Struktur, die mit einem Nachbarlistenmodell gemappt wurde.

Z. B.

node = TreeNode("rootnode")
node.append("node1")
node.append("node3")
session.add(node)
session.commit()

dump_tree(node)

Liste der Dateien

Assoziationen

Beispiele, die die Verwendung des "Assoziationsobjekt"-Musters veranschaulichen, bei dem eine Vermittlerklasse die Beziehung zwischen zwei Klassen vermittelt, die in einem Many-to-Many-Muster assoziiert sind.

Liste der Dateien

  • proxied_association.py - Gleiches Beispiel wie basic_association, mit Hinzufügung der Verwendung von sqlalchemy.ext.associationproxy, um explizite Verweise auf OrderItem optional zu machen.

  • basic_association.py - Veranschaulicht eine Many-to-Many-Beziehung zwischen einer „Order“ und einer Sammlung von „Item“-Objekten, wobei über ein Assoziationsobjekt namens „OrderItem“ ein Kaufpreis zugeordnet wird.

  • dict_of_sets_with_default.py - Ein fortgeschrittenes Assoziationsproxy-Beispiel, das die Verschachtelung von Assoziationsproxys veranschaulicht, um mehrstufige Python-Sammlungen zu erzeugen, in diesem Fall ein Wörterbuch mit Zeichenketten-Schlüsseln und Mengen von Ganzzahlen als Werten, die die zugrunde liegenden gemappten Klassen verbergen.

Asyncio Integration

Beispiele, die das Asyncio-Engine-Feature von SQLAlchemy veranschaulichen.

Liste der Dateien

  • async_orm.py - Veranschaulicht die Verwendung des sqlalchemy.ext.asyncio.AsyncSession-Objekts für asynchrone ORM-Nutzung.

  • async_orm_writeonly.py - Veranschaulicht die Verwendung von **Schreibgeschützten Beziehungen** für eine einfachere Handhabung von ORM-Sammlungen unter asyncio.

  • gather_orm_statements.py - Veranschaulicht die gleichzeitige Ausführung vieler Anweisungen mit asyncio.gather() über viele asyncio-Datenbankverbindungen, wobei ORM-Ergebnisse in einer einzigen AsyncSession zusammengeführt werden.

  • basic.py - Veranschaulicht die Asyncio-Engine / Connection-Schnittstelle.

  • greenlet_orm.py - Veranschaulicht die Verwendung des sqlalchemy.ext.asyncio.AsyncSession-Objekts für asynchrone ORM-Nutzung, einschließlich der optionalen run_sync()-Methode.

Gerichtete Graphen

Ein Beispiel für die Persistenz einer gerichteten Graphenstruktur. Der Graph wird als eine Sammlung von Kanten gespeichert, die jeweils sowohl einen "unteren" als auch einen "oberen" Knoten in einer Knotentabelle referenzieren. Grundlegende Persistenz und Abfrage von unteren und oberen Nachbarn werden veranschaulicht.

n2 = Node(2)
n5 = Node(5)
n2.add_neighbor(n5)
print(n2.higher_neighbors())

Liste der Dateien

Dynamische Beziehungen als Wörterbücher

Veranschaulicht, wie eine wörterbuchähnliche Fassade über einer "dynamischen" Beziehung platziert werden kann, sodass Wörterbuchoperationen (bei einfachen Zeichenketten-Schlüsseln) auf einer großen Sammlung operieren können, ohne die gesamte Sammlung auf einmal zu laden.

Liste der Dateien

Generische Assoziationen

Veranschaulicht verschiedene Methoden zur Zuordnung mehrerer übergeordneter Typen zu einem bestimmten untergeordneten Objekt.

Die Beispiele verwenden alle die deklarative Erweiterung zusammen mit deklarativen Mixins. Jedes von ihnen präsentiert am Ende den identischen Anwendungsfall: zwei Klassen, Customer und Supplier, die beide von dem HasAddresses-Mixin erben, der sicherstellt, dass die übergeordnete Klasse eine addresses-Sammlung bereitstellt, die Address-Objekte enthält.

Die Skripte discriminator_on_association.py und generic_fk.py sind modernisierte Versionen von Rezepten, die im Blogbeitrag von 2007 Polymorphic Associations with SQLAlchemy vorgestellt wurden.

Liste der Dateien

  • table_per_association.py - Veranschaulicht einen Mixin, der eine generische Assoziation über individuell generierte Assoziationstabellen für jede übergeordnete Klasse bereitstellt. Die assoziierten Objekte selbst werden in einer einzigen Tabelle gespeichert, die von allen übergeordneten Klassen gemeinsam genutzt wird.

  • table_per_related.py - Veranschaulicht eine generische Assoziation, die Assoziationsobjekte in einzelnen Tabellen speichert, die jeweils so generiert sind, dass sie diese Objekte im Auftrag einer bestimmten übergeordneten Klasse speichern.

  • discriminator_on_association.py - Veranschaulicht einen Mixin, der eine generische Assoziation unter Verwendung einer einzigen Zieltabelle und einer einzigen Assoziationstabelle bereitstellt, auf die von allen übergeordneten Tabellen verwiesen wird. Die Assoziationstabelle enthält eine "Diskriminator"-Spalte, die bestimmt, welcher übergeordnete Objekttyp mit jeder einzelnen Zeile in der Assoziationstabelle assoziiert ist.

  • generic_fk.py - Veranschaulicht einen sogenannten "generischen Fremdschlüssel", ähnlich wie in beliebten Frameworks wie Django, ROR usw. Dieser Ansatz umgeht Standard-Referenzintegritätspraktiken, da die "Fremdschlüssel"-Spalte nicht tatsächlich auf eine bestimmte Tabelle verweist; stattdessen wird Anwendungslogik verwendet, um zu bestimmen, auf welche Tabelle verwiesen wird.

Materialisierte Pfade

Veranschaulicht das Muster "materialisierte Pfade" für hierarchische Daten unter Verwendung des SQLAlchemy ORM.

Liste der Dateien

Verschachtelte Mengen

Veranschaulicht eine rudimentäre Möglichkeit, das Muster "verschachtelte Mengen" für hierarchische Daten unter Verwendung des SQLAlchemy ORM zu implementieren.

Liste der Dateien

Performance

Eine Leistungsprofilierungs-Suite für eine Vielzahl von SQLAlchemy-Anwendungsfällen.

Jede Suite konzentriert sich auf einen bestimmten Anwendungsfall mit einem bestimmten Leistungsprofil und den damit verbundenen Implikationen

  • Massen-Inserts

  • einzelne Inserts, mit oder ohne Transaktionen

  • Abrufen großer Mengen von Zeilen

  • Ausführen vieler kurzer Abfragen

Alle Suiten enthalten eine Vielzahl von Anwendungsfällen, die sowohl die Verwendung von Core als auch von ORM veranschaulichen, und sind im Allgemeinen nach Leistung sortiert, von der schlechtesten zur besten, umgekehrt basierend auf dem von SQLAlchemy bereitgestellten Funktionsumfang, von der größten zur kleinsten (diese beiden Dinge entsprechen im Allgemeinen perfekt).

Ein Kommandozeilen-Tool auf Paketebene wird vorgestellt, das die Ausführung einzelner Suiten ermöglicht.

$ python -m examples.performance --help
usage: python -m examples.performance [-h] [--test TEST] [--dburl DBURL]
                                      [--num NUM] [--profile] [--dump]
                                      [--echo]

                                      {bulk_inserts,large_resultsets,single_inserts}

positional arguments:
  {bulk_inserts,large_resultsets,single_inserts}
                        suite to run

optional arguments:
  -h, --help            show this help message and exit
  --test TEST           run specific test name
  --dburl DBURL         database URL, default sqlite:///profile.db
  --num NUM             Number of iterations/items/etc for tests;
                        default is module-specific
  --profile             run profiling and dump call counts
  --dump                dump full call profile (implies --profile)
  --echo                Echo SQL output

Eine Beispielausführung sieht so aus

$ python -m examples.performance bulk_inserts

Oder mit Optionen

$ python -m examples.performance bulk_inserts \
    --dburl mysql+mysqldb://scott:tiger@localhost/test \
    --profile --num 1000

Dateiliste

Liste der Dateien

  • bulk_updates.py - Diese Testreihe veranschaulicht verschiedene Möglichkeiten, eine große Anzahl von Zeilen im Stapel zu aktualisieren (in Arbeit! Es gibt derzeit nur einen Test).

  • large_resultsets.py - In dieser Testreihe betrachten wir die Zeit, die benötigt wird, um eine große Anzahl von sehr kleinen und einfachen Zeilen zu laden.

  • bulk_inserts.py - Diese Testreihe veranschaulicht verschiedene Möglichkeiten, eine große Anzahl von Zeilen im Stapel einzufügen.

  • short_selects.py - Diese Testreihe veranschaulicht verschiedene Möglichkeiten, einen einzelnen Datensatz nach Primärschlüssel auszuwählen.

  • single_inserts.py - In dieser Testreihe betrachten wir eine Methode, die eine Zeile innerhalb einer separaten Transaktion einfügt und sich danach wieder in einem "geschlossenen" Zustand befindet. Dies wäre analog zu einem API-Aufruf, der eine Datenbankverbindung öffnet, die Zeile einfügt, committet und schließt.

  • __main__.py - Ermöglicht die Ausführung des Pakets examples/performance als Skript.

Alle Tests mit Zeit ausführen

Dies ist die Standardform der Ausführung

$ python -m examples.performance single_inserts
Tests to run: test_orm_commit, test_bulk_save,
              test_bulk_insert_dictionaries, test_core,
              test_core_query_caching, test_dbapi_raw_w_connect,
              test_dbapi_raw_w_pool

test_orm_commit : Individual INSERT/COMMIT pairs via the
    ORM (10000 iterations); total time 13.690218 sec
test_bulk_save : Individual INSERT/COMMIT pairs using
    the "bulk" API  (10000 iterations); total time 11.290371 sec
test_bulk_insert_dictionaries : Individual INSERT/COMMIT pairs using
    the "bulk" API with dictionaries (10000 iterations);
    total time 10.814626 sec
test_core : Individual INSERT/COMMIT pairs using Core.
    (10000 iterations); total time 9.665620 sec
test_core_query_caching : Individual INSERT/COMMIT pairs using Core
    with query caching (10000 iterations); total time 9.209010 sec
test_dbapi_raw_w_connect : Individual INSERT/COMMIT pairs w/ DBAPI +
    connection each time (10000 iterations); total time 9.551103 sec
test_dbapi_raw_w_pool : Individual INSERT/COMMIT pairs w/ DBAPI +
    connection pool (10000 iterations); total time 8.001813 sec

Profile für einzelne Tests ausgeben

Ein Python-Profil-Output kann für alle Tests oder üblicherweise für einzelne Tests ausgegeben werden.

$ python -m examples.performance single_inserts --test test_core --num 1000 --dump
Tests to run: test_core
test_core : Individual INSERT/COMMIT pairs using Core. (1000 iterations); total fn calls 186109
         186109 function calls (186102 primitive calls) in 1.089 seconds

   Ordered by: internal time, call count

   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
     1000    0.634    0.001    0.634    0.001 {method 'commit' of 'sqlite3.Connection' objects}
     1000    0.154    0.000    0.154    0.000 {method 'execute' of 'sqlite3.Cursor' objects}
     1000    0.021    0.000    0.074    0.000 /Users/classic/dev/sqlalchemy/lib/sqlalchemy/sql/compiler.py:1950(_get_colparams)
     1000    0.015    0.000    0.034    0.000 /Users/classic/dev/sqlalchemy/lib/sqlalchemy/engine/default.py:503(_init_compiled)
        1    0.012    0.012    1.091    1.091 examples/performance/single_inserts.py:79(test_core)

    ...

Eigene Suiten schreiben

Das Profiler-Suite-System ist erweiterbar und kann auf Ihre eigenen Tests angewendet werden. Dies ist eine wertvolle Technik, um den richtigen Ansatz für eine leistungskritische Reihe von Routinen zu wählen. Wenn wir beispielsweise den Unterschied zwischen verschiedenen Arten des Ladens profilieren möchten, können wir eine Datei test_loads.py mit folgendem Inhalt erstellen:

from examples.performance import Profiler
from sqlalchemy import Integer, Column, create_engine, ForeignKey
from sqlalchemy.orm import relationship, joinedload, subqueryload, Session
from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()
engine = None
session = None


class Parent(Base):
    __tablename__ = "parent"
    id = Column(Integer, primary_key=True)
    children = relationship("Child")


class Child(Base):
    __tablename__ = "child"
    id = Column(Integer, primary_key=True)
    parent_id = Column(Integer, ForeignKey("parent.id"))


# Init with name of file, default number of items
Profiler.init("test_loads", 1000)


@Profiler.setup_once
def setup_once(dburl, echo, num):
    "setup once.  create an engine, insert fixture data"
    global engine
    engine = create_engine(dburl, echo=echo)
    Base.metadata.drop_all(engine)
    Base.metadata.create_all(engine)
    sess = Session(engine)
    sess.add_all(
        [
            Parent(children=[Child() for j in range(100)])
            for i in range(num)
        ]
    )
    sess.commit()


@Profiler.setup
def setup(dburl, echo, num):
    "setup per test.  create a new Session."
    global session
    session = Session(engine)
    # pre-connect so this part isn't profiled (if we choose)
    session.connection()


@Profiler.profile
def test_lazyload(n):
    "load everything, no eager loading."

    for parent in session.query(Parent):
        parent.children


@Profiler.profile
def test_joinedload(n):
    "load everything, joined eager loading."

    for parent in session.query(Parent).options(joinedload("children")):
        parent.children


@Profiler.profile
def test_subqueryload(n):
    "load everything, subquery eager loading."

    for parent in session.query(Parent).options(subqueryload("children")):
        parent.children


if __name__ == "__main__":
    Profiler.main()

Wir können unser neues Skript direkt ausführen

$ python test_loads.py  --dburl postgresql+psycopg2://scott:tiger@localhost/test
Running setup once...
Tests to run: test_lazyload, test_joinedload, test_subqueryload
test_lazyload : load everything, no eager loading. (1000 iterations); total time 11.971159 sec
test_joinedload : load everything, joined eager loading. (1000 iterations); total time 2.754592 sec
test_subqueryload : load everything, subquery eager loading. (1000 iterations); total time 2.977696 sec

Space Invaders

Ein Space Invaders-Spiel, das SQLite als Zustandsmaschine verwendet.

Ursprünglich im Jahr 2012 entwickelt. Angepasst für die Arbeit in Python 3.

Läuft in einer Textkonsole mit ASCII-Art.

../_images/space_invaders.jpg

Zum Ausführen

$ python -m examples.space_invaders.space_invaders

Während es läuft, beobachten Sie die SQL-Ausgabe im Log

$ tail -f space_invaders.log

Viel Spaß!

Liste der Dateien

Objekte versionieren

Versionierung mit einer Verlaufstabelle

Veranschaulicht eine Erweiterung, die Versionstabellen für Entitäten erstellt und Einträge für jede Änderung speichert. Die gegebenen Erweiterungen generieren eine anonyme "Verlaufsklasse", die historische Versionen des Zielobjekts darstellt.

Vergleichen Sie dies mit den Beispielen Versionierung mittels temporaler Zeilen, die Updates als neue Zeilen in derselben Tabelle schreiben, ohne eine separate Verlaufstabelle zu verwenden.

Die Verwendung wird anhand eines Unit-Test-Moduls test_versioning.py veranschaulicht, das mit dem internen pytest-Plugin von SQLAlchemy ausgeführt wird.

$ pytest test/base/test_examples.py

Ein Fragment der Beispielverwendung, mit deklarativer Syntax

from history_meta import Versioned, versioned_session


class Base(DeclarativeBase):
    pass


class SomeClass(Versioned, Base):
    __tablename__ = "sometable"

    id = Column(Integer, primary_key=True)
    name = Column(String(50))

    def __eq__(self, other):
        assert type(other) is SomeClass and other.id == self.id


Session = sessionmaker(bind=engine)
versioned_session(Session)

sess = Session()
sc = SomeClass(name="sc1")
sess.add(sc)
sess.commit()

sc.name = "sc1modified"
sess.commit()

assert sc.version == 2

SomeClassHistory = SomeClass.__history_mapper__.class_

assert sess.query(SomeClassHistory).filter(
    SomeClassHistory.version == 1
).all() == [SomeClassHistory(version=1, name="sc1")]

Der Versioned-Mixin ist für die Verwendung mit deklarativer Syntax konzipiert. Um die Erweiterung mit klassischen Mappern zu verwenden, kann die Funktion _history_mapper angewendet werden.

from history_meta import _history_mapper

m = mapper(SomeClass, sometable)
_history_mapper(m)

SomeHistoryClass = SomeClass.__history_mapper__.class_

Das Versionierungsbeispiel integriert sich auch in die ORM-optimistische Nebenläufigkeitsfunktion, die unter Konfigurieren eines Versionszählers dokumentiert ist. Um diese Funktion zu aktivieren, setzen Sie das Flag Versioned.use_mapper_versioning auf True.

class SomeClass(Versioned, Base):
    __tablename__ = "sometable"

    use_mapper_versioning = True

    id = Column(Integer, primary_key=True)
    name = Column(String(50))

    def __eq__(self, other):
        assert type(other) is SomeClass and other.id == self.id

Wenn oben zwei Instanzen von SomeClass mit demselben Versionsidentifikator gleichzeitig aktualisiert und zur Aktualisierung an die Datenbank gesendet werden, schlägt eine davon fehl, wenn die Isolationsstufe der Datenbank den beiden UPDATE-Anweisungen erlaubt, fortzufahren, da sie nicht mehr gegen den letzten bekannten Versionsidentifikator abgeglichen wird.

Liste der Dateien

  • test_versioning.py - Unit-Tests, die die Verwendung der Funktionen des Moduls history_meta.py veranschaulichen.

  • history_meta.py - Versioned Mixin-Klasse und andere Hilfsprogramme.

Versionierung mittels temporaler Zeilen

Mehrere Beispiele, die die Technik veranschaulichen, Änderungen abzufangen, die zunächst als UPDATE einer Zeile interpretiert würden, und stattdessen in ein INSERT einer neuen Zeile umzuwandeln, wobei die vorherige Zeile als historische Version erhalten bleibt.

Vergleichen Sie dies mit dem Beispiel Versionierung mit einer Verlaufstabelle, das eine Verlaufzeile in eine separate Verlaufstabelle schreibt.

Liste der Dateien

  • versioned_rows.py - Veranschaulicht eine Methode, um Änderungen an Objekten abzufangen und eine UPDATE-Anweisung für eine einzelne Zeile in eine INSERT-Anweisung umzuwandeln, sodass eine neue Zeile mit den neuen Daten eingefügt wird und die alte Zeile erhalten bleibt.

  • versioned_rows_w_versionid.py - Veranschaulicht eine Methode, um Änderungen an Objekten abzufangen und eine UPDATE-Anweisung für eine einzelne Zeile in eine INSERT-Anweisung umzuwandeln, sodass eine neue Zeile mit den neuen Daten eingefügt wird und die alte Zeile erhalten bleibt.

  • versioned_map.py - Eine Variante des versioned_rows-Beispiels, die auf dem Konzept einer "vertikalen Tabelle" basiert, ähnlich wie in den Beispielen Vertikale Attributzuordnung.

  • versioned_update_old_row.py - Veranschaulicht die gleiche UPDATE-zu-INSERT-Technik wie versioned_rows.py, sendet aber auch ein UPDATE für die **alte** Zeile, um eine Zeitstempeländerung zu bewirken. Enthält auch einen SessionEvents.do_orm_execute()-Hook, um Abfragen auf die aktuellste Version zu beschränken.

Vertikale Attributzuordnung

Veranschaulicht "vertikale Tabellen"-Mappings.

Eine "vertikale Tabelle" bezieht sich auf eine Technik, bei der einzelne Attribute eines Objekts als separate Zeilen in einer Tabelle gespeichert werden. Die Technik der "vertikalen Tabelle" wird verwendet, um Objekte mit einer variablen Anzahl von Attributen zu speichern, was auf Kosten einer einfachen Abfragesteuerung und Kürze geht. Sie wird häufig in Inhalts-/Dokumentenverwaltungssystemen verwendet, um benutzererstellte Strukturen flexibel darzustellen.

Zwei Varianten des Ansatzes werden vorgestellt. In der zweiten verweist jede Zeile auf einen "Datentyp", der Informationen über die Art der in dem Attribut gespeicherten Daten enthält, wie z. B. Ganzzahl, Zeichenkette oder Datum.

Beispiel

shrew = Animal("shrew")
shrew["cuteness"] = 5
shrew["weasel-like"] = False
shrew["poisonous"] = True

session.add(shrew)
session.flush()

q = session.query(Animal).filter(
    Animal.facts.any(
        and_(AnimalFact.key == "weasel-like", AnimalFact.value == True)
    )
)
print("weasel-like animals", q.all())

Liste der Dateien

Vererbungsmuster Rezepte

Grundlegende Vererbungsmuster

Funktionierende Beispiele für Single-Table-, Joined-Table- und Concrete-Table-Vererbung, wie in Mapping von Klassenhierarchien beschrieben.

Liste der Dateien

  • joined.py - Beispiel für Joined-Table-Vererbung (Tabelle pro Unterklasse).

  • concrete.py - Beispiel für Concrete-Table-Vererbung (Tabelle pro Klasse).

  • single.py - Beispiel für Single-Table-Vererbung (Tabelle pro Hierarchie).

Spezielle APIs

Attributinstrumentierung

Beispiele, die Modifikationen am Attributverwaltungssystem von SQLAlchemy veranschaulichen.

Liste der Dateien

Horizontales Sharding

Ein einfaches Beispiel für die Verwendung der SQLAlchemy Sharding API. Sharding bezieht sich auf die horizontale Skalierung von Daten über mehrere Datenbanken hinweg.

Die grundlegenden Komponenten eines "geschardeten" Mappings sind:

  • mehrere Engine-Instanzen, denen jeweils eine "Shard-ID" zugewiesen ist. Diese Engine-Instanzen können sich auf verschiedene Datenbanken, verschiedene Schemas / Konten innerhalb derselben Datenbank beziehen oder sie können auch nur durch Optionen unterschieden werden, die dazu führen, dass sie beim Verwenden auf verschiedene Schemas oder Tabellen zugreifen.

  • eine Funktion, die eine einzelne Shard-ID zurückgeben kann, wenn eine zu speichernde Instanz übergeben wird; dies wird "shard_chooser" genannt.

  • eine Funktion, die eine Liste von Shard-IDs zurückgeben kann, die auf eine bestimmte Instanz-ID zutreffen; dies wird "id_chooser" genannt. Wenn sie alle Shard-IDs zurückgibt, werden alle Shards durchsucht.

  • eine Funktion, die eine Liste von Shard-IDs zurückgeben kann, die versucht werden sollen, wenn eine bestimmte Abfrage (Query) übergeben wird ("query_chooser"). Wenn sie alle Shard-IDs zurückgibt, werden alle Shards abgefragt und die Ergebnisse zusammengeführt.

In diesen Beispielen werden verschiedene Arten von Shards gegen dasselbe Basisbeispiel verwendet, das Wetterdaten pro Kontinent verarbeitet. Wir stellen Beispiel-Shard_Chooser-, ID_Chooser- und Query_Chooser-Funktionen bereit. Der Query_Chooser veranschaulicht die Inspektion des SQL-Ausdruckselements, um zu versuchen, einen einzelnen angeforderten Shard zu ermitteln.

Die Erstellung generischer Sharding-Routinen ist ein ehrgeiziger Ansatz für das Problem der Organisation von Instanzen über mehrere Datenbanken hinweg. Für eine einfachere Alternative ist der "Distinct Entity"-Ansatz eine einfache Methode, um Objekte explizit verschiedenen Tabellen (und potenziell Datenbankknoten) zuzuweisen - beschrieben im Wiki unter EntityName.

Liste der Dateien

  • separate_databases.py - Veranschaulicht Sharding mit separaten SQLite-Datenbanken.

  • separate_tables.py - Veranschaulicht Sharding mit einer einzelnen SQLite-Datenbank, die jedoch mehrere Tabellen mit einer Namenskonvention haben wird.

  • separate_schema_translates.py - Veranschaulicht Sharding mit einer einzelnen Datenbank mit mehreren Schemas, wobei für jeden Shard eine andere "schema_translates_map" verwendet werden kann.

  • asyncio.py - Veranschaulicht die Sharding-API, die mit asyncio verwendet wird.

ORM erweitern

ORM-Abfrageereignisse

Rezepte, die die Erweiterung des ORM SELECT-Verhaltens veranschaulichen, wie es von Session.execute() mit der 2.0-Stil-Nutzung von select() sowie dem 1.x-Stil Query-Objekt verwendet wird.

Beispiele beinhalten Demonstrationen der Option with_loader_criteria() sowie des Hooks SessionEvents.do_orm_execute().

Ab SQLAlchemy 1.4 ist die Query-Konstruktion mit der Select-Konstruktion vereinheitlicht, sodass diese beiden Objekte meist identisch sind.

Liste der Dateien

  • temporal_range.py - Veranschaulicht eine benutzerdefinierte Abfragekriterien, die auf ausgewählte Entitäten angewendet werden.

  • filter_public.py - Veranschaulicht globale Kriterien, die auf Entitäten eines bestimmten Typs angewendet werden.

Dogpile Caching

Veranschaulicht, wie dogpile.cache-Funktionalität mit ORM-Abfragen eingebettet wird, was eine vollständige Cache-Kontrolle sowie die Möglichkeit ermöglicht, "lazy-loaded"-Attribute aus einem Langzeit-Cache abzurufen.

In dieser Demo werden folgende Techniken veranschaulicht:

  • Verwendung des SessionEvents.do_orm_execute()-Ereignishooks

  • Grundlegende Technik, um Session.execute() zu umgehen, um aus einer benutzerdefinierten Cache-Quelle anstelle der Datenbank abzurufen.

  • Rudimentäres Caching mit dogpile.cache unter Verwendung von "Regionen", die eine globale Steuerung über eine feste Menge von Konfigurationen ermöglichen.

  • Verwendung benutzerdefinierter UserDefinedOption-Objekte zur Konfiguration von Optionen in einem Anweisungsobjekt.

Siehe auch

Anweisungen erneut ausführen - enthält ein allgemeines Beispiel der hier vorgestellten Technik.

Z. B.

# query for Person objects, specifying cache
stmt = select(Person).options(FromCache("default"))

# specify that each Person's "addresses" collection comes from
# cache too
stmt = stmt.options(RelationshipCache(Person.addresses, "default"))

# execute and results
result = session.execute(stmt)

print(result.scalars().all())

Zur Ausführung müssen sowohl SQLAlchemy als auch dogpile.cache installiert sein oder sich im aktuellen PYTHONPATH befinden. Die Demo erstellt ein lokales Verzeichnis für Datendateien, fügt anfängliche Daten ein und wird ausgeführt. Die Demo wird ein zweites Mal ausgeführt, wobei die bereits vorhandenen Cache-Dateien verwendet werden. Es wird genau eine SQL-Anweisung gegen zwei Tabellen ausgegeben - das angezeigte Ergebnis verwendet jedoch Dutzende von Lazy-Loads, die alle aus dem Cache geladen werden.

Die Demo-Skripte selbst werden, nach Komplexität geordnet, als Python-Module ausgeführt, damit relative Importe funktionieren.

$ python -m examples.dogpile_caching.helloworld

$ python -m examples.dogpile_caching.relationship_caching

$ python -m examples.dogpile_caching.advanced

$ python -m examples.dogpile_caching.local_session_caching

Liste der Dateien

  • environment.py - Erstellt Daten-/Cache-Dateipfade und Konfigurationen, bootstrappt bei Bedarf Fixture-Daten.

  • caching_query.py - Repräsentiert Funktionen und Klassen, die die Verwendung von Dogpile-Caching mit SQLAlchemy ermöglichen. Führt eine Abfrageoption namens FromCache ein.

  • model.py - Das Datenmodell, das eine Person darstellt, die mehrere Adress-Objekte hat, jedes mit Postleitzahl, Stadt, Land.

  • fixture_data.py - Installiert einige Beispieldaten. Hier haben wir eine Handvoll Postleitzahlen für einige US/kanadische Städte. Dann werden 100 Personendatensätze installiert, jeder mit einer zufällig ausgewählten Postleitzahl.

  • helloworld.py - Veranschaulicht, wie Daten geladen und die Ergebnisse gecached werden.

  • relationship_caching.py - Veranschaulicht, wie Cache-Optionen zu Beziehungsendpunkten hinzugefügt werden, sodass Lazy-Loads aus dem Cache geladen werden.

  • advanced.py - Veranschaulicht die Verwendung von Query in Kombination mit der FromCache-Option, einschließlich Frontend-Laden, Cache-Invalidierung und Sammlungs-Caching.

  • local_session_caching.py - Dieses Beispiel erstellt ein neues dogpile.cache-Backend, das Daten in einem Wörterbuch speichert, das lokal für die aktuelle Sitzung ist. Entfernen Sie die Sitzung, und der Cache ist weg.