Вставка связей «многие ко многим» через таблицу в SQL Alchemy

1

Предполагая следующие модели SQL Alchemy:

likes = db.Table('likes',
    db.Column('from_id', db.Integer, db.ForeignKey('user.id'), primary_key=True),
    db.Column('to_id', db.Integer, db.ForeignKey('user.id'), primary_key=True)
)

class User(db.Model):
    id = db.Column(db.Integer, primary_key=True, autoincrement=True)
    liked_by = db.relationship('User', secondary=likes, foreign_keys=likes.c.to_id, lazy='subquery')
    likes = db.relationship('User', secondary=likes, foreign_keys=likes.c.from_id, lazy='subquery')

Что самое быстрый и простой способ добавить много, like отношений между многими User объектами таким образом, что не потерпит неудачу, если дубли попытки?

Мое самое близкое решение:

x = [
    {'from_id': 1, 'to_id': 2},
    {'from_id': 2, 'to_id': 3},
    {'from_id': 1, 'to_id': 3},
    ...
]
stmt = likes.insert().values(x)
session.execute(stmt)
session.commit()

Но вся эта транзакция завершится неудачей, если, например, 1 → 2 уже существует. Есть ли способ выполнить этот оператор таким образом, который добавит новые строки в таблицу likes и не будет терпеть неудачу в существующих строках?

Решение должно быть выполнено (т.е. Поддерживать массовую вставку). База данных - PostgreSQL 10.x, если требуется не общий ответ.

  • 1
    Между прочим, я думаю, что ваша самореферентная конфигурация «многие ко многим» немного не подходит : docs.sqlalchemy.org/en/latest/orm/… . Не уверен, но, возможно, вместо определения используемых внешних ключей нужны первичные и вторичные соединения.
  • 0
    @IljaEverilä IljaEverilä Вы определенно правы, использование первичных и вторичных объединений действительно дает правильное поведение! Ура :)
Теги:
python-3.x
sqlalchemy

1 ответ

1
Лучший ответ

Вы можете использовать функции "upsert", добавленные в Postgresql несколько недавно. Если вы просто хотите игнорировать дубликаты, используйте ON CONFLICT DO NOTHING:

from sqlalchemy.dialects.postgresql import insert

stmt = insert(likes).values(x).on_conflict_do_nothing()
...
  • 0
    Круто, это выглядит именно то, что мне нужно, спасибо!

Ещё вопросы

Сообщество Overcoder
Наверх
Меню