So sind sagen, dass ich einige Klassen X, Y und Z SQLAlchemy deklarative Syntax mit einigen einfachen Spalten und Beziehungen definierenWie kann ich ‚index‘ SQLAlchemy Modell Attribute, die Primärschlüssel und Beziehungen
Anforderungen:
auf Klassenebene,
(X|Y|Z).primary_keys
gibt eine Sammlung von
der jeweiligen Klasse Primärschlüssel (InstrumentedAttribute
Objekte) ich die Klasse Beziehungen in derselben verweisen auch(X|Y|Z).relations
wollen Wegauf Instanzebene, würde ich die gleichen Attribute wie die Attribute instanzierte Werte verweisen, ob sie waren bevölkerten meine eigenen Konstrukteure verwenden, einzelne Attribute
Einrichter, oder was auch immer SQLAlchemy tut, wenn es ruft Zeilen von der DB ab.
Bisher habe ich folgendes.
import collections
import sqlalchemy
import sqlalchemy.ext.declarative
from sqlalchemy import MetaData, Column, Table, ForeignKey, Integer, String, Date, Text
from sqlalchemy.orm import relationship, backref
class IndexedMeta(sqlalchemy.ext.declarative.DeclarativeMeta):
"""Metaclass to initialize some class-level collections on models"""
def __new__(cls, name, bases, defaultdict):
cls.pk_columns = set()
cls.relations = collections.namedtuple('RelationshipItem', 'one many')(set(), set())
return super().__new__(cls, name, bases, defaultdict)
Base = sqlalchemy.ext.declarative.declarative_base(metaclass=IndexedMeta)
def build_class_lens(cls, key, inst):
"""Populates the 'indexes' of primary key and relationship attributes with the attributes' names. Additionally, separates "x to many" relationships from "x to one" relationships and associates "x to one" relathionships with the local-side foreign key column"""
if isinstance(inst.property, sqlalchemy.orm.properties.ColumnProperty):
if inst.property.columns[0].primary_key:
cls.pk_columns.add(inst.key)
elif isinstance(inst.property, sqlalchemy.orm.properties.RelationshipProperty):
if inst.property.direction.name == ('MANYTOONE' or 'ONETOONE'):
local_column = cls.__mapper__.get_property_by_column(inst.property.local_side[0]).key
cls.relations.one.add((local_column, inst.key))
else:
cls.relations.many.add(inst.key)
sqlalchemy.event.listen(Base, 'attribute_instrument', build_class_lens)
class Meeting(Base):
__tablename__ = 'meetings'
def __init__(self, memo):
self.memo = memo
id = Column(Integer, primary_key=True)
date = Column(Date)
memo = Column('note', String(60), nullable=True)
category_name = Column('category', String(60), ForeignKey('categories.name'))
category = relationship("Category", backref=backref('meetings'))
topics = relationship("Topic",
secondary=meetings_topics,
backref="meetings")
...
...
Ok, so dass wird mir auf der Ebene der Klassen durch, obwohl ich fühle mich wie ich dumme Sachen mit metaclasses tue, und ich einige seltsame intermittierende Fehler, bei denen das ‚sqlalchemy‘ -Modul angeblich nicht in erkannt wird build_class_lens
und evals zu Nonetype.
Ich bin mir nicht sicher, wie ich auf der Instanzenebene vorgehen soll. Ich habe in die Ereignisschnittstelle geschaut. Ich sehe das ORM-Ereignis init
, aber es scheint vor der __init__
-Funktion zu laufen, die auf meinen Modellen definiert ist, was bedeutet, dass die Instanzattribute zu dieser Zeit noch nicht gefüllt waren, also kann ich meine "Linse" nicht auf ihnen aufbauen. Ich frage mich auch, ob das Attribut Ereignis set
hilfreich sein könnte. Das ist mein nächster Versuch, obwohl ich mich immer noch frage, ob es der passendste Weg ist.
Alles in allem frage ich mich wirklich, ob mir eine wirklich elegante Möglichkeit fehlt, dieses Problem anzugehen.