SQLAlchemy:如何仅在常量子句上与自定义主连接建立关系

时间:2014-07-08 14:15:39

标签: sqlalchemy

我尝试改进通用关联示例discriminator_on_association.py,以便在不添加任何新列的情况下使用混合模型。

当我尝试使用如下关系时:

relationship(assoc_cls,
             primaryjoin=foreign(remote(assoc_cls.discriminator))==discriminator)

我面临一个错误:

  

sqlalchemy.exc.ArgumentError:找不到任何简单的相等   涉及主要的本地映射外键列的表达式   加入条件'address_association.discriminator =:discriminator_1'   关于Supplier.address_association的关系。确保引用   列与ForeignKey或ForeignKeyConstraint相关联,或   在带有foreign()注释的连接条件中注释。至   允许比较运算符以外的'==',关系可以   标记为viewonly = True。

如何设置关系以获得LEFT JOIN,如下所示?

SELECT *
  FROM table_x 
       LEFT JOIN address_association ON address_association.discriminator='table_x'

是否可以设置关系以获得LEFT OUTER JOIN?

更新: 我尝试使用的属性的最后一个源代码版本。

class BaseModelMixin(object):
    @db.declared_attr
    def fid(cls):
        return db.Column(db.Numeric(10,0), primary_key=True)

class Attr_type(BaseModelMixin, db.Model):
    __tablename__ = 'attributes'

    fsname = db.Column(db.Unicode(128), index=True, nullable=False)
    fitype = db.Column(db.Numeric(5,0), nullable=False, default=200)
    _fstable = db.Column('fstable', db.String(64), index=True, nullable=True)

class Attr_value(BaseModelMixin, db.Model):
    __tablename__ = 'table_attrs'

    firec_id = db.Column(db.Numeric(10,0), index=True, nullable=False)
    fiattr_id = db.Column(db.Numeric(10,0), db.ForeignKey('attributes.fid', ondelete='CASCADE'), index=True, nullable=False)
    attr_type = db.relation("Attr_type", lazy="joined", uselist=False)
    fitype = db.association_proxy("attr_type", "fitype")
    _fstable = db.association_proxy("attr_type", "_fstable")
    value = db.Column(db.Unicode(255))

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

class AttributesMixin(object):
    @property    
    def _table_attr_types(self):
        RowAttrVal = type("Attr_val_%s_%s" % (self.__tablename__, self.fid),
              (Attr_value, ),
              dict(
                   __mapper_args__ = {
                                      "polymorphic_on": "firec_id",
                                      "polymorphic_identity": self.fid
                                      }
                  )
              )

        TableAttrType = type("Attr_type_%s_%s" % (self.__tablename__, self.fid),
                         (Attr_type, ),
                         dict(
                              _values = db.relation(RowAttrVal,
                                                    uselist=False, 
                                                    ),
                              _value = db.association_proxy("_values", "value",
                              __mapper_args__ = {
                                                 "polymorphic_on": "_fstable",
                                                 "polymorphic_identity": self.__tablename__
                                                }
                              )
                         )

        return TableAttrType    

    def __new__(cls):        
        def get_attributes(self):
            return self._table_attr_types.query.all()

        def set_attributes(self, values):
            self._table_attr_types.query.set_by_jsons(values, decode=False)

        cls.attributes = property(get_attributes, set_attributes)

        return super(AttributesMixin, cls).__new__(cls)

更新2 http://pastebin.com/YiX1ycNh - getter和setter属性有效,但表达式不适用

1 个答案:

答案 0 :(得分:2)

不确定这是否是您正在寻找的模式,但目前为止似乎是这样。为了代理常规的@property,有很多方法可以做到这一点,这里我重新调整了_AssociationSet,但这只是任何具有代理行为的Python类似对象。

from sqlalchemy import Integer, ForeignKey, Column, String, create_engine
from sqlalchemy.orm import relationship, Session, exc as orm_exc, \
    object_session
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.ext.associationproxy import _AssociationSet
import operator

Base = declarative_base()


class ThingAssociationSet(_AssociationSet):
    def __init__(self, host):
        self.lazy_collection = lambda: host._attributes.attributes
        self.creator = lambda value: Attribute(value=value)
        self.getter = operator.attrgetter("value")

        def setter(target, value):
            target.value = value
        self.setter = setter
        self.parent = None


class HasThings(object):
    thing_type = None

    @property
    def _attributes(self):
        sess = object_session(self)
        if not sess:
            raise ValueError("attributes require shared session state")
        try:
            attrs = sess.query(AttributeCollection).\
                filter_by(type=self.thing_type).one()
        except orm_exc.NoResultFound:
            attrs = AttributeCollection(type=self.thing_type)
            sess.add(attrs)
        self.__dict__['attributes'] = attrs
        return attrs

    @property
    def attributes(self):
        return ThingAssociationSet(self)


class AttributeCollection(Base):
    __tablename__ = 'attr_collection'

    id = Column(Integer, primary_key=True)
    type = Column(String(30), nullable=False)
    attributes = relationship("Attribute",
                    collection_class=set, cascade="all, delete-orphan")


class Attribute(Base):
    __tablename__ = 'attribute'

    id = Column(Integer, primary_key=True)
    collection_id = Column(ForeignKey('attr_collection.id'))
    value = Column(String(100), nullable=False)


class X(HasThings, Base):
    thing_type = 'x'
    __tablename__ = 'x'
    id = Column(Integer, primary_key=True)


class Y(HasThings, Base):
    thing_type = 'y'
    __tablename__ = 'y'
    id = Column(Integer, primary_key=True)


e = create_engine("sqlite://", echo=True)
Base.metadata.create_all(e)

sess = Session(e)

x1 = X()
x2 = X()
x3 = X()
y1 = Y()
y2 = Y()

sess.add_all([x1, x2, x3, y1, y2])

x2.attributes.add("x attribute 1")
x3.attributes.add("x attribute 2")

y1.attributes.add("y attribute 1")

assert x3.attributes == set(["x attribute 1", "x attribute 2"])

x1.attributes.remove("x attribute 1")

assert x3.attributes == set(["x attribute 2"])