在sqlalchemy映射的集合中使用值的元组

问题描述

| 在多对多关系中,我在关联表上有一些额外的数据来描述这种关系(数量和布尔值)。我想使用映射的集合来避免直接使用关联对象,但是我不知道如何在映射中使用元组作为值。据我所知,将中间人表与SQLAlchemy结合使用时,列表的字典属性是相似的,但倒是相反。 为了说明这一点,我想做这样的事情:
>>> collection.items[item] = (3,True)
>>> collection.items[item] = (1,False)
>>> colletion.items
{\"item name\": (3,True),\"item name\": (1,False)}
这...有效...但是最终SQLAlchemy尝试将元组放入数据库中(我将尝试稍作重新创建)。 我也尝试过在键(相关对象和另一列之一)中使用元组,但是它看起来很糟糕,并且不起作用:
>>> collection.items[item,True]  = 3
>>> collection.items[item,False] = 1
>>> collection.items
{(<item>,True): 3,(<item>,False): 1}
我可以将项目名称和一个值放到映射的集合中而不会出现问题:我有这种关系的另一种(结构相同)形式,我通过创建两个关系(和关联代理)来解决,该两个关联(和关联代理)根据布尔值在它们之间划分了关联表值,并且其创建函数正确设置了布尔值,而没有任何其他干扰。不幸的是,在这种情况下,布尔值指定了较小的语义差异(应用程序代码需要将项目视为一个组),而在当前问题中,这并不是一个无关紧要的外观差异(应用程序代码不应将项目视为组,而是值确实会更改项目的显示方式,因此是必需的)。     

解决方法

链接的答案具有所有要素。 attribute_mapped_collection和association_proxy可以一起做很多事情。首先是string-> tuple(int,boolean)的字典(已更新为m2m):
from sqlalchemy import Integer,Boolean,String,Column,create_engine,\\
    ForeignKey
from sqlalchemy.orm import Session,relationship
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.ext.associationproxy import association_proxy
from sqlalchemy.orm.collections import attribute_mapped_collection

Base = declarative_base()

class SomeClass(Base):
    __tablename__ = \'sometable\'

    id = Column(Integer,primary_key=True)
    tuple_elements = relationship(
                \"TupleAssociation\",collection_class=attribute_mapped_collection(\"name\"),cascade=\"all,delete-orphan\"
            )
    items = association_proxy(\"tuple_elements\",\"as_tuple\")

class TupleAssociation(Base):
    __tablename__ = \'tuple_association\'
    parent_id = Column(Integer,ForeignKey(\'sometable.id\'),primary_key=True)
    tuple_id = Column(Integer,ForeignKey(\"tuple_data.id\"),primary_key=True)
    name = Column(String)

    tuple_element = relationship(\"TupleElement\")

    def __init__(self,key,tup):
        self.name = key
        self.tuple_element = TupleElement(tup)

    @property
    def as_tuple(self):
        return self.tuple_element.as_tuple

class TupleElement(Base):
    __tablename__ = \'tuple_data\'

    id = Column(Integer,primary_key=True)
    col1 = Column(Integer)
    col2 = Column(Boolean)

    def __init__(self,tup):
        self.col1,self.col2 = tup

    @property
    def as_tuple(self):
        return self.col1,self.col2


e = create_engine(\'sqlite://\')
Base.metadata.create_all(e)
s = Session(e)

collection = SomeClass()
collection.items[\"item name 1\"] = (3,True)
collection.items[\"item name 2\"] = (1,False)
print collection.items

s.add(collection)
s.commit()

collection = s.query(SomeClass).first()
print collection.items
这是关联中元组和端点上名称的另一种选择:
from sqlalchemy import Integer,primary_key=True)
    name_id = Column(Integer,ForeignKey(\"name_data.id\"),primary_key=True)

    col1 = Column(Integer)
    col2 = Column(Boolean)

    name_element = relationship(\"NameElement\")

    def __init__(self,tup):
        self.name_element = NameElement(name=key)
        self.col1,self.col2 = tup

    @property
    def name(self):
        return self.name_element.name

    @property
    def as_tuple(self):
        return self.col1,self.col2

class NameElement(Base):
    __tablename__ = \'name_data\'

    id = Column(Integer,primary_key=True)
    name = Column(String)


e = create_engine(\'sqlite://\',echo=True)
Base.metadata.create_all(e)
s = Session(e)

collection = SomeClass()
collection.items[\"item name 1\"] = (3,False)
print collection.items

s.add(collection)
s.commit()

collection = s.query(SomeClass).first()
print collection.items
那可能就是您所需要的。如果您使用的是支持SQL元组的Postgresql,则可以使用Hybrid加
tuple_()
在上面添加更多内容,以便
as_tuple
也可以在SQL级别使用(下面也使用一对多而不是关联对象例如):
from sqlalchemy import Integer,relationship
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.ext.associationproxy import association_proxy
from sqlalchemy.orm.collections import attribute_mapped_collection
from sqlalchemy.ext import hybrid
from sqlalchemy.sql import tuple_

Base = declarative_base()

class SomeClass(Base):
    __tablename__ = \'sometable\'

    id = Column(Integer,primary_key=True)
    tuple_elements = relationship(
                \"TupleElement\",\"as_tuple\")

class TupleElement(Base):
    __tablename__ = \'tuple_data\'

    id = Column(Integer,primary_key=True)
    parent_id = Column(Integer,nullable=False)
    name = Column(String)
    col1 = Column(Integer)
    col2 = Column(Boolean)

    def __init__(self,tup):
        self.name = key
        self.col1,self.col2 = tup

    @hybrid.hybrid_property
    def as_tuple(self):
        return self.col1,self.col2

    @as_tuple.expression
    def as_tuple(self):
        return tuple_(self.col1,self.col2)

e = create_engine(\'postgresql://scott:tiger@localhost/test\',echo=True)
Base.metadata.drop_all(e)
Base.metadata.create_all(e)
s = Session(e)

collection = SomeClass()
collection.items[\"item name 1\"] = (3,False)
print collection.items

s.add(collection)
s.commit()

q = s.query(SomeClass).join(SomeClass.tuple_elements)
assert q.filter(TupleElement.as_tuple == (3,True)).first() is collection
assert q.filter(TupleElement.as_tuple == (5,False)).first() is None
print s.query(TupleElement.as_tuple).all()
    

相关问答

依赖报错 idea导入项目后依赖报错,解决方案:https://blog....
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下...
错误1:gradle项目控制台输出为乱码 # 解决方案:https://bl...
错误还原:在查询的过程中,传入的workType为0时,该条件不起...
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct...