shop_platform - flask db downgrade 遇到 TypeError
事發經過
發現建立 cart table 時,漏掉 user_id 這個 foreign key,所以幫 user 和 cart 兩張資料表補上 one-one relationship。
class User(db.Model):
__tablename__ = 'user'
id = db.Column(db.Integer, primary_key=True)
display_name = db.Column(db.String(50), unique=True, nullable=False)
email = db.Column(db.String(50), unique=True, nullable=False)
password = db.Column(db.String(140), nullable=False)
cell_phone = db.Column(db.String(20))
address = db.Column(db.String(100))
store_introduction = db.Column(db.String(2000))
role = db.Column(db.String(10), nullable=False, default='user')
insert_time = db.Column(db.DateTime, nullable=False, default=datetime.now)
update_time = db.Column(db.DateTime, onupdate=datetime.now, nullable=False, default=datetime.now)
cart = db.relationship('Cart', back_populates='user', uselist=False)
delivery_orders = db.relationship('Order', back_populates='seller')
purchase_order = db.relationship('Order', back_populates='buyer')
class Cart(db.Model):
__tablename__ = 'cart'
id = db.Column(db.Integer, primary_key=True)
insert_time = db.Column(db.DateTime, nullable=False, default=datetime.now)
update_time = db.Column(db.DateTime, onupdate=datetime.now, nullable=False, default=datetime.now)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
user = db.relationship('User', back_populates='cart')
products = db.relationship('CartItem', back_populates='cart')
下了 flask db migrate -m "<migration name>"
指令後產生的 migration 檔:
"""add one-one relationship between cart and user table
Revision ID: 5b69bc8db7e7
Revises: cd6dce4eb1ee
Create Date: 2021-08-26 16:20:18.947078
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = '5b69bc8db7e7'
down_revision = 'cd6dce4eb1ee'
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('cart', sa.Column('user_id', sa.Integer(), nullable=True))
op.create_foreign_key(None, 'cart', 'user', ['user_id'], ['id'])
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_constraint(None, 'cart', type_='foreignkey')
op.drop_column('cart', 'user_id')
# ### end Alembic commands ###
下 flask db upgrade
指令時沒有問題,順利地在 cart table 加上 user_id 欄位,並且設了外鍵限制。
下 flask db migrate -m "<migration name>"
指令時出現錯誤訊息如下:
INFO [alembic.runtime.migration] Context impl MySQLImpl.
INFO [alembic.runtime.migration] Will assume non-transactional DDL.
INFO [alembic.runtime.migration] Running downgrade 5b69bc8db7e7 -> cd6dce4eb1ee, add one-one relationship between cart and user table
Traceback (most recent call last):
File "/Users/flora/.local/share/virtualenvs/shop_platform-9-YKR-2e/bin/flask", line 8, in <module>
sys.exit(main())
File "/Users/flora/.local/share/virtualenvs/shop_platform-9-YKR-2e/lib/python3.9/site-packages/flask/cli.py", line 990, in main
cli.main(args=sys.argv[1:])
File "/Users/flora/.local/share/virtualenvs/shop_platform-9-YKR-2e/lib/python3.9/site-packages/flask/cli.py", line 596, in main
return super().main(*args, **kwargs)
File "/Users/flora/.local/share/virtualenvs/shop_platform-9-YKR-2e/lib/python3.9/site-packages/click/core.py", line 1062, in main
rv = self.invoke(ctx)
File "/Users/flora/.local/share/virtualenvs/shop_platform-9-YKR-2e/lib/python3.9/site-packages/click/core.py", line 1668, in invoke
return _process_result(sub_ctx.command.invoke(sub_ctx))
File "/Users/flora/.local/share/virtualenvs/shop_platform-9-YKR-2e/lib/python3.9/site-packages/click/core.py", line 1668, in invoke
return _process_result(sub_ctx.command.invoke(sub_ctx))
File "/Users/flora/.local/share/virtualenvs/shop_platform-9-YKR-2e/lib/python3.9/site-packages/click/core.py", line 1404, in invoke
return ctx.invoke(self.callback, **ctx.params)
File "/Users/flora/.local/share/virtualenvs/shop_platform-9-YKR-2e/lib/python3.9/site-packages/click/core.py", line 763, in invoke
return __callback(*args, **kwargs)
File "/Users/flora/.local/share/virtualenvs/shop_platform-9-YKR-2e/lib/python3.9/site-packages/click/decorators.py", line 26, in new_func
return f(get_current_context(), *args, **kwargs)
File "/Users/flora/.local/share/virtualenvs/shop_platform-9-YKR-2e/lib/python3.9/site-packages/flask/cli.py", line 440, in decorator
return __ctx.invoke(f, *args, **kwargs)
File "/Users/flora/.local/share/virtualenvs/shop_platform-9-YKR-2e/lib/python3.9/site-packages/click/core.py", line 763, in invoke
return __callback(*args, **kwargs)
File "/Users/flora/.local/share/virtualenvs/shop_platform-9-YKR-2e/lib/python3.9/site-packages/flask_migrate/cli.py", line 167, in downgrade
_downgrade(directory, revision, sql, tag, x_arg)
File "/Users/flora/.local/share/virtualenvs/shop_platform-9-YKR-2e/lib/python3.9/site-packages/flask_migrate/__init__.py", line 98, in wrapped
f(*args, **kwargs)
File "/Users/flora/.local/share/virtualenvs/shop_platform-9-YKR-2e/lib/python3.9/site-packages/flask_migrate/__init__.py", line 195, in downgrade
command.downgrade(config, revision, sql=sql, tag=tag)
File "/Users/flora/.local/share/virtualenvs/shop_platform-9-YKR-2e/lib/python3.9/site-packages/alembic/command.py", line 335, in downgrade
script.run_env()
File "/Users/flora/.local/share/virtualenvs/shop_platform-9-YKR-2e/lib/python3.9/site-packages/alembic/script/base.py", line 490, in run_env
util.load_python_file(self.dir, "env.py")
File "/Users/flora/.local/share/virtualenvs/shop_platform-9-YKR-2e/lib/python3.9/site-packages/alembic/util/pyfiles.py", line 97, in load_python_file
module = load_module_py(module_id, path)
File "/Users/flora/.local/share/virtualenvs/shop_platform-9-YKR-2e/lib/python3.9/site-packages/alembic/util/compat.py", line 184, in load_module_py
spec.loader.exec_module(module)
File "<frozen importlib._bootstrap_external>", line 850, in exec_module
File "<frozen importlib._bootstrap>", line 228, in _call_with_frames_removed
File "migrations/env.py", line 91, in <module>
run_migrations_online()
File "migrations/env.py", line 85, in run_migrations_online
context.run_migrations()
File "<string>", line 8, in run_migrations
File "/Users/flora/.local/share/virtualenvs/shop_platform-9-YKR-2e/lib/python3.9/site-packages/alembic/runtime/environment.py", line 813, in run_migrations
self.get_context().run_migrations(**kw)
File "/Users/flora/.local/share/virtualenvs/shop_platform-9-YKR-2e/lib/python3.9/site-packages/alembic/runtime/migration.py", line 561, in run_migrations
step.migration_fn(**kw)
File "/Users/flora/projects/shop_platform/migrations/versions/5b69bc8db7e7_add_one_one_relationship_between_cart_.py", line 28, in downgrade
op.drop_constraint(None, 'cart', type_='foreignkey')
File "<string>", line 8, in drop_constraint
File "<string>", line 3, in drop_constraint
File "/Users/flora/.local/share/virtualenvs/shop_platform-9-YKR-2e/lib/python3.9/site-packages/alembic/operations/ops.py", line 150, in drop_constraint
return operations.invoke(op)
File "/Users/flora/.local/share/virtualenvs/shop_platform-9-YKR-2e/lib/python3.9/site-packages/alembic/operations/base.py", line 354, in invoke
return fn(self, operation)
File "/Users/flora/.local/share/virtualenvs/shop_platform-9-YKR-2e/lib/python3.9/site-packages/alembic/operations/toimpl.py", line 161, in drop_constraint
operations.impl.drop_constraint(
File "/Users/flora/.local/share/virtualenvs/shop_platform-9-YKR-2e/lib/python3.9/site-packages/alembic/ddl/mysql.py", line 133, in drop_constraint
super(MySQLImpl, self).drop_constraint(const)
File "/Users/flora/.local/share/virtualenvs/shop_platform-9-YKR-2e/lib/python3.9/site-packages/alembic/ddl/impl.py", line 266, in drop_constraint
self._exec(schema.DropConstraint(const))
File "/Users/flora/.local/share/virtualenvs/shop_platform-9-YKR-2e/lib/python3.9/site-packages/alembic/ddl/impl.py", line 146, in _exec
return conn.execute(construct, multiparams)
File "/Users/flora/.local/share/virtualenvs/shop_platform-9-YKR-2e/lib/python3.9/site-packages/sqlalchemy/engine/base.py", line 1263, in execute
return meth(self, multiparams, params, _EMPTY_EXECUTION_OPTS)
File "/Users/flora/.local/share/virtualenvs/shop_platform-9-YKR-2e/lib/python3.9/site-packages/sqlalchemy/sql/ddl.py", line 77, in _execute_on_connection
return connection._execute_ddl(
File "/Users/flora/.local/share/virtualenvs/shop_platform-9-YKR-2e/lib/python3.9/site-packages/sqlalchemy/engine/base.py", line 1350, in _execute_ddl
compiled = ddl.compile(
File "/Users/flora/.local/share/virtualenvs/shop_platform-9-YKR-2e/lib/python3.9/site-packages/sqlalchemy/sql/elements.py", line 489, in compile
return self._compiler(dialect, **kw)
File "/Users/flora/.local/share/virtualenvs/shop_platform-9-YKR-2e/lib/python3.9/site-packages/sqlalchemy/sql/ddl.py", line 29, in _compiler
return dialect.ddl_compiler(dialect, self, **kw)
File "/Users/flora/.local/share/virtualenvs/shop_platform-9-YKR-2e/lib/python3.9/site-packages/sqlalchemy/sql/compiler.py", line 454, in __init__
self.string = self.process(self.statement, **compile_kwargs)
File "/Users/flora/.local/share/virtualenvs/shop_platform-9-YKR-2e/lib/python3.9/site-packages/sqlalchemy/sql/compiler.py", line 489, in process
return obj._compiler_dispatch(self, **kwargs)
File "/Users/flora/.local/share/virtualenvs/shop_platform-9-YKR-2e/lib/python3.9/site-packages/sqlalchemy/ext/compiler.py", line 443, in <lambda>
lambda *arg, **kw: existing(*arg, **kw),
File "/Users/flora/.local/share/virtualenvs/shop_platform-9-YKR-2e/lib/python3.9/site-packages/sqlalchemy/ext/compiler.py", line 499, in __call__
expr = fn(element, compiler, **kw)
File "/Users/flora/.local/share/virtualenvs/shop_platform-9-YKR-2e/lib/python3.9/site-packages/alembic/ddl/mysql.py", line 397, in _mysql_drop_constraint
return compiler.visit_drop_constraint(element, **kw)
File "/Users/flora/.local/share/virtualenvs/shop_platform-9-YKR-2e/lib/python3.9/site-packages/sqlalchemy/dialects/mysql/base.py", line 2201, in visit_drop_constraint
const = self.preparer.format_constraint(constraint)
File "/Users/flora/.local/share/virtualenvs/shop_platform-9-YKR-2e/lib/python3.9/site-packages/sqlalchemy/sql/compiler.py", line 5095, in format_constraint
return self.truncate_and_render_constraint_name(
File "/Users/flora/.local/share/virtualenvs/shop_platform-9-YKR-2e/lib/python3.9/site-packages/sqlalchemy/sql/compiler.py", line 5119, in truncate_and_render_constraint_name
return self._truncate_and_render_maxlen_name(
File "/Users/flora/.local/share/virtualenvs/shop_platform-9-YKR-2e/lib/python3.9/site-packages/sqlalchemy/sql/compiler.py", line 5128, in _truncate_and_render_maxlen_name
self.dialect.validate_identifier(name)
File "/Users/flora/.local/share/virtualenvs/shop_platform-9-YKR-2e/lib/python3.9/site-packages/sqlalchemy/engine/default.py", line 576, in validate_identifier
if len(ident) > self.max_identifier_length:
TypeError: object of type 'NoneType' has no len()
解決辦法
用「TypeError: object of type 'NoneType' has no len() flask-migrate」當關鍵字,搜尋到 drop_constraint etc. should check for name=None and emit informative error message since this is common w/ autogenerate #588 這篇文章。看樣子,是因為呼叫 drop_constraint
時要輸入 constraint 的 name。
在 MySQLWorkbench 裡查到,自動生成的 constraint name 叫做 cart_ibfk_1,然後去修改 migration 檔:
"""add one-one relationship between cart and user table
Revision ID: 5b69bc8db7e7
Revises: cd6dce4eb1ee
Create Date: 2021-08-26 16:20:18.947078
"""
from alembic import op
import sqlalchemy as sa
# revision identifiers, used by Alembic.
revision = '5b69bc8db7e7'
down_revision = 'cd6dce4eb1ee'
branch_labels = None
depends_on = None
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('cart', sa.Column('user_id', sa.Integer(), nullable=True))
op.create_foreign_key('cart_ibfk_1', 'cart', 'user', ['user_id'], ['id'])
# ### end Alembic commands ###
def downgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.drop_constraint('cart_ibfk_1', 'cart', type_='foreignkey')
op.drop_column('cart', 'user_id')
# ### end Alembic commands ###
然後就沒事了。
commit: feat: add one-one relationship between user and cart table
參考資料
sqlalchemy 官網,對於 constraint 命名機制的建議:The Importance of Naming Constraints
Comments
Post a Comment