Запустите параметризованный SQL-оператор UPDATE SET в Connection.execute sqlalchemy - PullRequest
1 голос
/ 11 ноября 2019

Я хочу запустить параметризованный оператор mysql UPDATE SET в flask_sqlalchemy. Поскольку я не знаю, какие столбцы нужно обновить, я написал вспомогательную функцию, которая поможет в написании оператора.

Моя модель

class User(db.Model):
    id = db.Column(db.Integer, primary_key=True, autoincrement= True)
    username = db.Column(db.String(80), nullable=False)
    email = db.Column(db.String(120), nullable=False)

    def __repr__(self):
        return '<User %r>' % self.username

Вспомогательная функция

def run_sql(params):
    # e.g: params = {'username': "testing", "email": "testing@testing.ts", "id": 1}
    id = params.pop("id")
    # params = {'username': "testing", "email": "testing@testing.ts"}
    sets = list(map(lambda col: f"{col}=:{col}", params.keys()))
    # sets = ["username=:username", "email=:email"]
    sets = ", ".join(sets)
    # sets = "username=:username, email=:email"
    params["id"] = id
    # params = {'username': "testing", "email": "testing@testing.ts", "id": 1}
    sql_statement = f"""UPDATE User SET {sets} WHERE id=:id LIMIT 1"""
    # sql_statement = UPDATE User SET username=:username, email=:email WHERE id=:id LIMIT 1
    return  sql_statement

Вызов вспомогательной функции

if __name__ == "__main__":
    conn = engine.connect()
    params = {'username': "testing", "email": "testing@testing.ts", "id": 1}
    sql_statement = run_sql(params)
    conn.execute(sql_statement, params)

Запуск предыдущего кода приводит к следующему исключению

"sqlalchemy.exc.ProgrammingError: (pymysql.err.ProgrammingError) (1064, "You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ':username, email=:email WHERE id=:id LIMIT 1' at line 1") [SQL: 'UPDATE User SET username=:username, email=:email WHERE id=:id LIMIT 1'] [parameters: {'username': 'testing', 'email': 'testing@testing.ts', 'id': 1}] (Background on this error at: http://sqlalche.me/e/f405)"

Оператор SQL выглядит хорошо для меня, поэтому параметры. Я что-то упустил?

Ответы [ 2 ]

2 голосов
/ 11 ноября 2019

Параметры связывания не в стиле MySQL, и когда вы передаете простой текст в engine.execute(), SQLAlchemy не применяет диалект к запросу перед его выполнением.

Попробуйте это:

engine.execute("SELECT :val", {"val": 1})  # will fail, same as your query

... и затем это:

engine.execute("SELECT %(val)s", {"val": 1})  # will execute

Заключение запроса в text() позволит SQLAlchemy обрабатывать правильный стиль привязки:

from sqlalchemy import text  # or you can use db.text w/ flask-sqlalchemy
engine.execute(text("SELECT :val"), {"val": 1})

OneСледует также отметить, что SQLAlchemy будет автоматически обрабатывать создание запроса UPDATE с учетом значений в параметре dict, например:

id_ = 1
params = {'username': "testing", "email": "testing@testing.ts"}
User.__table__.update().values(params).where(id=id_)
# UPDATE user SET username=%(username)s, email=%(email)s WHERE user.id = %(id_1)s

params = {'username': "testing"}
User.__table__.update().values(params).where(id=id_)
# UPDATE user SET username=%(username)s WHERE user.id = %(id_1)s

params = {'username': "testing", "unexpected": "value"}
User.__table__.update().values(params).where(id=id_)
# sqlalchemy.exc.CompileError: Unconsumed column names: unexpected
2 голосов
/ 11 ноября 2019

Если вы хотите использовать форму :named_parameter для своего оператора SQL, вам нужно использовать метод text SQLAlchemy, а затем вызвать метод execute объекта Engine:

import sqlalchemy as sa

# ...

engine.execute("CREATE TEMPORARY TABLE temp (id varchar(10) PRIMARY KEY)")
sql = sa.text("INSERT INTO temp (id) VALUES (:id)")
params = {'id': 'foo'}
engine.execute(sql, params)

print(engine.execute("SELECT * FROM temp").fetchall())  # [('foo',)]
...