Обновление SQLAlchemy с предложением in от kwargs

#python #sqlalchemy

Вопрос:

Я пытаюсь написать функцию, которая принимает kwargs в качестве параметра и генерирует оператор обновления, в котором строки, подлежащие обновлению, указаны в предложении in.

Вот что у меня есть:

     def update_by_in(self, **kwargs):
        filter_group = []
        for col in kwargs['query_params']:
            attr = getattr(self.model_class, col)
            filter_group.append(attr.in_(tuple(kwargs['query_params'][col])))
        self._session.query(self.model_class).
            filter(*filter_group).
            update(kwargs['values'])


    self.update_by_in(
        **{'query_params': {'companyCode': ['A', 'B', 'C']},
           'values': {'portfolioName': 'test'}}
     )

sqlalchemy.orm.evaluator.UnevaluatableError: Cannot evaluate clauselist with operator <function comma_op at 0x7f33f037adc0>
 

Чтобы убедиться, что часть запроса работает, я распечатал ответ от

 self._session.query(self.model_class).filter(*filter_group).all()

[<common.models_dec_core.Portfolio object at 0x7f2b98157f40>]
 

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

Что я здесь делаю не так?

Ответ №1:

Эта проблема не возникает с sqlalchemy 1.4.2, но возникает с sqlalchemy 1.3.16.

Я думаю, что это происходит потому synchronize_session , что значение по умолчанию равно "evaluate" и оно не может разрешить in_ условие в 1.3.16.

Ниже условие using or_ работает по умолчанию, но условие using in_ требует synchronize_session установки значения "fetch" .

Пример с or_ и in_

 
from datetime import datetime, date
from sqlalchemy import (
    create_engine,
    Text,
    Integer,
    String,
    ForeignKey,
    UniqueConstraint,
    update,
    DateTime,
   Date,
    Boolean,
    LargeBinary,
)
from sqlalchemy.schema import (
    Table,
    Column,
    MetaData,
)
from sqlalchemy.sql import select, and_, or_, func
from sqlalchemy.orm import relationship
from sqlalchemy.orm import Session
from sqlalchemy.exc import IntegrityError
from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()


engine = create_engine("sqlite://", echo=False)


class User(Base):
    __tablename__ = 'users'
    id = Column(Integer, primary_key=True)
    name = Column(String(100), nullable=False)
    code = Column(String(1), nullable=False)
    portfolio = Column(String(100), nullable=True)


Base.metadata.create_all(engine)
def update_by_or(session, model_class, **kwargs):
    filter_group = []
    for col in kwargs['query_params']:
        attr = getattr(model_class, col)
        filter_group.append(or_(*[attr == v for v in kwargs['query_params'][col]]))
    session.query(model_class).
        filter(filter_group[0]).
    update(kwargs['values'])


def update_by_in(session, model_class, **kwargs):
    filter_group = []
    for col in kwargs['query_params']:
        attr = getattr(model_class, col)
        filter_group.append(attr.in_(kwargs['query_params'][col]))
    session.query(model_class).
        filter(filter_group[0]).
    update(kwargs['values'], synchronize_session='fetch')

def print_users(session):
    for u in session.query(User).all():
        print (u.id, u.name, u.code, u.portfolio)

session = Session(engine)

session.add(User(name='One', code='A'))
session.add(User(name='Two', code='B'))
session.add(User(name='Three', code='C'))
session.add(User(name='Four', code='D'))
session.commit()
print_users(session)
update_by_in(session, User, **{'query_params': {'code': ['A', 'B', 'C']},
   'values': {User.portfolio: 'test1'}})
session.commit()
print_users(session)
update_by_in(session, User, **{'query_params': {'code': ['A', 'B', 'C']},
   'values': {User.portfolio: 'test2'}})
session.commit()
print_users(session)
 

Выход

 1 One A None
2 Two B None
3 Three C None
4 Four D None
1 One A test1
2 Two B test1
3 Three C test1
4 Four D None
1 One A test2
2 Two B test2
3 Three C test2
4 Four D None
 

Хотя исключение, которое я получаю, это:

 sqlalchemy.exc.InvalidRequestError: Could not evaluate current criteria in Python: "Cannot evaluate clauselist with operator <function comma_op at 0x7f7c082821e0>". Specify 'fetch' or False for the synchronize_session parameter.
 

Ответ №2:

Я заставил это работать, добавив synchronize_session=’fetch’ в обновление. Действительно хотел бы знать, зачем это было нужно — у меня есть аналогичный код, но он не использует переменные (модель и столбцы жестко закодированы) и работает без сеанса synchronize_session.