LINUX.ORG.RU

sqlalchemy проверить на дубликат перед вставкой

 ,


0

1

В sqlalchemy существует метод для проверки на дубликат перед вставкой записи?

Есть модель-таблица

class MyTable(Base):

    __tablename__ = 'MyTable'

    id = Column(Integer, primary_key=True)
    name = Column(String, nullable=False)
    date = Column(Date, nullable=False)
    count = Column(Integer, nullable=False)

    __table_args__ = (UniqueConstraint('name', 'date', name='uniq_idx_1'), )

Метод вставки

def insert(myTable: MyTable) -> None:
    with sessionScope() as session:
        session.add(myTable)
будет бросать исключение, если идет дубликат по комбинации уникальных полей name и date. Как бы предварительно перед session.add прочекать строчку для вставки на отсуствие дубликата? обязательно ли все записи выбирать?

Но зачем? По исключению и поймёшь это.

gruy ★★★★★ ()
Ответ на: комментарий от eternal_sorrow

фигня какая то - не ловиться исключение, ни большое, ни маленькое:

from sqlalchemy.exc import IntegrityError

def insert(myTable: MyTable) -> None:
    with sessionScope() as session:
        try:
            session.add(myTable)
        except IntegrityError as e:
            print('pass duplicate')
        except Exception as e:
            print(e)
            raise

# ...
    myTable = MyTable(name='x', date='2021-04-30', count=22)
    insert(myTable)
IntegrityError: (psycopg2.IntegrityError) duplicate key value violates unique constraint "uniq_idx_1"
DETAIL:  Key (name, date)=(x, 2021-04-30) already exists.
scientistpython ()
Последнее исправление: scientistpython (всего исправлений: 1)
Ответ на: комментарий от scientistpython

Это не цивилизованный способ, а лишний запрос, к тому же бесполезный без блокировки. Обработка ошибки — самый оптимальный и единственный корректный способ.

WitcherGeralt ★★ ()
Ответ на: комментарий от eternal_sorrow

на практике, в контекстном менеджере вот так перехватывает

from sqlalchemy.exc import IntegrityError

def sessionScope():
    try:
        session = Session(bind=engine)
        yield session
        session.commit()
    except IntegrityError:
        print('pass duplicate')
        session.rollback()
    except:
        session.rollback()
        raise

def insert(myTable: MyTable) -> None:
    with sessionScope() as session:
        session.add(myTable)
scientistpython ()
Последнее исправление: scientistpython (всего исправлений: 1)
Ответ на: комментарий от scientistpython

except:

никогда так не делай. в крайнем случае except Exception:. в твоём же случае session.rollback() лучше делать в finally:. а ещё в алхимии должен быть контекстный менеджер (engine.begin() или лучше connection.begin()), который обрабатывает транзакцию, тебе не нужно его реализовывать

eternal_sorrow ★★★★★ ()
Последнее исправление: eternal_sorrow (всего исправлений: 1)
Ответ на: комментарий от scientistpython

в случае exception я должен откатиться, а в finaly я сделал session.close()

да, верно. тут моя ошибка

eternal_sorrow ★★★★★ ()
Ответ на: комментарий от eternal_sorrow

Я сутал с поведением при работе с asyncpg, там отдельная UniqueViolationError из драйвера не перехватывается.

Алхимия далекро не такая консистентная как принято считать. Зачастую приходится аргументы непосредственно в инстанс драйвера пробрасывать, ошибки ловить и руками какие-то бд-специфичные фичи костылять.

WitcherGeralt ★★ ()
Ответ на: комментарий от OxiD

Если запись с уникальным вторичным ключем уже существует, то обновлять не нужно. И по сути, я выбираю между: 1) сделать запрос и проверить наличие записи (select только приходит на ум) и потом вставить 2) попробовать сразу вставить и, если запись есть - обработать исключение

scientistpython ()
Ответ на: комментарий от scientistpython

Или другими словами:

1) не допускать вставки

def insert(myTable: MyTable) -> None:
    with sessionScope() as session:
        row = session.query(MyTable) \
                     .filter(MyTable.name==myTable.name) \
                     .filter(MyTable.date==myTable.date) \
                     .all()
        if len(row) == 0:
            session.add(myTable)

2) по Питонски в контекстный манагер добавить пропуск исключения

@contextmanager
def sessionScope():
    try:
        session = Session(bind=engine)
        yield session
        session.commit()
    except IntegrityError as e:
        print('pass')
        session.rollback()
    # ...

def insert(myTable: MyTable) -> None:
    with sessionScope() as session:
        session.add(myTable)
scientistpython ()
Ответ на: комментарий от scientistpython

IntegrityError кажется может возникать и по другим причинам ( ну или добавите новый уник индекс, надо будет код как то править)

А для селекта как раз и надо делать for update иначе может быть гонка

OxiD ★★★★ ()
Для того чтобы оставить комментарий войдите или зарегистрируйтесь.