Heim  >  Artikel  >  Backend-Entwicklung  >  Best Practices für Alembic und SQLAlchemy

Best Practices für Alembic und SQLAlchemy

Mary-Kate Olsen
Mary-Kate OlsenOriginal
2024-11-01 10:20:30753Durchsuche

In diesem Artikel gehe ich kurz auf einige Best Practices ein, die dabei helfen, Projekte organisiert zu halten, die Datenbankwartung zu vereinfachen und häufige Fallstricke zu vermeiden bei der Arbeit mit Alembic und SQLAlchemy. Diese Techniken haben mich mehr als einmal vor Ärger bewahrt. Folgendes werden wir behandeln:

  1. Namenskonventionen
  2. Migrationen nach Datum sortieren
  3. Tabellen-, Spalten- und Migrationskommentare
  4. Datenverarbeitung bei Migrationen ohne Modelle
  5. Migrationstest (Stufentest)
  6. Dienst zum Ausführen von Migrationen
  7. Mixins für Modelle verwenden

1. Namenskonventionen

SQLAlchemy ermöglicht Ihnen die Einrichtung einer Namenskonvention, die beim Generieren von Migrationen automatisch auf alle Tabellen und Einschränkungen angewendet wird. Dies erspart Ihnen die manuelle Benennung von Indizes, Fremdschlüsseln und anderen Einschränkungen, wodurch die Datenbankstruktur vorhersehbar und konsistent wird.

Um dies in einem neuen Projekt einzurichten, fügen Sie der Basisklasse eine Konvention hinzu, damit Alembic automatisch das gewünschte Benennungsformat verwendet. Hier ist ein Beispiel für eine Konvention, die in den meisten Fällen gut funktioniert:

from sqlalchemy import MetaData
from sqlalchemy.orm import DeclarativeBase

convention = {
    'all_column_names': lambda constraint, table: '_'.join(
        [column.name for column in constraint.columns.values()]
    ),
    'ix': 'ix__%(table_name)s__%(all_column_names)s',
    'uq': 'uq__%(table_name)s__%(all_column_names)s',
    'ck': 'ck__%(table_name)s__%(constraint_name)s',
    'fk': 'fk__%(table_name)s__%(all_column_names)s__%(referred_table_name)s',
    'pk': 'pk__%(table_name)s',
}

class BaseModel(DeclarativeBase):
    metadata = MetaData(naming_convention=convention)

2. Migrationen nach Datum sortieren

Alembic-Migrationsdateinamen beginnen normalerweise mit einem Revisions-Tag, wodurch die Reihenfolge der Migrationen im Verzeichnis zufällig erscheinen kann. Manchmal ist es nützlich, sie chronologisch zu sortieren.

Alembic ermöglicht das Anpassen der Vorlage für den Migrationsdateinamen in der Datei alembic.ini mit der Einstellung file_template. Hier sind zwei praktische Namensformate, um Migrationen organisiert zu halten:

  1. Basierend auf dem Datum:
file_template = %%(year)d-%%(month).2d-%%(day).2d_%%(rev)s_%%(slug)s
  1. Basierend auf dem Unix-Zeitstempel:
file_template = %%(epoch)d_%%(rev)s_%%(slug)s

Durch die Verwendung von Datums- oder Unix-Zeitstempeln in Dateinamen bleiben Migrationen organisiert und die Navigation wird einfacher. Ich bevorzuge die Verwendung von Unix-Zeitstempeln, und ein Beispiel wird im nächsten Abschnitt bereitgestellt.

3. Kommentare für Tabellen und Migrationen

Für diejenigen, die in einem Team arbeiten, ist das Kommentieren von Attributen eine gute Vorgehensweise. Erwägen Sie bei SQLAlchemy-Modellen das direkte Hinzufügen von Kommentaren zu Spalten und Tabellen, anstatt sich auf Dokumentzeichenfolgen zu verlassen. Auf diese Weise sind Kommentare sowohl im Code als auch in der Datenbank verfügbar, was es DBAs oder Analysten erleichtert, Tabellen- und Feldzwecke zu verstehen.

class Event(BaseModel):
    __table_args__ = {'comment': 'System (service) event'}

    id: Mapped[uuid.UUID] = mapped_column(
        UUID(as_uuid=True),
        primary_key=True,
        comment='Event ID - PK',
    )
    service_id: Mapped[int] = mapped_column(
        sa.Integer,
        sa.ForeignKey(
            f'{IntegrationServiceModel.__tablename__}.id',
            ondelete='CASCADE',
        ),
        nullable=False,
        comment='FK to integration service that owns the event',
    )
    name: Mapped[str] = mapped_column(
        sa.String(256), nullable=False, comment='Event name'
    )

Es ist auch hilfreich, Kommentare zu Migrationen hinzuzufügen, damit sie im Dateisystem leichter gefunden werden können. Ein Kommentar kann mit -m hinzugefügt werden. beim Generieren der Migration. Der Kommentar erscheint im Dokumentstring und im Dateinamen. Diese Benennung erleichtert das Auffinden der erforderlichen Migration erheblich.

from sqlalchemy import MetaData
from sqlalchemy.orm import DeclarativeBase

convention = {
    'all_column_names': lambda constraint, table: '_'.join(
        [column.name for column in constraint.columns.values()]
    ),
    'ix': 'ix__%(table_name)s__%(all_column_names)s',
    'uq': 'uq__%(table_name)s__%(all_column_names)s',
    'ck': 'ck__%(table_name)s__%(constraint_name)s',
    'fk': 'fk__%(table_name)s__%(all_column_names)s__%(referred_table_name)s',
    'pk': 'pk__%(table_name)s',
}

class BaseModel(DeclarativeBase):
    metadata = MetaData(naming_convention=convention)

4. Vermeiden Sie die Verwendung von Modellen bei Migrationen

Modelle werden häufig für Datenmanipulationen verwendet, beispielsweise zum Übertragen von Daten von einer Tabelle in eine andere oder zum Ändern von Spaltenwerten. Allerdings kann die Verwendung von ORM-Modellen bei Migrationen zu Problemen führen, wenn sich das Modell nach der Erstellung der Migration ändert. In solchen Fällen wird eine Migration auf Basis des alten Modells bei der Ausführung scheitern, da das Datenbankschema möglicherweise nicht mehr mit dem aktuellen Modell übereinstimmt.

Migrationen sollten statisch und unabhängig vom aktuellen Zustand der Modelle erfolgen, um eine korrekte Ausführung unabhängig von Codeänderungen sicherzustellen. Im Folgenden finden Sie zwei Möglichkeiten, die Verwendung von Modellen für Datenmanipulationen zu vermeiden.

  • Roh-SQL zur Datenbearbeitung verwenden:
file_template = %%(year)d-%%(month).2d-%%(day).2d_%%(rev)s_%%(slug)s
  • Tabellen direkt in der Migration definieren: Wenn Sie SQLAlchemy für Datenmanipulationen verwenden möchten, können Sie Tabellen direkt in der Migration manuell definieren. Dies gewährleistet ein statisches Schema zum Zeitpunkt der Migrationsausführung und ist nicht von Änderungen in den Modellen abhängig.
file_template = %%(epoch)d_%%(rev)s_%%(slug)s

5. Treppentest für Migrationstests

Der Stairway-Test beinhaltet das schrittweise Testen von Upgrade-/Downgrade-Migrationen, um sicherzustellen, dass die gesamte Migrationskette korrekt funktioniert. Dadurch wird sichergestellt, dass bei jeder Migration erfolgreich eine neue Datenbank von Grund auf erstellt und ein Downgrade ohne Probleme durchgeführt werden kann. Das Hinzufügen dieses Tests zu CI ist für Teams von unschätzbarem Wert und spart Zeit und Frustration.

Best Practices for Alembic and SQLAlchemy

Die Integration des Tests in Ihr Projekt ist einfach und schnell möglich. Ein Codebeispiel finden Sie in diesem Repository. Es enthält auch andere wertvolle Migrationstests, die hilfreich sein können.

6. Migrationsdienst

Ein separater Dienst zur Durchführung von Migrationen. Dies ist nur eine Möglichkeit, Migrationen durchzuführen. Bei der Entwicklung vor Ort oder in entwicklungsähnlichen Umgebungen passt diese Methode gut. Ich möchte Sie an die Funktion „Conditional depend_on“ erinnern, die hier relevant ist. Wir nehmen das Anwendungsimage mit Alembic auf und führen es in einem separaten Container aus. Wir fügen eine Abhängigkeit von der Datenbank mit der Bedingung hinzu, dass Migrationen nur beginnen, wenn die Datenbank bereit ist, Anfragen zu verarbeiten (service_healthy). Darüber hinaus kann für die Anwendung eine Bedingung depend_on (service_completed_successfully) hinzugefügt werden, um sicherzustellen, dass sie erst gestartet wird, nachdem die Migrationen erfolgreich abgeschlossen wurden.

class Event(BaseModel):
    __table_args__ = {'comment': 'System (service) event'}

    id: Mapped[uuid.UUID] = mapped_column(
        UUID(as_uuid=True),
        primary_key=True,
        comment='Event ID - PK',
    )
    service_id: Mapped[int] = mapped_column(
        sa.Integer,
        sa.ForeignKey(
            f'{IntegrationServiceModel.__tablename__}.id',
            ondelete='CASCADE',
        ),
        nullable=False,
        comment='FK to integration service that owns the event',
    )
    name: Mapped[str] = mapped_column(
        sa.String(256), nullable=False, comment='Event name'
    )

Die depend_on-Bedingung stellt sicher, dass Migrationen erst ausgeführt werden, wenn die Datenbank vollständig bereit ist, und dass die Anwendung nach Abschluss der Migrationen gestartet wird.

7. Mixins für Modelle

Obwohl dies ein offensichtlicher Punkt sein mag, ist es wichtig, ihn nicht zu übersehen. Die Verwendung von Mixins ist eine praktische Möglichkeit, Codeduplizierung zu vermeiden. Mixins sind Klassen, die häufig verwendete Felder und Methoden enthalten, die in beliebige Modelle integriert werden können, wo sie benötigt werden. Beispielsweise benötigen wir häufig die Felder „created_at“ und „update_at“, um die Erstellungs- und Aktualisierungszeiten von Datensätzen zu verfolgen. Es kann auch nützlich sein, eine auf UUID basierende ID zu verwenden, um Primärschlüssel zu standardisieren. All dies kann in Mixins gekapselt werden.

from sqlalchemy import MetaData
from sqlalchemy.orm import DeclarativeBase

convention = {
    'all_column_names': lambda constraint, table: '_'.join(
        [column.name for column in constraint.columns.values()]
    ),
    'ix': 'ix__%(table_name)s__%(all_column_names)s',
    'uq': 'uq__%(table_name)s__%(all_column_names)s',
    'ck': 'ck__%(table_name)s__%(constraint_name)s',
    'fk': 'fk__%(table_name)s__%(all_column_names)s__%(referred_table_name)s',
    'pk': 'pk__%(table_name)s',
}

class BaseModel(DeclarativeBase):
    metadata = MetaData(naming_convention=convention)

Durch das Hinzufügen dieser Mixins können wir bei Bedarf UUID-IDs und Zeitstempel in jedes Modell einbinden:

file_template = %%(year)d-%%(month).2d-%%(day).2d_%%(rev)s_%%(slug)s

Abschluss

Der Umgang mit Migrationen kann eine Herausforderung sein, aber die Befolgung dieser einfachen Vorgehensweisen trägt dazu bei, dass Projekte gut organisiert und überschaubar bleiben. Namenskonventionen, Datumssortierung, Kommentare und Tests haben mich vor dem Chaos bewahrt und dazu beigetragen, Fehler zu vermeiden. Ich hoffe, dieser Artikel erweist sich als hilfreich – teilen Sie uns gerne Ihre eigenen Migrationstipps in den Kommentaren mit!

Das obige ist der detaillierte Inhalt vonBest Practices für Alembic und SQLAlchemy. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Stellungnahme:
Der Inhalt dieses Artikels wird freiwillig von Internetnutzern beigesteuert und das Urheberrecht liegt beim ursprünglichen Autor. Diese Website übernimmt keine entsprechende rechtliche Verantwortung. Wenn Sie Inhalte finden, bei denen der Verdacht eines Plagiats oder einer Rechtsverletzung besteht, wenden Sie sich bitte an admin@php.cn