У меня есть 2 таблицы в SQLAlchemy (с использованием declerative_base()
), которые объединены через отдельную таблицу
# no comment on the wired naming....
site_word = Table('word_site', Base.metadata,
Column('site_id', Integer, ForeignKey('sites.site_id'), nullable = False, primary_key = True),
Column('word_id', Integer, ForeignKey('site_words.word_id'), nullable = False, primary_key = True))
Отображение слов
class Word(Base):
# snip
word = Column('word', Text, nullable = False, unique = True)
sites = relationship('Site', secondary = site_word, back_populates = 'words')
Отображение сайтов
class Site(Base):
# snip
words = relationship('Word', secondary = site_word, back_populates = 'sites')
Вставка таких работ, как и ожидалось, вставляет уже существующие слова, конечно, из-за unique
в Word.word
.
Я попытался использовать событие before_insert, чтобы проверить, существует ли элемент уже.
event.listen(Word, 'before_insert', some_event)
Я могу получить информацию, чтобы определить слово уникально, как ожидалось, но я не знаю, как добавить новое значение в таблицу join (site_word
).
Я мог бы написать триггер или процедуру для базы данных, но не хочу перемещать слишком много логики в базу данных (не знаю, возможно ли это, так как в то время Site
не будет известен). Я мог бы удалить ограничение для word
-column, но я все еще не могу понять, как получить доступ к информации (на другом конце соединения), чтобы создать запись в таблице соединений, но не в таблице Word
.
Я ищу способ, чтобы создать запись в site_word
и Site
только таблице.
обновление 1:
Я мог бы связать событие с Site
но я не вижу возможности получить информацию о сайте, который будет вставлен. Есть ли возможность сохранить сайт и впоследствии создать связь?
Мне удалось это сделать. Ответ Марка Геммилла заставил меня думать в правильном направлении.
Мне нужно прослушивать атрибут AttributeEvent вместо MapperEvent.
event.listen(Site.words, 'append', test_event, retval = True)
Тогда я могу сделать что-то вроде:
def test_event(target, value, initiator):
try:
return session.query(Word).filter(Word.word == value.word.lower()).one()
except sqlalchemy.orm.exc.NoResultFound:
return value
Это либо возвращает существующее слово из таблицы, либо просто возвращает сопоставленный объект, который необходимо сохранить.
Трудно увидеть проблему без примера того, как вы работаете с вашими объектами. Я так понимаю, вы пытаетесь сделать что-то вроде этого:
site = Site()
site.words.append(Word('Someword'))
session.add(site)
session.commit()
Это, конечно же, вызовет IntegrityError
если "somord" уже существует. Чтобы обойти это, вам нужно сначала запросить базу данных для слова, прежде чем добавлять его на сайт:
def get_unique_word(session, word):
try:
return session.query(Word).filter(Word.word==word).one()
except sqlalchemy.orm.exc.NoResultFound:
return Word(word)
site = Site()
site.words.append(get_unique_word(session, 'someword'))
session.add(site)
session.commit()
Если это полностью не работает, вам нужно будет описать свою проблему более подробно.