我尝试改进通用关联示例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属性有效,但表达式不适用
答案 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"])