【问题标题】:SQLAchemy db.drop_all() gives AttributeError: 'NoneType' object has no attribute 'lower'SQLAlchemy db.drop_all() 给出 AttributeError: 'NoneType' object has no attribute 'lower'
【发布时间】:2018-12-08 18:16:03
【问题描述】:

我正在使用烧瓶开发 API,它是各种扩展。我有一个与本地 MySQL 数据库建立连接的数据库。当我尝试执行db.drop_all()时,出现以下错误:

Traceback (most recent call last):
  File "/usr/lib/python3.6/code.py", line 91, in runcode
    exec(code, self.locals)
  File "<input>", line 2, in <module>
  File "/home/zahessi/.local/lib/python3.6/site-packages/flask_sqlalchemy/__init__.py", line 971, in drop_all
    self._execute_for_all_tables(app, bind, 'drop_all')
  File "/home/zahessi/.local/lib/python3.6/site-packages/flask_sqlalchemy/__init__.py", line 955, in _execute_for_all_tables
    op(bind=self.get_engine(app, bind), **extra)
  File "/home/zahessi/.local/lib/python3.6/site-packages/sqlalchemy/sql/schema.py", line 4032, in drop_all
    tables=tables)
  File "/home/zahessi/.local/lib/python3.6/site-packages/sqlalchemy/engine/base.py", line 1940, in _run_visitor
    conn._run_visitor(visitorcallable, element, **kwargs)
  File "/home/zahessi/.local/lib/python3.6/site-packages/sqlalchemy/engine/base.py", line 1549, in _run_visitor
    **kwargs).traverse_single(element)
  File "/home/zahessi/.local/lib/python3.6/site-packages/sqlalchemy/sql/visitors.py", line 121, in traverse_single
    return meth(obj, **kw)
  File "/home/zahessi/.local/lib/python3.6/site-packages/sqlalchemy/sql/ddl.py", line 909, in visit_metadata
    self.traverse_single(fkc)
  File "/home/zahessi/.local/lib/python3.6/site-packages/sqlalchemy/sql/visitors.py", line 121, in traverse_single
    return meth(obj, **kw)
  File "/home/zahessi/.local/lib/python3.6/site-packages/sqlalchemy/sql/ddl.py", line 971, in visit_foreign_key_constraint
    self.connection.execute(DropConstraint(constraint))
  File "/home/zahessi/.local/lib/python3.6/site-packages/sqlalchemy/engine/base.py", line 948, in execute
    return meth(self, multiparams, params)
  File "/home/zahessi/.local/lib/python3.6/site-packages/sqlalchemy/sql/ddl.py", line 68, in _execute_on_connection
    return connection._execute_ddl(self, multiparams, params)
  File "/home/zahessi/.local/lib/python3.6/site-packages/sqlalchemy/engine/base.py", line 1003, in _execute_ddl
    if not self.schema_for_object.is_default else None)
  File "<string>", line 1, in <lambda>
  File "/home/zahessi/.local/lib/python3.6/site-packages/sqlalchemy/sql/elements.py", line 442, in compile
    return self._compiler(dialect, bind=bind, **kw)
  File "/home/zahessi/.local/lib/python3.6/site-packages/sqlalchemy/sql/ddl.py", line 26, in _compiler
    return dialect.ddl_compiler(dialect, self, **kw)
  File "/home/zahessi/.local/lib/python3.6/site-packages/sqlalchemy/sql/compiler.py", line 219, in __init__
    self.string = self.process(self.statement, **compile_kwargs)
  File "/home/zahessi/.local/lib/python3.6/site-packages/sqlalchemy/sql/compiler.py", line 245, in process
    return obj._compiler_dispatch(self, **kwargs)
  File "/home/zahessi/.local/lib/python3.6/site-packages/sqlalchemy/sql/visitors.py", line 81, in _compiler_dispatch
    return meth(self, **kw)
  File "/home/zahessi/.local/lib/python3.6/site-packages/sqlalchemy/dialects/mysql/base.py", line 1312, in visit_drop_constraint
    const = self.preparer.format_constraint(constraint)
  File "<string>", line 1, in <lambda>
  File "/home/zahessi/.local/lib/python3.6/site-packages/sqlalchemy/sql/compiler.py", line 3151, in format_constraint
    return self.quote(constraint.name)
  File "/home/zahessi/.local/lib/python3.6/site-packages/sqlalchemy/sql/compiler.py", line 3101, in quote
    if self._requires_quotes(ident):
  File "/home/zahessi/.local/lib/python3.6/site-packages/sqlalchemy/sql/compiler.py", line 3072, in _requires_quotes
    lc_value = value.lower()
AttributeError: 'NoneType' object has no attribute 'lower'

唯一有用的是删除整个数据库,然后重新创建它。但是我只通过 CLI 来做这件事,所以在代码中没有传统的方法来做。

编辑:数据库在这个工厂初始化:

def create_app():
    from flask import Flask
    from models import db, ma
    from flask_compress import Compress
    from sqlathanor import initialize_flask_sqlathanor

    # initialization
    app = Flask(__name__)
    app.config.from_object('config.Config')
    db.init_app(app)
    ma.init_app(app)
    initialize_flask_sqlathanor(db)
    Compress(app)

    # blueprints
    from blueprints.users_crud.views import MANAGE_USERS_BLUE
    from blueprints.projects_tasks.views import PROJECTS_BLUE
    app.register_blueprint(MANAGE_USERS_BLUE)
    app.register_blueprint(PROJECTS_BLUE)

    return app, db

在我测试时,错误不依赖于此处调用的扩展。 我的目标是删除所有表。

【问题讨论】:

  • 我认为如果对这些问题的答案进行编辑,您的问题会更清楚:db 是如何声明的?您是要删除表还是删除数据库?
  • @OluwafemiSule 更新了帖子。
  • 谢谢。似乎模型中定义的约束之一可能缺少名称,但这可能表明db 的声明方式存在不同的问题(请参阅github.com/mitsuhiko/flask-sqlalchemy/blob/…)。您是否在 Flask 应用程序上下文中运行 db.drop_all()
  • @OluwafemiSule 我确实这样做了。但是你怎么能声明一个没有名字的约束呢?我的所有模型都是通过类定义的,包括多对多关系的连接表。
  • 惊喜!惊喜!看起来一个人能够声明一个没有名称的约束,而 SQLAlchemy 对此一无所知。一些数据库引擎在没有给出约束名时会自动创建一个唯一的约束名。 docs.sqlalchemy.org/en/latest/core/…。您也可以在 Flask-SQLAlchemy 中配置命名约定,或者使用像 Alembic 这样的迁移工具来跟踪约束名称github.com/mitsuhiko/flask-sqlalchemy/blob/…

标签: python python-3.x flask sqlalchemy flask-sqlalchemy


【解决方案1】:

如果没有为约束设置名称,SQLAlchemy 不会为其创建名称,并将约束名称的分配留给数据库。 [1]

在对具有以这种方式创建的约束的表执行 drop 或 alter 语句时,这种方法会发生错误。这是因为 SQLAlchemy 不知道要为约束解析什么名称。 [2]

虽然通过以下方式删除所有表时应该可以反映表及其外键:

@manager.command
def dropdb():
    db.metadata.reflect(bind=db.engine)
    db.metadata.drop_all(db.engine)

对于 MySQL 方言,外键不会反映在表中,并且在反映时需要指定 ForeignKeyConstraint 名称。 [3] 当您查看生成的迁移脚本时,这就是 Alembic 的做法。

在回答您的问题时,需要命名约束。例如

class Branch(Base):
    //...
    project_id = Column(Integer, ForeignKey('projects.id', name="fk_branch_project_id_projects"))

对应用程序模型中声明的所有ForeignKey 执行此操作很痛苦,SQLAlchemy 提供了一种更好的方法来提供一致的命名约定。用于约束的命名约定需要写在models.py 中。 [4]

#...
from sqlalchemy import Column, Integer, DateTime, MetaData

convention = {
  "ix": 'ix_%(column_0_label)s',
  "uq": "uq_%(table_name)s_%(column_0_name)s",
  "ck": "ck_%(table_name)s_%(constraint_name)s",
  "fk": "fk_%(table_name)s_%(column_0_name)s_%(referred_table_name)s",
  "pk": "pk_%(table_name)s"
}
metadata = MetaData(naming_convention=convention)
db = SQLAlchemy(model_class=FlaskBaseModel, metadata=metadata)
#...

除此之外,我强烈建议使用像 Alembic 这样的迁移工具,而不是每次都删除并创建所有表来将更改应用到应用程序模型。

【讨论】:

    猜你喜欢
    • 2017-03-08
    • 1970-01-01
    • 2022-12-15
    • 2017-10-20
    • 2022-12-04
    • 2022-01-10
    • 1970-01-01
    • 2014-07-13
    • 2021-07-25
    相关资源
    最近更新 更多