flask-sqlalchemy icon indicating copy to clipboard operation
flask-sqlalchemy copied to clipboard

CLI support

Open singingwolfboy opened this issue 8 years ago • 24 comments

Basic CLI support for Flask-SQLAlchemy, modeled after the CLI support in Flask-Security. Supports the following command:

  • flask db create

singingwolfboy avatar Jul 23 '17 17:07 singingwolfboy

I don't know how I feel about this. I'd rather people use Flask-Alembic or Flask-Migrate to manage creating/dropping tables.

Also, using db here is problematic as there is no way to say this is lower priority than Alembic/Migrate, so might overwrite those commands.

davidism avatar Jul 23 '17 19:07 davidism

FWIW, even when using Flask-Migrate and Alembic it's usually a good idea to let SQLAlchemy deal with creating the initial revision. However, I think adding support to call create_all (and at the same time stamp the alembic state to 'head') through the CLI exposed in there would make more sense.

ThiefMaster avatar Jul 23 '17 19:07 ThiefMaster

@davidism: It shouldn't be too hard to have Flask-Alembic or Flask-Migrate add commands to the db command group. Just import it and modify it. For example:

try:
    from flask_sqlalchemy.cli import db
except ImportError:
    @click.group()
    def db():
        pass

@db.command()
def migrate():
    # implementation here...

singingwolfboy avatar Jul 23 '17 20:07 singingwolfboy

@davidism: I created an example repository that demonstrates how I think we could resolve this problem. It turns out that if you set up both Flask-SQLAlchemy and Flask-Alembic, and they both try to create a command group named db, the second extension will clobber the first. This is actually a good thing: we can re-implement these Flask-SQLAlchemy commands in Flask-Alembic, and do them the Alembic way.

Example repo: https://github.com/singingwolfboy/flask-sqlalchemy-alembic-test

Can you take a look, and tell me what you think of this approach?

singingwolfboy avatar Jul 25 '17 20:07 singingwolfboy

But is that order guaranteed? There's no requirement that Flask-Alembic/Migrate is initialized after Flask-SQLAlchemy.

davidism avatar Jul 25 '17 20:07 davidism

It kind of makes sense to initialize Flask-SQLAlchemy before an extension that depends on it. Doesn't seem to be a hard requirement though (at least not when using init_app())

ThiefMaster avatar Jul 25 '17 20:07 ThiefMaster

I guarantee we will get users doing it in the "wrong" order unless Flask-Alembic/Migrate enforce it in init_app.

While I recognize that create/drop commands would be useful for quick apps, for pretty much any serious development I want to encourage people to use Alembic to manage their database. This is partially just a documentation issue. We should have a page that says "create your tables. If you're doing this a lot, use Alembic to manage migrations."

@miguelgrinberg any comments?

davidism avatar Jul 25 '17 20:07 davidism

Keep in mind that "quick apps" are a major use case for Flask. Many people who want database support in Flask aren't interested in setting up database migrations, because it's too much overhead for what they're looking for.

singingwolfboy avatar Jul 25 '17 21:07 singingwolfboy

The argument that this is useful for quick apps isn't strong in my opinion. A quick app will likely use a SQLite database. If I need to drop all tables in a SQLite database, I delete the database file, as that is fully accurate, unlike drop_all() which may miss tables if the database metadata changed from when the tables were created with create_all(). I honestly don't like drop_all() because it isn't implemented to do what you would expect, so I would not expose it more by adding a command for it.

I guess I can see a benefit in having a create command, but I suggest not implementing drop. Instead, I would add a --drop-tables-first option to create for example, which I think makes it a bit more obvious that you are dropping the tables that you are about to create, not the tables that were created long ago.

miguelgrinberg avatar Jul 25 '17 21:07 miguelgrinberg

Would you be willing to update Flask-Migrate so it ensures it's initialized after Flask-SQLAlchemy? That way we guarantee the correct commands are used.

davidism avatar Jul 25 '17 21:07 davidism

@davidism I assume you mean doing this, right? That's not a problem I can add that.

miguelgrinberg avatar Jul 25 '17 22:07 miguelgrinberg

@miguelgrinberg I hadn't thought about that argument against drop_all(). Between that, and @ThiefMaster's vote against it, I'm OK with removing the db drop command.

singingwolfboy avatar Jul 25 '17 22:07 singingwolfboy

@miguelgrinberg I'm going with

def init_app(self, app):
    if 'sqlalchemy' not in app.extensions:
        raise RuntimeError('you need to initialize Flask-SQLAlchemy first')

and adding a create command that just calls upgrade, so that users can't mistakenly call create when Alembic should be managing the tables.

Although overriding the create command on the import probably works too.

davidism avatar Jul 25 '17 23:07 davidism

Don't some people use SQLAlchemy directly, without using the Flask-SQLAlchemy extension? Do you think that any of them use Flask-Alembic or Flask-Migrate? If so, they probably won't be happy with a mandatory dependency on Flask-SQLAlchemy (although it's easy enough to work around, just by shoving some random object in app.extensions['sqlalchemy']...)

singingwolfboy avatar Jul 25 '17 23:07 singingwolfboy

Not sure about Flask-Migrate, Flask-Alembic only works if Flask-SQLAlchemy is installed, I don't currently support other setups.

davidism avatar Jul 25 '17 23:07 davidism

@davidism If Flask-Alembic already has a hard dependency on Flask-SQLAlchemy, then enforcing the ordering is definitely a good idea.

singingwolfboy avatar Jul 25 '17 23:07 singingwolfboy

Flask-Migrate also requires Flask-SQLAlchemy.

@davidism is it necessary to force the init of Flask-SQLalchemy first? Importing the db command should get things in the correct order, I think? And that way I don't have to force users to upgrade Flask-SQLAlchemy if they don't want to, specially considering some deprecated items might be going away soon.

miguelgrinberg avatar Jul 25 '17 23:07 miguelgrinberg

Either way will work, neither requires updating Flask-SQLAlchemy.

davidism avatar Jul 25 '17 23:07 davidism

I pushed a commit to remove the flask db drop command. Is there anything else that needs to be done here, or can this be merged?

singingwolfboy avatar Jul 28 '17 01:07 singingwolfboy

@singingwolfboy The db_drop import in test_cli.py failed the build for some Python versions.

Thanks for the PR ;) it seems quite handy!

pavelpascari avatar Aug 18 '17 13:08 pavelpascari

The pull request is now passing tests. Is there anything else that needs to be done here, or can this be merged?

singingwolfboy avatar Sep 12 '17 20:09 singingwolfboy

Bump here? Also, needs rebase :)

gaffney avatar May 09 '18 16:05 gaffney

@gaffney thanks for the reminder! I've rebased the pull request.

singingwolfboy avatar May 09 '18 16:05 singingwolfboy

Note that Travis CI is only failing this pull request because it's trying to build on Python 2.6, which SQLAlchemy no longer supports. #621 should resolve this issue.

singingwolfboy avatar May 10 '18 08:05 singingwolfboy

Closing for now, this is too outdated. Opened #1090 to summarize and discuss a bit more.

davidism avatar Sep 18 '22 18:09 davidism