2012-04-08 75 views
1

我“指数” SQLAlchemy的模型属性所以说我有一些类X,Y和Z使用的SQLAlchemy声明的语法来定义一些简单的列和关系怎么可能是主键和关系

要求:

  1. 在类级别,(X|Y|Z).primary_keys返回
    各个类的主键(InstrumentedAttribute 对象)的集合我也想(X|Y|Z).relations引用类的在同一 关系方式

  2. 在实例级,我想同样的属性引用 这些属性实例化的价值观,他们是否已经 用自己的构造稀少,个别属性
    制定者,或什么的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_class_lens并且演变为Nonetype。

我不太清楚我应该如何在实例级别进行操作。 我查看了事件界面。我看到ORM事件init,但它似乎在我的模型上定义的__init__函数之前运行,这意味着实例属性当时尚未填充,所以我无法在其上创建“镜头”。 我也想知道属性事件set可能有帮助。这是我的下一次尝试,但我仍然怀疑它是否是最合适的方式。

总而言之,我真的不知道我是否错过了一些非常优雅的方法来解决这个问题。

回答

3

我认为具有陈述性的元类事物由旧的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