Использование кортежей значений в сопоставленной коллекции sqlalchemy - PullRequest
3 голосов
/ 24 мая 2011

В отношении «многие ко многим» у меня есть несколько дополнительных данных в таблице ассоциаций, чтобы описать отношение (количество и логическое значение).Я хотел бы использовать сопоставленную коллекцию, чтобы избежать непосредственной работы с объектами ассоциации, но я не могу понять, как использовать кортеж для значений в отображении.Насколько я могу судить, Атрибут как диктат списков с использованием таблицы посредников с 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}

I может поместите имя элемента и одно значение в сопоставленную коллекцию без проблем: у меня была другая (структурно идентичная) форма этих отношений, которую я решил, создав два отношения (и прокси-серверы ассоциации), которые разделяли таблицу ассоциаций между ними на основе логического значения,и их функции-создатели правильно устанавливают логическое значение без каких-либо дополнительных помех.К сожалению, в этом случае логическое значение указало незначительное семантическое различие (код приложения должен рассматривать элементы как группу), тогда как в текущей проблеме это не является незначительным косметическим различием (код приложения не должен обрабатывать элементы как группы, нозначение влияет на то, как элемент отображается, и поэтому необходимо).

1 Ответ

8 голосов
/ 24 мая 2011

связанный ответ имеет все компоненты.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, 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)
    name_id = Column(Integer, ForeignKey("name_data.id"), primary_key=True)

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

    name_element = relationship("NameElement")

    def __init__(self, key, 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, True)
collection.items["item name 2"] = (1, False)
print collection.items

s.add(collection)
s.commit()

collection = s.query(SomeClass).first()
print collection.items

это, вероятно, все, что вам нужно.Если вы используете Postgresql, который поддерживает кортежи SQL, вы можете добавить больше к вышеперечисленному, используя гибриды плюс tuple_(), так что as_tuple можно использовать и на уровне SQL (ниже также используется один ко многим вместообъект ассоциации только для примера):

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
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", 
                collection_class=attribute_mapped_collection("name"),
                cascade="all, delete-orphan"
            )
    items = association_proxy("tuple_elements", "as_tuple")

class TupleElement(Base):
    __tablename__ = 'tuple_data'

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

    def __init__(self, key, 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, True)
collection.items["item name 2"] = (1, 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()
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...