#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.