Browse Source

Added migrations via Flask-Migrate

sh4nks 11 years ago
parent
commit
7228d577bf
9 changed files with 177 additions and 19 deletions
  1. 4 1
      flaskbb/app.py
  2. 4 0
      flaskbb/extensions.py
  3. 1 1
      flaskbb/forum/views.py
  4. 18 12
      manage.py
  5. 1 0
      migrations/README
  6. 45 0
      migrations/alembic.ini
  7. 73 0
      migrations/env.py
  8. 22 0
      migrations/script.py.mako
  9. 9 5
      requirements.txt

+ 4 - 1
flaskbb/app.py

@@ -26,7 +26,7 @@ from flaskbb.admin.views import admin
 from flaskbb.forum.views import forum
 
 from flaskbb.extensions import (db, login_manager, mail, cache, redis,
-                                debugtoolbar)
+                                debugtoolbar, migrate)
 from flaskbb.utils.helpers import (format_date, time_since, crop_title,
                                    can_post_reply, can_post_topic,
                                    can_delete_topic, can_delete_post, is_online,
@@ -74,6 +74,9 @@ def configure_extensions(app):
     # Flask-SQLAlchemy
     db.init_app(app)
 
+    # Flask-Migrate
+    migrate.init_app(app, db)
+
     # Flask-Mail
     mail.init_app(app)
 

+ 4 - 0
flaskbb/extensions.py

@@ -14,6 +14,7 @@ from flask.ext.mail import Mail
 from flask.ext.cache import Cache
 from flask.ext.debugtoolbar import DebugToolbarExtension
 from flask.ext.redis import Redis
+from flask.ext.migrate import Migrate
 
 # Database
 db = SQLAlchemy()
@@ -32,3 +33,6 @@ redis = Redis()
 
 # Debugtoolbar
 debugtoolbar = DebugToolbarExtension()
+
+# Migrations
+migrate = Migrate()

+ 1 - 1
flaskbb/forum/views.py

@@ -122,7 +122,7 @@ def view_forum(forum_id):
 def markread(forum_id=None):
 
     if not current_user.is_authenticated():
-        flash("We do not support cookie based unread forums yet.", "danger")
+        flash("You need to be logged in for that feature.", "danger")
         return redirect(url_for("forum.index"))
 
     # Mark a single forum as read

+ 18 - 12
manage.py

@@ -12,6 +12,7 @@
 """
 from flask import current_app
 from flask.ext.script import Manager, Shell, Server
+from flask.ext.migrate import MigrateCommand
 
 from flaskbb import create_app
 from flaskbb.extensions import db
@@ -30,6 +31,9 @@ manager = Manager(app)
 # Run local server
 manager.add_command("runserver", Server("localhost", port=8080))
 
+# Migration commands
+manager.add_command('db', MigrateCommand)
+
 
 # Add interactive project shell
 def make_shell_context():
@@ -39,18 +43,21 @@ manager.add_command("shell", Shell(make_context=make_shell_context))
 
 @manager.command
 def initdb():
-    """
-    Creates the database.
-    """
+    """Creates the database."""
 
     db.create_all()
 
 
 @manager.command
+def dropdb():
+    """Deletes the database"""
+
+    db.drop_all()
+
+
+@manager.command
 def createall():
-    """
-    Creates the database with some example content.
-    """
+    """Creates the database with some example content."""
 
     # Just for testing purposes
     db.drop_all()
@@ -59,20 +66,19 @@ def createall():
     create_test_data()
 
 
+# TODO: Implement this...
 @manager.command
 def create_admin():
-    """
-    Creates the admin user
-    """
+    """Creates the admin user"""
+
     db.create_all()
     create_admin_user()
 
 
 @manager.command
 def create_default_data():
-    """
-    This should be created by every flaskbb installation
-    """
+    """This should be created by every flaskbb installation"""
+
     db.create_all()
     create_default_groups()
     create_welcome_forum()

+ 1 - 0
migrations/README

@@ -0,0 +1 @@
+Generic single-database configuration.

+ 45 - 0
migrations/alembic.ini

@@ -0,0 +1,45 @@
+# A generic, single database configuration.
+
+[alembic]
+# template used to generate migration files
+# file_template = %%(rev)s_%%(slug)s
+
+# set to 'true' to run the environment during
+# the 'revision' command, regardless of autogenerate
+# revision_environment = false
+
+
+# Logging configuration
+[loggers]
+keys = root,sqlalchemy,alembic
+
+[handlers]
+keys = console
+
+[formatters]
+keys = generic
+
+[logger_root]
+level = WARN
+handlers = console
+qualname =
+
+[logger_sqlalchemy]
+level = WARN
+handlers =
+qualname = sqlalchemy.engine
+
+[logger_alembic]
+level = INFO
+handlers =
+qualname = alembic
+
+[handler_console]
+class = StreamHandler
+args = (sys.stderr,)
+level = NOTSET
+formatter = generic
+
+[formatter_generic]
+format = %(levelname)-5.5s [%(name)s] %(message)s
+datefmt = %H:%M:%S

+ 73 - 0
migrations/env.py

@@ -0,0 +1,73 @@
+from __future__ import with_statement
+from alembic import context
+from sqlalchemy import engine_from_config, pool
+from logging.config import fileConfig
+
+# this is the Alembic Config object, which provides
+# access to the values within the .ini file in use.
+config = context.config
+
+# Interpret the config file for Python logging.
+# This line sets up loggers basically.
+fileConfig(config.config_file_name)
+
+# add your model's MetaData object here
+# for 'autogenerate' support
+# from myapp import mymodel
+# target_metadata = mymodel.Base.metadata
+from flask import current_app
+config.set_main_option('sqlalchemy.url', current_app.config.get('SQLALCHEMY_DATABASE_URI'))
+target_metadata = current_app.extensions['migrate'].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)
+
+    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.
+
+    """
+    engine = engine_from_config(
+                config.get_section(config.config_ini_section),
+                prefix='sqlalchemy.',
+                poolclass=pool.NullPool)
+
+    connection = engine.connect()
+    context.configure(
+                connection=connection,
+                target_metadata=target_metadata
+                )
+
+    try:
+        with context.begin_transaction():
+            context.run_migrations()
+    finally:
+        connection.close()
+
+if context.is_offline_mode():
+    run_migrations_offline()
+else:
+    run_migrations_online()
+

+ 22 - 0
migrations/script.py.mako

@@ -0,0 +1,22 @@
+"""${message}
+
+Revision ID: ${up_revision}
+Revises: ${down_revision}
+Create Date: ${create_date}
+
+"""
+
+# revision identifiers, used by Alembic.
+revision = ${repr(up_revision)}
+down_revision = ${repr(down_revision)}
+
+from alembic import op
+import sqlalchemy as sa
+${imports if imports else ""}
+
+def upgrade():
+    ${upgrades if upgrades else "pass"}
+
+
+def downgrade():
+    ${downgrades if downgrades else "pass"}

+ 9 - 5
requirements.txt

@@ -4,25 +4,29 @@ Flask-Cache==0.12
 Flask-DebugToolbar==0.9.0
 Flask-Login==0.2.9
 Flask-Mail==0.9.0
+Flask-Migrate==1.2.0
 Flask-SQLAlchemy==1.0
 Flask-Script==0.6.6
 Flask-Themes2==0.1.3
 Flask-WTF==0.9.4
 Jinja2==2.7.2
+Mako==0.9.1
 MarkupSafe==0.18
 Pygments==1.6
 SQLAlchemy==0.9.1
 WTForms==1.0.5
 Werkzeug==0.9.4
-amqp==1.3.3
+alembic==0.6.2
+amqp==1.4.2
 anyjson==0.3.3
-billiard==3.3.0.13
+billiard==3.3.0.14
 blinker==1.3
-celery==3.1.7
+celery==3.1.8
+ipython==1.1.0
 itsdangerous==0.23
-kombu==3.0.8
+kombu==3.0.10
 postmarkup==1.2.0
 pytz==2013.9
-redis==2.9.0
+redis==2.9.1
 simplejson==3.3.2
 wsgiref==0.1.2