Ассоциация прокси SQLAlchemy - PullRequest
7 голосов
/ 13 марта 2012

Этот источник подробно описывает, как использовать прокси-серверы ассоциации для создания представлений и объектов со значениями объекта ORM.

Однако, когда я добавляю значение, соответствующее существующему объекту в базе данных(и указанное значение является либо уникальным, либо первичным ключом), оно создает конфликтующий объект, поэтому я не могу зафиксировать.

Так что в моем случае это полезно только в качестве представления, и мне нужно будет использовать запросы ORMчтобы получить объект, который нужно добавить.

Это мой единственный вариант, или я могу использовать слияние (я могу сделать это, только если это первичный ключ, а не уникальное ограничение), ИЛИ установить конструктортак, что он будет использовать существующий объект в базе данных, если он существует вместо создания нового объекта?

Например, из документов:

user.keywords.append('cheese inspector')

# Is translated by the association proxy into the operation:

user.kw.append(Keyword('cheese inspector'))

Но я бы хотел бытьв переводе на что-то вроде: (конечно, запрос может завершиться неудачей).

keyword = session.query(Keyword).filter(Keyword.keyword == 'cheese inspector').one()
user.kw.append(keyword)

ИЛИ в идеале

user.kw.append(Keyword('cheese inspector'))
session.merge() # retrieves identical object from the database, or keeps new one
session.commit() # success!

Полагаю, это может даже не быть хорошей идеей, но это может бытьв определенных случаях использования :)

Ответы [ 2 ]

9 голосов
/ 13 марта 2012

Пример, показанный на странице документации, на которую вы ссылаетесь, является типом отношения composition (в терминах ООП) и, как таковой, представляет тип отношения owns, а не uses в терминах глаголов. Поэтому каждый owner будет иметь свою собственную копию одного и того же (в стоимостном выражении) ключевого слова.

Фактически, вы можете использовать именно то предложение из документации, на которую вы ссылаетесь в своем вопросе, чтобы создать собственный метод creator и взломать его для повторного использования существующего объекта для данного ключа вместо простого создания нового. В этом случае пример кода класса User и функции creator будет выглядеть следующим образом:

def _keyword_find_or_create(kw):
    keyword = Keyword.query.filter_by(keyword=kw).first()
    if not(keyword):
        keyword = Keyword(keyword=kw)
        # if aufoflush=False used in the session, then uncomment below
        #session.add(keyword)
        #session.flush()
    return keyword

class User(Base):
    __tablename__ = 'user'
    id = Column(Integer, primary_key=True)
    name = Column(String(64))
    kw = relationship("Keyword", secondary=lambda: userkeywords_table)
    keywords = association_proxy('kw', 'keyword', 
            creator=_keyword_find_or_create, # @note: this is the 
            )
3 голосов
/ 16 января 2014

Я недавно столкнулся с той же проблемой.Майк Байер, создатель SQLAlchemy, сослался на рецепт «уникальный объект» , но также показал мне вариант, использующий прослушиватель событий.Последний подход изменяет прокси-сервер ассоциации так, что UserKeyword.keyword временно указывает на обычную строку и создает новый объект Keyword, только если ключевое слово еще не существует.

from sqlalchemy import event

# Same User and Keyword classes from documentation

class UserKeyword(Base):
    __tablename__ = 'user_keywords'

    # Columns
    user_id = Column(Integer, ForeignKey(User.id), primary_key=True)
    keyword_id = Column(Integer, ForeignKey(Keyword.id), primary_key=True)
    special_key = Column(String(50))

    # Bidirectional attribute/collection of 'user'/'user_keywords'
    user = relationship(
        User,
        backref=backref(
            'user_keywords',
            cascade='all, delete-orphan'
            )
        )

    # Reference to the 'Keyword' object
    keyword = relationship(Keyword)

    def __init__(self, keyword=None, user=None, special_key=None):
        self._keyword_keyword = keyword_keyword  # temporary, will turn into a
                                                 # Keyword when we attach to a 
                                                 # Session
        self.special_key = special_key

    @property
    def keyword_keyword(self):
        if self.keyword is not None:
            return self.keyword.keyword
        else:
            return self._keyword_keyword

    @event.listens_for(Session, "after_attach")
    def after_attach(session, instance):
        # when UserKeyword objects are attached to a Session, figure out what 
        # Keyword in the database it should point to, or create a new one
        if isinstance(instance, UserKeyword):
            with session.no_autoflush:
                keyword = session.query(Keyword).\
                    filter_by(keyword=instance._keyword_keyword).\
                    first()
                if keyword is None:
                    keyword = Keyword(keyword=instance._keyword_keyword)
                instance.keyword = keyword
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...