Python 我怎么能';指数';作为主键和关系的SQLAlchemy模型属性
假设我有一些类X、Y和Z,它们使用SQLAlchemy声明性语法来定义一些简单的列和关系 要求:Python 我怎么能';指数';作为主键和关系的SQLAlchemy模型属性,python,sqlalchemy,Python,Sqlalchemy,假设我有一些类X、Y和Z,它们使用SQLAlchemy声明性语法来定义一些简单的列和关系 要求: 在类级别,(X | Y | Z)。主键返回一组 相应的类“主键”(InstrumentedAttribute 对象)我还希望(X | Y | Z).relations引用类的 同理 在实例级别,我希望引用相同的属性 这些属性的实例化值,无论它们是否 使用我自己的构造函数、单个属性填充 setters,或者SQLAlchemy在从 db 到目前为止,我有以下几点 import collections
(X | Y | Z)。主键
返回一组相应的类“主键”(
InstrumentedAttribute
对象)我还希望(X | Y | Z).relations
引用类的
同理setters,或者SQLAlchemy在从 db
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")
...
...
好吧,这让我在类级别上过得去,尽管我觉得我在用元类做一些愚蠢的事情,而且我遇到了一些奇怪的间歇性错误,其中“sqlalchemy”模块据称在build\u class\u lens
中未被识别,并评估为Nonetype
我不太确定应该如何在实例级别继续。
我已经研究了事件界面。我看到ORM事件init
,但它似乎在我的模型上定义的\uuuu init\uuuu
函数之前运行,这意味着当时还没有填充实例属性,因此我无法在它们上构建“镜头”。
我还想知道属性eventset
是否有帮助。这是我的下一次尝试,尽管我仍然怀疑这是否是最合适的方式
总而言之,我真的很想知道我是否错过了解决这个问题的一些非常优雅的方法。我认为元类与声明性的关系是按照老的XML说的,“如果你有问题,并且使用XML,现在你有两个问题”。Python中的元类非常有用,可以作为检测新类构造的钩子,仅此而已。我们现在有足够的事件,除了声明性的已经做的事情之外,不需要使用元类 在这种情况下,我会进一步指出,尝试积极构建这些集合的方法并不真正值得——懒散地生成它们要容易得多,如下所示:
from sqlalchemy import *
from sqlalchemy.orm import *
from sqlalchemy.ext.declarative import declarative_base
import collections
from sqlalchemy.orm.properties import RelationshipProperty
class memoized_classproperty(object):
"""A decorator that evaluates once at the class level,
assigns the new value to the class.
"""
def __init__(self, fget, doc=None):
self.fget = fget
self.__doc__ = doc or fget.__doc__
self.__name__ = fget.__name__
def __get__(desc, self, cls):
result = desc.fget(cls)
setattr(cls, desc.__name__, result)
return result
class Lens(object):
@memoized_classproperty
def pk_columns(cls):
return class_mapper(cls).primary_key
@memoized_classproperty
def relations(cls):
props = collections.namedtuple('RelationshipItem', 'one many')(set(), set())
# 0.8 will have "inspect(cls).relationships" here
mapper = class_mapper(cls)
for item in mapper.iterate_properties:
if isinstance(item, RelationshipProperty):
if item.direction.name == ('MANYTOONE' or 'ONETOONE'):
local_column = mapper.get_property_by_column(item.local_side[0]).key
props.one.add((local_column, item.key))
else:
props.many.add(item.key)
return props
Base= declarative_base(cls=Lens)
meetings_topics = Table("meetings_topics", Base.metadata,
Column('topic_id', Integer, ForeignKey('topic.id')),
Column('meetings_id', Integer, ForeignKey('meetings.id')),
)
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")
class Category(Base):
__tablename__ = 'categories'
name = Column(String(50), primary_key=True)
class Topic(Base):
__tablename__ = 'topic'
id = Column(Integer, primary_key=True)
print Meeting.pk_columns
print Meeting.relations.one
# assignment is OK, since prop is memoized
Meeting.relations.one.add("FOO")
print Meeting.relations.one