You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Flask-Migrate无法检测现有数据库表问题求助

问题:Flask-Migrate无法识别已存在的数据库表

跟着教程学习数据库迁移,执行flask db init命令后成功生成migrations文件夹,但运行flask db migrate -m 'Initial_Migration'时,无法检测到数据库中已存在的表,导致迁移脚本尝试创建models.py中的所有表,执行flask db upgrade时因尝试创建已存在的表报错。

models.py部分代码

# Third-party libraries
from sqlalchemy import Table, Column, Integer, String, Boolean, DateTime, ForeignKey, inspect, exc
from sqlalchemy.orm import relationship
from flask_login import UserMixin
from . import basic_functions as bf
import os
cfg = __import__(os.environ['CONFIG_NAME']) # Equal to "import config as cfg"
from . import db


#Set up logger
database_manager_logger = bf.create_logger('data_base_manager')

#Create all tables
class TopicSubscription(db.Model):
    __tablename__="TopicSubscription" #We set our own name because otherwise a name is chosen according to SQLAlchemy conventions which can lead to errors (Table name is used for foreign keys)
    id = Column(Integer, primary_key=True)
    user_id = Column(Integer, ForeignKey('User.id')) #Note this is referring to the table name, not the class name!
    topic_id = Column(Integer, ForeignKey('Topic.id'))

class User(db.Model,UserMixin):
    # id = Column(CHAR(21), primary_key=True)
    __tablename__ = "User"
    id = Column(Integer, primary_key=True)
    name = Column(String(80), unique=True, nullable=False)
    email = Column(String(120), unique=True, nullable=False)
    nickname = Column(String(80), unique=True, nullable=True)
    profile_pic = Column(String(200), unique=True, nullable=True)
    last_send_date = Column(DateTime) #Last time a mail was sent to the user.

    topics = relationship('Topic', secondary=TopicSubscription.__table__,
                          back_populates="users")  # Allows to access topics that this user subscribed to through the junction table TopicSubscription
    subscribed_questions = relationship("QuestionSubscription", primaryjoin="User.id==QuestionSubscription.user_id")

class Topic(db.Model):
    __tablename__ = "Topic"
    id = Column(Integer, primary_key=True)
    topic = Column(String(80)) #The topic name
    active = Column(Boolean, default=False)

    users = relationship('User', secondary=TopicSubscription.__table__, back_populates = "topics") #This back_populates refers to the "topics" attribute on the User object.
    # subscriptions = relationship("TopicSubscription", cascade="all, delete-orphan") #Seems like this is not used, delete it?
    questions = relationship("Question", cascade="all, delete-orphan")

初始化Flask-Migrate的__init__.py文件(重点查看initialize_database函数)

import os
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
from flask_login import LoginManager
import time

#######################
#### Configuration ####
#######################

# Initialize the database, login_manager and the app variables in the global scope so they are accessible everywhere.
db = SQLAlchemy()
from project.models import * #Define all tables, must be done at module level for some reason
login_manager = LoginManager()
app = None #Initialize the global app variable

######################################
#### Application Factory Function ####
######################################

def create_app(config_filename=None):

    global app #Declare the app as global else it is not accessible for other modules.
    app = Flask(__name__, instance_relative_config=True)
    app.config.from_pyfile(config_filename)  # Load the config file
    app.secret_key = os.environ.get("SECRET_KEY") or os.urandom(24)

    initialize_database(app)
    register_blueprints(app)
    start_scheduler()

    return app


##########################
#### Helper Functions ####
##########################

def initialize_database(app):
    # Since the application instance is now created, pass it to each Flask
    # extension instance to bind it to the Flask application instance (app)
    db.init_app(app)

    with app.app_context():
        db.engine.dialect.supports_sane_rowcount = db.engine.dialect.supports_sane_multi_rowcount = False  # Otherwise there's an error when deleting topics
        retry_database_connect(nr_of_retries=50)
        migrate = Migrate(app, db)  # This makes sure that any database changes are migrated.



def retry_database_connect(nr_of_retries:int):
    """Connects to the database, catching errors and re-trying for 25 seconds."""
    connection_error = True
    counter = 0
    while (connection_error == True and counter < nr_of_retries):
        try:
            # This has to be run before the creation of all tables, otherwise the newly created tables are deleted and there are zero tables to work with.
            # It knows which tables to drop because in the drop_all() command the connection to the sql database is made automatically (based on info in the db object)
            if cfg.drop_all_tables:
                print("Dropping all tables")
                db.drop_all()
            db.create_all()  # Must be called after all classes are defined (otherwise it doesn't create the tables.
            connection_error = False  # set connection error to false so we can continue
        except exc.SQLAlchemyError:
            counter += 1
            print("Connection failed " + str(counter) + " time(s).")
            time.sleep(5)
    if connection_error:
        raise exc.SQLAlchemyError  # connection failed, tried for 250 seconds.

def register_blueprints(app):
    # Since the application instance is now created, register each Blueprint
    # with the Flask application instance (app)
    from project.home import home_blueprint
    from project.login import login_blueprint
    from project.topic import topic_blueprint
    from project.question import question_blueprint
    from project.answer import answer_blueprint
    app.register_blueprint(home_blueprint)
    app.register_blueprint(login_blueprint)
    app.register_blueprint(topic_blueprint)
    app.register_blueprint(question_blueprint)
    app.register_blueprint(answer_blueprint)

def start_scheduler():
    import project.scheduler #This will trigger the scheduler.__init__ file which will start the scheduler

终端输出内容

INFO  [alembic.runtime.migration] Context impl MySQLImpl.
INFO  [alembic.runtime.migration] Will assume non-transactional DDL.
INFO  [alembic.autogenerate.compare] Detected added table 'Topic'
INFO  [alembic.autogenerate.compare] Detected added table 'User'
INFO  [alembic.autogenerate.compare] Detected added table 'Question'
INFO  [alembic.autogenerate.compare] Detected added table 'TopicSubscription'
INFO  [alembic.autogenerate.compare] Detected added table 'Evaluation'
INFO  [alembic.autogenerate.compare] Detected added table 'QuestionSubscription'
INFO  [alembic.autogenerate.compare] Detected removed table 'question'
INFO  [alembic.autogenerate.compare] Detected removed table 'topicsubscription'
INFO  [alembic.autogenerate.compare] Detected removed table 'evaluation'
INFO  [alembic.autogenerate.compare] Detected removed index 'email' on 'user'
INFO  [alembic.autogenerate.compare] Detected removed index 'name' on 'user'
INFO  [alembic.autogenerate.compare] Detected removed index 'nickname' on 'user'
INFO  [alembic.autogenerate.compare] Detected removed index 'profile_pic' on 'user'
INFO  [alembic.autogenerate.compare] Detected removed table 'user'
INFO  [alembic.autogenerate.compare] Detected removed table 'topic'
INFO  [alembic.autogenerate.compare] Detected removed table 'questionsubscription'

已尝试的解决方法

  • 试过相关Stack Overflow解决方案但无效,已将env.py还原为默认形式

请问哪里操作有误?

PS:不确定问题根源,仅分享了这些脚本,若需其他内容请告知。

内容的提问来源于stack exchange,提问作者ThaNoob

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.13 15:05:34