类型错误:'property'对象不是可迭代的 SQLAlchemy



Alembic的自动生成程序不断抛出这个错误,这对我来说相当神秘:

INFO  [alembic.runtime.migration] Context impl PostgresqlImpl.
INFO  [alembic.runtime.migration] Will assume transactional DDL.
Traceback (most recent call last):
File "/home/mas/projects/icc/icc2-backend/.venv/bin/alembic", line 8, in <module>
sys.exit(main())
File "/home/mas/projects/icc/icc2-backend/.venv/lib/python3.9/site-packages/alembic/config.py", line 581, in main
CommandLine(prog=prog).main(argv=argv)
File "/home/mas/projects/icc/icc2-backend/.venv/lib/python3.9/site-packages/alembic/config.py", line 575, in main
self.run_cmd(cfg, options)
File "/home/mas/projects/icc/icc2-backend/.venv/lib/python3.9/site-packages/alembic/config.py", line 552, in run_cmd
fn(
File "/home/mas/projects/icc/icc2-backend/.venv/lib/python3.9/site-packages/alembic/command.py", line 214, in revision
script_directory.run_env()
File "/home/mas/projects/icc/icc2-backend/.venv/lib/python3.9/site-packages/alembic/script/base.py", line 489, in run_env
util.load_python_file(self.dir, "env.py")
File "/home/mas/projects/icc/icc2-backend/.venv/lib/python3.9/site-packages/alembic/util/pyfiles.py", line 98, in load_python_file
module = load_module_py(module_id, path)
File "/home/mas/projects/icc/icc2-backend/.venv/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 855, in exec_module
File "<frozen importlib._bootstrap>", line 228, in _call_with_frames_removed
File "/home/mas/projects/icc/icc2-backend/alembic/env.py", line 84, in <module>
run_migrations_online()
File "/home/mas/projects/icc/icc2-backend/alembic/env.py", line 78, in run_migrations_online
context.run_migrations()
File "<string>", line 8, in run_migrations
File "/home/mas/projects/icc/icc2-backend/.venv/lib/python3.9/site-packages/alembic/runtime/environment.py", line 846, in run_migrations
self.get_context().run_migrations(**kw)
File "/home/mas/projects/icc/icc2-backend/.venv/lib/python3.9/site-packages/alembic/runtime/migration.py", line 511, in run_migrations
for step in self._migrations_fn(heads, self):
File "/home/mas/projects/icc/icc2-backend/.venv/lib/python3.9/site-packages/alembic/command.py", line 190, in retrieve_migrations
revision_context.run_autogenerate(rev, context)
File "/home/mas/projects/icc/icc2-backend/.venv/lib/python3.9/site-packages/alembic/autogenerate/api.py", line 444, in run_autogenerate
self._run_environment(rev, migration_context, True)
File "/home/mas/projects/icc/icc2-backend/.venv/lib/python3.9/site-packages/alembic/autogenerate/api.py", line 483, in _run_environment
compare._populate_migration_script(
File "/home/mas/projects/icc/icc2-backend/.venv/lib/python3.9/site-packages/alembic/autogenerate/compare.py", line 25, in _populate_migration_script
_produce_net_changes(autogen_context, upgrade_ops)
File "/home/mas/projects/icc/icc2-backend/.venv/lib/python3.9/site-packages/alembic/autogenerate/compare.py", line 50, in _produce_net_changes
comparators.dispatch("schema", autogen_context.dialect.name)(
File "/home/mas/projects/icc/icc2-backend/.venv/lib/python3.9/site-packages/alembic/util/langhelpers.py", line 303, in go
fn(*arg, **kw)
File "/home/mas/projects/icc/icc2-backend/.venv/lib/python3.9/site-packages/alembic/autogenerate/compare.py", line 75, in _autogen_for_tables
[(table.schema, table.name) for table in autogen_context.sorted_tables]
File "/home/mas/projects/icc/icc2-backend/.venv/lib/python3.9/site-packages/alembic/util/langhelpers.py", line 230, in __get__
obj.__dict__[self.__name__] = result = self.fget(obj)
File "/home/mas/projects/icc/icc2-backend/.venv/lib/python3.9/site-packages/alembic/autogenerate/api.py", line 362, in sorted_tables
result.extend(m.sorted_tables)
TypeError: 'property' object is not iterable

我的模型是这样的:

# app/models/mixins.py
from app import db
from sqlalchemy.ext.declarative import declared_attr, as_declarative
@as_declarative()
class Base(db.Model):
"""This Base class does nothing. It is here in case I need to expand
implement something later. I feel like it's a good early practice.
Attributes
----------
id : int
The basic primary key id number of any class.
Notes
-----
The __tablename__ is automatically set to the class name lower-cased.
There's no need to mess around with underscores, that just confuses the
issue and makes programmatically referencing the table more difficult.
"""
__abstract__ = True
id = db.Column(db.Integer, primary_key=True)
@declared_attr
def __tablename__(cls):
return cls.__name__.lower()
# app/models/annotations.py
from datetime import datetime as dt
from app import db
from app.models.mixins import Base
class Annotation(Base):
bookid = db.Column(db.Integer)
author = db.Column(db.String)
created = db.Column(db.DateTime)
edits = db.relationship('Edit', back_populates='annotation')
HEAD = db.relationship('Edit',
primaryjoin='Edit.annotation_id==Annotation.id',
uselist=False)
def __init__(self, book, author, start, end, text, *args, **kwargs):
self.created = dt.now()
self.bookid = book
self.author = author
super().__init__(*args, **kwargs)
self.edits.append(Edit(author, start, end, text))

class Edit(Base):
annotation_id = db.Column(db.Integer, db.ForeignKey('annotation.id'),
index=True)
text = db.Column(db.Text)
editor = db.Column(db.String)
start = db.Column(db.Integer)
end = db.Column(db.Integer)
created = db.Column(db.DateTime)
annotation = db.relationship('Annotation', back_populates='edits')
def __init__(self, editor, start, end, text, *args, **kwargs):
self.editor = editor
self.start, self.end = start, end
self.text = text
self.created = dt.utcnow()
super().__init__(*args, **kwargs)

因为我不确定它是否相关,我的alembic/env.py

from logging.config import fileConfig
from sqlalchemy import engine_from_config
from sqlalchemy import pool
from alembic import context
import os, inspect, sys
currentdir = os.path.dirname(os.path.abspath(inspect.getfile(inspect.currentframe())))
parentdir = os.path.dirname(currentdir)
sys.path.insert(0, parentdir)
from config import Config
config = context.config # alembic
config.set_main_option('sqlalchemy.url', Config.SQLALCHEMY_DATABASE_URI)
print(Config.SQLALCHEMY_DATABASE_URI)

# Interpret the config file for Python logging.
# This line sets up loggers basically.
fileConfig(config.config_file_name)

from app import db
target_metadata = db.MetaData
# other values from the config, defined by the needs of env.py,
# can be acquired:
# my_important_option = config.get_main_option("my_important_option")
# ... etc.

def run_migrations_offline():
"""Run migrations in 'offline' mode.
This configures the context with just a URL
and not an Engine, though an Engine is acceptable
here as well.  By skipping the Engine creation
we don't even need a DBAPI to be available.
Calls to context.execute() here emit the given string to the
script output.
"""
url = config.get_main_option("sqlalchemy.url")
context.configure(
url=url,
target_metadata=target_metadata,
literal_binds=True,
dialect_opts={"paramstyle": "named"},
)
with context.begin_transaction():
context.run_migrations()

def run_migrations_online():
"""Run migrations in 'online' mode.
In this scenario we need to create an Engine
and associate a connection with the context.
"""
connectable = engine_from_config(
config.get_section(config.config_ini_section),
prefix="sqlalchemy.",
poolclass=pool.NullPool,
)
with connectable.connect() as connection:
context.configure(
connection=connection, target_metadata=target_metadata
)
with context.begin_transaction():
context.run_migrations()

if context.is_offline_mode():
run_migrations_offline()
else:
run_migrations_online()

代码全部在https://github.com/anno-wiki/icc2-backend如果这有帮助的话。我希望有人能对此有所了解。Alembic的代码相当抽象,很难理解为什么这一行在我的配置中出现问题。我以前用过很多次Base mixin,它从来都不是问题。

似乎通过将target_metadata = db.MetaData更改为target_metadata = db.metdata解决了问题,尽管后来没有检测到模型。导入app.models.Annotation并使用Annotation.metadata使整个过程顺利进行。

最新更新