2017-06-04 1 views
3

Motivation

Nehmen Sie ein triviales Beispiel, wo eine Spalte von Daten durch einen Aufzählungstyp in SQL dargestellt werden muss:Wie werden in SQLAlchemy Lookup-Tabellendaten deklariert und initialisiert?

+------------------------------------------+ 
| user          | 
+----+------+-----+------------------------+ 
| id | name | age | relationship_status_id | 
+----+------+-----+------------------------+ 
| 1 | John | 27 | 3      | 
| 2 | Mary | 77 | 1      | 
| 3 | Jack | 40 | 4      | 
+----+------+-----+------------------------+ 

+---------------------+ 
| relationship_status | 
+----+----------------+ 
| id | name   | 
+----+----------------+ 
| 1 | married  | 
| 2 | widowed  | 
| 3 | single   | 
| 4 | divorced  | 
+----+----------------+ 

definieren (erklärt) die Tabellen selbst in SQLAlchemy ist relativ einfach:

from sqlalchemy import Column, ForeignKey, Integer, String 
from sqlalchemy.ext.declarative import declarative_base 

Base = declarative_base() 


class User(Base): 
    __tablename__ = 'user' 

    id = Column(Integer, primary_key=True) 
    name = Column(String) 
    age = Column(Integer) 
    relationship_status_id = Column(Integer, ForeignKey('relationship_status.id')) 


class RelationshipStatus(Base): 
    __tablename__ = 'relationship_status' 

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

Bei der Initialisierung der Datenbank können Tabellen mit einer Anweisung Base.metadata.create_all(engine) erstellt werden. Die Tabelle user würde während der Laufzeit der Anwendung gefüllt werden. Die Daten der Lookup-Tabelle relationship_status bleiben jedoch konstant, und es scheint angezeigt, diese Daten zusammen mit der Tabellendefinition zu "deklarieren".

Noch persistente Daten zu einer Tabelle erfordert natürlich eine session, und im Gegensatz zu den Tabellendefinitionen selbst, SQLAlchemy scheint kein deklaratives Konstrukt für "erwartete Zeilen" in einer bestimmten Tabelle (natürlich, da die Mehrheit der Tabellen in jedem Anwendung sind wie user mit dynamischen Daten).

Problem

Mit SQLAlchemy, wie kann man sowohl das Schema und die Daten einer Lookup-Tabelle vor der Anwendung Laufzeit erklären? Idealerweise würde die Lösung die Erstellung eines Enum-ähnlichen Konstrukts beinhalten, das die Daten enthält, auf die sich andere Teile der Anwendung beziehen können.

Forschung

Der Schöpfer von SQLAlchemy vorgeschlagen the enum recipe. Der einzige offensichtliche Nachteil einer solchen Lösung ist, dass man sich innerhalb des verwendeten DBMS auf den enum Datentyp verlassen muss. Für den Umfang dieser Frage wird eine DBMS-unabhängige Lookup-Table-Lösung bevorzugt.

Eine verwandte Alternative, die ebenfalls von SQLAlchemy's Creator vorgeschlagen wird, ist the unique object recipe. Eine solche Implementierung stellt sicher, dass die von Nachschlagetabellenabfragen zurückgegebenen Zeilen ohne Duplikate bleiben, aber ein Objekt session immer noch benötigt wird, um irgendwelche Deklarationen oder Anforderungen zu machen - was die Trennung von Belangen zwischen Datenbankdefinition und Implementierung verwischt. Außerdem müssten die Kunden einfach nur "wissen", nach welchen Zeilen sie fragen sollen, anstatt irgendeine Art von Enumeration (in Python) als Referenz zu haben.


Die Ursache des Problems kann konzeptionell sein und nicht an SQLAlchemy oder Python gebunden sein. In jedem Fall würde jeder Rat sehr geschätzt werden.

Antwort

1

Erstens würde ich argumentieren, dass Daten, die zur Anwendungsentwicklungszeit konstant und bekannt sind, in den meisten Situationen nicht gut zum Speichern in einer Datenbank geeignet sind. Ich würde entweder eine DBMS-basierte Enumeration oder eine Python-basierte Enumeration und eine Check-Constraint verwenden, um sicherzustellen, dass jede Zeile in der Benutzertabelle einen gültigen Beziehungsstatus hat. Da Sie gesagt haben, dass Sie das nicht tun werden, scheint es, als würden Sie nach einer Möglichkeit suchen, einige Einfügungen zu dem Zeitpunkt auszulösen, zu dem Ihre relation_statustabelle erstellt wird. Ich habe angepasst, um etwas in eine Tabelle einzufügen, anstatt die Tabelle zu ändern. Sie sollten dies anpassen können, um Ihre Beziehungsstatuswerte einzufügen.

from sqlalchemy import event 
from sqlalchemy import Table, Column, Metadata, Integer 

m = MetaData() 
some_table = Table('some_table', m, Column('data', Integer)) 

def after_create(target, connection, **kw): 
    connection.execute("insert into %s values ('1','single');" % 
          (target.name)) 

event.listen(some_table, "after_create", after_create) 
+0

Sie könnten String-Formatierung mit ['table()'] vermeiden (http: //docs.sqlalchemy.org/de/latest/core/selectable.html # sqlalchemy.sql.expression.table): 'connection.execute (table (target.name) .insert(). values ​​([{'name': 'single'}, ...])) ', was es auch erlauben würde, die Anfangswerte als Python-Strukturen zu definieren, anstatt sie in die SQL-Abfrage einzufügen. –

+0

Vielen Dank für den Verweis auf den 'after_create' Trigger; es scheint die am besten geeignete Lösung zu sein. Ich bin fasziniert von Ihrer Notiz, dass solche Daten nicht für die Speicherung in einer Datenbank geeignet sind, und ich würde dem eher zustimmen. Aber mit DBMS-basierten Aufzählungen, die systemübergreifend inkonsistent sind, welche Alternativen gibt es für Situationen wie die oben beschriebene? Ich hatte den Eindruck, dass Nachschlagetabellen die akzeptierte Best Practice waren. Wie würden Überprüfungsbedingungen mit einer Python-Enumeration implementiert? – RNanoware

Verwandte Themen