Как я могу передать определенные атрибуты через несколько объектов с помощью SQLAlchemies association_proxy? - PullRequest
1 голос
/ 20 января 2012

У меня есть два типа объектов:

  • divers ValueObjects, которые наследуются от BaseValueObject, и
  • Addendum -объектов, которые содержат аннотации.

Addendum имеет специальные аннотации (атрибуты: name, description, flag) и общие аннотации ((badgetype, badgevalue)), где badgevalue хранится в Badge -объекте иbadgetype in an AddendumBadgeMap`-объект.

Теперь я хотел бы получить доступ к

  • name, description и flag в качестве атрибутов 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 голосов
/ 25 января 2012

Для одного решения потребуются две части реализации:

  1. Реализация составных-прокси-серверов-прокси в Addendum
  2. Сделать новое свойство из (1) Addendum.badges доступным из BaseValueObject

Часть 1: Реализация составной-прокси-ассоциации

Добавьте следующие свойства к 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

и добавьте сверху следующий импорт:

from sqlalchemy.ext.associationproxy import association_proxy
from sqlalchemy.orm.collections import attribute_mapped_collection

Часть 2: Доступ к 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

Полный код

import sqlalchemy as sa
import sqlalchemy.orm as orm
from sqlalchemy.orm import scoped_session, sessionmaker
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.ext.associationproxy import association_proxy
from sqlalchemy.orm.collections import attribute_mapped_collection

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}

  # Define Badge-Access:
  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

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)

  # Properties:
  base_value_object = orm.relationship(
      "BaseValueObject",
      backref=orm.backref('addendum', uselist=False)
      )

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

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

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


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)

  #Properties:
  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')
###################################

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")
  o1.name = "First Object"
  session.add(o1)
  session.commit()

  o1.badges = {'first name': 'Max'}
  o1.badges['last name'] = 'Mueller'
  session.commit()
  print o1.name
  print o1.description
  print o1.flag
  print o1.any_attribute
  print o1.badges
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...