Flask SQL Alchemy listens_for 装饰器

Flask SQL Alchemy listens_for decorator

我正在尝试在 Flask 中创建待办事项列表应用程序。我对 Python 有很多经验,但对 Flask 框架还很陌生。

我有 3 个模型 - 用户、任务和项目,我希望有一个名为收件箱的默认项目,每个用户甚至在自己创建任何项目之前都拥有该项目。

用户和项目通过多对多关系关联。

我对此的尝试是在一个函数上使用以下装饰器,该函数创建一个名为 Inbox 的新项目。

@event.listens_for(Project.__table__, 'after_create')
def create_inbox(*args, **kwargs):
    inbox = Project(name='Inbox')
    db.session.add(inbox)
    db.session.commit()

然后,当我创建一个新用户时,我将 'Inbox' 项目添加到他们在 __init__ 函数中的项目中。

这是一个class刚用来设置User和Project之间的多对多关系:

class UserProject(UserMixin, db.Model):
    __tablename__ = 'userprojects'
    project_id = db.Column(db.Integer, db.ForeignKey('projects.id'), primary_key=True)
    user_id = db.Column(db.Integer, db.ForeignKey('users.id'), primary_key=True)

这是监听table创建的项目模型和装饰器:

class Project(UserMixin, db.Model):
    __tablename__ = 'projects'
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(32))
    tasks = db.relationship('Task', backref='project', lazy='dynamic') # one project has many tasks
    users = db.relationship('User', secondary='userprojects', lazy='dynamic', backref=db.backref('user', lazy='dynamic'))

@event.listens_for(Project.__table__, 'after_create')
def create_inbox(*args, **kwargs):
    inbox = Project(name='Inbox')
    db.session.add(inbox)
    db.session.commit()

这是用户模型:

class User(UserMixin, db.Model):
    __tablename__ = 'users'
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(80), unique=True, nullable=True )
    email = db.Column(db.String(120), unique=True, nullable=True)
    password_hash = db.Column(db.String(128))
    tasks = db.relationship('Task', backref='author', lazy='dynamic') # one user has many tasks
    projects = db.relationship('Project', secondary='userprojects', lazy='dynamic', backref=db.backref('project', lazy='dynamic'))

    def __init__(self, **kwargs):
        super(User, self).__init__(**kwargs)
        p = Project.query.first()
        if p not in self.projects.all():
            self.projects.append(p)

还有一个 Task 模型,但我没有将它包含在这里,因为它与问题无关。

出现的问题是当我尝试使用迁移和升级时。我执行 flask db init,迁移和升级并得到以下输出,所以它似乎一切正常,并且迁移文件看起来很好。

flask db init

Creating directory /Users/Jasmine/projects/flask/tasky/migrations ... done
Creating directory /Users/Jasmine/projects/flask/tasky/migrations/versions ... done
Generating /Users/Jasmine/projects/flask/tasky/migrations/script.py.mako ... done
Generating /Users/Jasmine/projects/flask/tasky/migrations/env.py ... done
Generating /Users/Jasmine/projects/flask/tasky/migrations/README ... done
Generating /Users/Jasmine/projects/flask/tasky/migrations/alembic.ini ... done
Please edit configuration/connection/logging settings in '/Users/Jasmine/projects/flask/tasky/migrations/alembic.ini' before proceeding.

flask db migrate

INFO  [alembic.runtime.migration] Context impl SQLiteImpl.
INFO  [alembic.runtime.migration] Will assume non-transactional DDL.
INFO  [alembic.autogenerate.compare] Detected added table 'projects'
INFO  [alembic.autogenerate.compare] Detected added table 'users'
INFO  [alembic.autogenerate.compare] Detected added table 'tasks'
INFO  [alembic.autogenerate.compare] Detected added index 'ix_tasks_due' on '['due']'
INFO  [alembic.autogenerate.compare] Detected added table 'userprojects'
Generating /Users/Jasmine/projects/flask/tasky/migrations/versions/64311763e16f_creating_the_tables.py ... done

flask db upgrade
INFO  [alembic.runtime.migration] Context impl SQLiteImpl.
INFO  [alembic.runtime.migration] Will assume non-transactional DDL.
INFO  [alembic.runtime.migration] Running upgrade  -> 64311763e16f, creating the tables

然而,当我进入 flask shell 并输入 Project.query.all() 时,我得到一个空列表,我希望得到一个包含一项的列表 - 一个名称为 'Inbox' 的项目.

我不确定错误在哪里,因此非常感谢您对此提出任何建议。

我经常看到有两种不同的方法来处理在数据库中创建初始数据,我猜你想到了第三种。

一个选项是创建一个脚本(或 flask 命令的自定义扩展),将所有初始值添加到数据库中。例如,在您执行 flask db upgrade 之后,您可以执行 flask db initdb 以添加该收件箱项目。您可以以可以 运行 多次的方式编写此 initdb 函数,并且它只会添加缺少的内容,而不会创建重复项。

另一种选择是编辑您的迁移脚本以包括添加此数据。 Alembic 提供了可用于此目的的 execute() and bulk_insert() 操作。因此,在 运行 flask db migrate 之后,您可以在编辑器中打开迁移脚本并手动编辑它以包括收件箱条目的创建。只有在您完成这些更改后,您才能 运行 flask db upgrade.

我认为您的解决方案有点矫枉过正。您正在设置一个事件观察器,用于在应用程序安装的整个生命周期中发生一次的事件。考虑到此事件处理程序将在您的应用程序 运行 期间处于活动状态,即使在 运行 期间根本不需要它。我建议您改用上面两种更直接的方法之一。