如何使用SQLAlchemies association_proxy跨多个对象传递特定属性?

时间:2012-01-20 14:07:32

标签: python proxy sqlalchemy

我有两种类型的对象:

  • 潜水员ValueObjects继承自BaseValueObject
  • Addendum - 带注释的对象。

Addendum具有特定注释(属性:namedescriptionflag)和一般注释((badgetype, badgevalue)),其中badgevalue是存储在Badge - 对象和badgetype in an AddendumBadgeMap`对象中。

现在,我想访问

  • namedescriptionflag作为ValueOjects
  • 的属性
  • Badges直接来自ValueOjects作为字典。

以下是一个示例实现:

import sqlalchemy as sa
import sqlalchemy.orm as orm
from sqlalchemy.orm import scoped_session, sessionmaker
from sqlalchemy.ext.declarative import declarative_base

DBSession = scoped_session(sessionmaker())
Base = declarative_base ()

###### ValueObjects: ##########
class BaseValueObject(Base):
  __tablename__ = 'value_objects'
  id = sa.Column(sa.Integer, primary_key=True)
  vo_type = sa.Column (sa.String, nullable=False)
  __mapper_args__ = {'polymorphic_on': vo_type}

class ObjectOne(BaseValueObject):
  __tablename__ = 'objects_one'
  __mapper_args__ = {'polymorphic_identity': 'ObjectOne'}
  id = sa.Column(sa.ForeignKey('value_objects.id'), primary_key=True)
  any_attribute = sa.Column(sa.String)


class ObjectTwo(BaseValueObject):
  __tablename__ = 'objects_two'
  __mapper_args__ = {'polymorphic_identity': 'ObjectTwo'}
  id = sa.Column(sa.ForeignKey('value_objects.id'), primary_key=True)
  any_attribute = sa.Column(sa.String)
###############################


##### Addendum and Bagde ##########
class Addendum(Base):
  __tablename__ = 'addenda'
  __table_args__ = (
      sa.UniqueConstraint('name', 'id'),
      {}
      )
  id = sa.Column (sa.Integer, primary_key=True)
  name = sa.Column (sa.String, nullable=False, default='')
  description = sa.Column (sa.String, nullable=False, default='')
  flag = sa.Column ( sa.Boolean, nullable=False, default=False)
  value_object_id = sa.Column ( sa.ForeignKey ('value_objects.id'),
      unique=True)


class Badge(Base):
  __tablename__ = 'badges'
  id = sa.Column (sa.Integer, primary_key=True)
  value = sa.Column (sa.String)


class AddendumBadgeMap(Base):
  __tablename__ = 'addendum_badge_maps'
  __table_args__ = (
      sa.PrimaryKeyConstraint('id'),
      sa.UniqueConstraint('badge_id', 'badge_type'),
      {}
      )
  id = sa.Column('id', sa.Integer)
  addendum_id = sa.Column(sa.ForeignKey('addenda.id'), nullable=False)
  badge_id =  sa.Column(sa.ForeignKey('badges.id'), nullable=False)
  badge_type = sa.Column (sa.String)
###################################

if __name__ == '__main__':
  engine = sa.create_engine('sqlite:///:memory:', echo=True)
  DBSession.configure(bind=engine)
  Base.metadata.create_all(engine)
  session = DBSession()

  o1 = ObjectOne(any_attribute="test1")
  session.add(o1)
  session.commit()

我必须补充一点,我可以执行以下操作:

o1.name = "my_name"
# => `Addendum` will be attached with `name="my_name"`
o1.description = "my_description"
# => `descrpiption` of the attached `Addendum` get's changed
o1.flag
# => same behavior (in general: there should be max. one `Addendum` per `ValueObject`)
o1.bagdes = {"file_origin": "/home/.."}
# => attach to the `Addendum` a `Badge` with `value="/home/.."` and
#    `AddendumBagdeMap.badge_type="file_origin"` (if `Addendum` does
#     not exist for this `ValueObject`: create it first)

1 个答案:

答案 0 :(得分:0)

一个解决方案需要实现两部分:

  1. Addendum
  2. 上实施composite-association-proxies
  3. 从({1}}
  4. 访问(1)Addendum.badges中的新属性

    第1部分:复合关联代理的实施

    将以下属性添加到BaseValueObject

    Addendum

    以及以下badges = association_proxy ( 'addendum_badge_maps', 'badge_value', creator=lambda bt, bv: AddendumBadgeMap(badge_type=bt, badge_value=bv) )

    AddendumBadgeMap

    现在,向addendum = orm.relationship(Addendum, backref=orm.backref( 'addendum_badge_maps', collection_class = attribute_mapped_collection("badge_type"), cascade="all, delete-orphan") ) badge = orm.relationship('Badge', single_parent=True, ) badge_value = association_proxy ('badge', 'value') 添加__init__ - 方法:

    Badge

    并在顶部添加以下导入:

    def __init__(self, value):
      self.value = value
    

    第2部分:从from sqlalchemy.ext.associationproxy import association_proxy from sqlalchemy.orm.collections import attribute_mapped_collection 访问badge - 代理

    通过向BaseValueObjects添加属性,将Addendum - 对象链接到BaseValueObjects

    Addendum

    要访问base_value_object = orm.relationship( "BaseValueObject", backref=orm.backref('addendum', uselist=False) ) ,请将以下内容添加到badegs

    BaseValueObjects

    完整代码

    def add_badge(self, badge_dict={}, **kwargs):
      if self.addendum is None:
        self.addendum = Addendum()
      badge_dict.update(kwargs)
      self.addendum.badges = badge_dict
    
    _name = association_proxy('addendum', 'name',
        creator=lambda name: Addendum(
          name=name, description='', flag=False))
    _description = association_proxy('addendum', 'description',
        creator=lambda description: Addendum(
          name='', description=description, flag=False))
    _flag = association_proxy('addendum', 'flag',
        creator=lambda flag: Addendum(
          name='', description='', flag=flag))
    
    @property
    def badges(self):
      try:
        return self.addendum.badges
      except AttributeError:
        "In case that there is no ``Addendum`` specified, yet."
        return None
    
    @badges.setter
    def badges(self, value):
      self.add_badge(value)
    
    @property
    def name(self):
      try:
        return self._name
      except AttributeError:
        "In case that there is no ``Addendum`` specified, yet."
        return ""
    
    @name.setter
    def name(self, value):
      self._name = value
    
    @property
    def description(self):
      try:
        return self._description
      except AttributeError:
        "In case that there is no ``Addendum`` specified, yet."
        return ""
    
    @description.setter
    def description(self, value):
      self._description = value
    
    @property
    def flag(self):
      try:
        return self._flag
      except AttributeError:
        "In case that there is no ``Addendum`` specified, yet."
        return ""
    
    @flag.setter
    def flag(self, value):
      self._flag = value