[英]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.