假设有这样的场景, 某实体在具体条目上, 其属性是不定的, 或者其属性是充分稀疏的:
id | name | attr_0 | attr_1 | attr_2 | … | attr_n |
---|
1 | foo | 1 | abc | 33 | … | any |
这种情况下, 把属性看成是单独的实体, 是一个更好的建模方式:
id | entity_id | attr_name | attr_value |
---|
1 | 1 | attr_0 | 1 |
2 | 1 | attr_1 | 33 |
… | … | … | … |
n | 1 | attr_n | any |
这种模型下, ORM 层面我们考虑封装一个对操作更友好的上层操作接口, 比如:
1 2 3 4 5
| obj = Entity() obj['attr_0'] = '1' obj['attr_1'] = '33' session.add(obj) session.commit()
|
实现上, 就是把对象的方法, 包装成 SQLAlchemy
的 ORM
中的对应的关系操作.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
| class BaseModel(declarative_base()): __abstract__ = True
class Entity(BaseModel): __tablename__ = 'entity'
id = Column(Integer, autoincrement=True, primary_key=True) name = Column(Unicode(32), server_default='')
_attributes = relationship('Attribute', collection_class=attribute_mapped_collection('key'), lazy='joined', cascade="all, delete-orphan") attributes = association_proxy('_attributes', 'value', creator=lambda k, v: Attribute(key=k, value=v))
class Attribute(BaseModel): __tablename__ = 'attribute'
id = Column(Integer, autoincrement=True, primary_key=True) entity = Column(Integer, ForeignKey('entity.id'), index=True) key = Column(Unicode(32), server_default='') value = Column(UnicodeText, server_default='')
if __name__ == '__main__': BaseModel.metadata.create_all(Engine)
session = Session()
entity = Entity(name=u'哈哈') entity.attributes[u'first'] = u'abc' entity.attributes[u'sec'] = u'hoho' session.add(entity) session.commit()
entity = session.query(Entity).first() print entity.attributes del entity.attributes['first'] session.commit()
entity = session.query(Entity).first() print entity.attributes
|
实现上就两点:
_attributes
关系中, 指定 collection_class
, 于是就可以得到一个像 dict
的属性对象了.association_proxy
从 dict
的属性对象中只抽出我们关心的 value
属性值.
这个场景中, 还可以再进一步, 在 Entity
类上实现 dict
的一些方法, 直接操作其 attributes
属性, association_proxy
就直接返回 Entity
的实例, 这样代码可以变成这样:
1 2 3
| entity = Entity(name=u'ABC') entity[u'first'] = u'a' entity[u'sec'] = u'hoho'
|