简体   繁体   English

防止 MySQL 在 Flask-SQLAlchemy 中截断值

[英]Prevent MySQL to truncate values in Flask-SQLAlchemy

I have a Flask app that connects to a database using Flask-SQLAlchemy.我有一个使用 Flask-SQLAlchemy 连接到数据库的 Flask 应用程序。 When i want to insert a value to a table that is too long or invalid(ish), i usually get a warning that the value is truncated, and MySQL happily insert the valid part of the value.当我想向一个太长或无效(ish)的表插入一个值时,我通常会收到一个警告,该值被截断,而 MySQL 很乐意插入该值的有效部分。 This results in various problems later.这会导致以后出现各种问题。

Checking the sql_mode variable during my connection yields the following modes:在我的连接期间检查sql_mode变量会产生以下模式:

  • STRICT_TRANS_TABLES
  • ERROR_FOR_DIVISION_BY_ZERO
  • NO_AUTO_CREATE_USER
  • NO_ENGINE_SUBSTITUTION

To achieve my goal, i need to add STRICT_ALL_TABLES to this mix, which i can easily do with this (pretty dump) snippet ( db is the SQLAlchemy instance in my app):为了实现我的目标,我需要将STRICT_ALL_TABLES添加到这个组合中,我可以用这个(漂亮的转储)片段轻松做到这一点( db是我的应用程序中的SQLAlchemy实例):

res = list(db.engine.execute('SHOW VARIABLES LIKE \'sql_mode\''))
modes = res[0][1].split(',')
db.engine.execute(f'SET sql_mode = \'{",".join(modes)}\'')

# I tried and failed using the following for some reason
db.engine.execute('SET sql_mode = ?', '.'.join(modes)

After this, when i update a model with an unfitting value, i get exactly what i want;在此之后,当我用不合适的值更新模型时,我得到了我想要的; an exception instead of a warning:异常而不是警告:

sqlalchemy.exc.DataError: (pymysql.err.DataError) (1265, "Data truncated for column 'value' at row 1")

Question is, how can i tell SQLAlchemy to do this every time when i connect to a MySQL instance?问题是,当我每次连接到 MySQL 实例时,如何告诉 SQLAlchemy 执行此操作?

With some help from @gord-thompson's documentation link in the comments, i devised the following solution:在评论中@gord-thompson 的文档链接的帮助下,我设计了以下解决方案:

from flask_sqlalchemy import SQLAlchemy
from sqlalchemy import event
from sqlalchemy.dialects.mysql.base import MySQLDialect


def mysql_on_connect(connection, conn_record):  # pylint: disable=unused-variable
    """Turn on strict mode for all tables
    """

    cursor = connection.cursor()

    # Get the current SQL Mode
    cursor.execute('SHOW VARIABLES LIKE \'sql_mode\'')
    res = list(cursor)
    modes = set(res[0][1].split(','))

    # Add STRICT_ALL_TABLES to the list of modes
    modes.add('STRICT_ALL_TABLES')
    new_modes = ','.join(modes)
    cursor.execute('SET sql_mode = %s', new_modes)

    cursor.close()


class StrictSQLAlchemy(SQLAlchemy):
    def init_app(self, app, *args, **kwargs):
        super().init_app(app, *args, **kwargs)

        with app.app_context():
            if isinstance(self.engine.dialect, MySQLDialect):
                event.listens_for(self.engine, 'connect')(mysql_on_connect)


db = StrictSQLAlchemy()


class User(db.Model):
    id = db.Column(db.Integer(), primary_key=True)
    username = db.Column(db.String(length=20), unique=True)


app = Flask()
db.init_app(app)

user = User(username='a_very_long_username_that_doesnt_fit_in_20_characters')
db.session.add(user)
db.session.commit()

And now, instead of a warning and a truncated value, I get a proper exception:现在,我得到了一个适当的异常,而不是警告和截断的值:

sqlalchemy.exc.DataError: (pymysql.err.DataError) (1406, "Data too long for column 'username' at row 1")

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM