diff --git a/bootstrap/init_db.py b/bootstrap/init_db.py new file mode 100644 index 0000000..e51c118 --- /dev/null +++ b/bootstrap/init_db.py @@ -0,0 +1,8 @@ +from oshipka.persistance import populate_static, init_db + +from populate import populate_db +from webapp.app import app + +if init_db(app): + populate_static(app) + populate_db(app) diff --git a/bootstrap/manager.py b/bootstrap/manager.py new file mode 100644 index 0000000..9a9d944 --- /dev/null +++ b/bootstrap/manager.py @@ -0,0 +1,17 @@ +from flask_migrate import MigrateCommand +from flask_script import Manager + +from config import SQLALCHEMY_DATABASE_URI +from oshipka.persistance import db, migrate +from oshipka.webapp import app + +app.config["SQLALCHEMY_DATABASE_URI"] = SQLALCHEMY_DATABASE_URI +app.config["SQLALCHEMY_TRACK_MODIFICATIONS"] = False +db.init_app(app) +migrate.init_app(app, db) +manager = Manager(app) +manager.add_command('db', MigrateCommand) +from webapp.models import * + +if __name__ == '__main__': + manager.run() diff --git a/bootstrap/run.py b/bootstrap/run.py index 7254975..1d516f8 100644 --- a/bootstrap/run.py +++ b/bootstrap/run.py @@ -1,6 +1,5 @@ from config import TEMPLATES_FOLDER, STATIC_FOLDER -from oshipka import init_db -from oshipka.persistance import populate_static +from oshipka.persistance import populate_static, init_db from populate import populate_db from webapp.app import app diff --git a/oshipka.sh b/oshipka.sh index e6b4708..f422ce9 100755 --- a/oshipka.sh +++ b/oshipka.sh @@ -8,7 +8,7 @@ echo "oshipka is at: $OSHIPKA_PATH" #!/usr/bin/env bash HELP=" -Usage $0 [ bootstrap | model | init | worker | web | venv | install | link | cert ] +Usage $0 [ bootstrap | model | db_migrate | db_upgrade | init | worker | web | venv | install | link | cert ] bootstrap [PROJECT_PATH] Create a new project in PROJECT_PATH model [MODEL_NAME] Create or update a model @@ -99,6 +99,11 @@ bootstrap() { cd ${PROJECT_PATH} init_venv link_dev_oshipka + source venv/bin/activate + python manager.py db init + python manager.py db migrate -m "Initial migration." + python init_db.py + python manager.py db upgrade } run_in_prod() { @@ -114,6 +119,28 @@ model() { python "${OSHIPKA_PATH}/vm_gen/vm_gen.py" "`pwd`" } +db_migrate() { + shift + source venv/bin/activate + python init_db.py + db_upgrade + python manager.py db migrate + _post_migrate +} + +db_upgrade() { + shift + source venv/bin/activate + python init_db.py + python manager.py db upgrade +} + +_post_migrate() { + for i in migrations/versions/*.py; do + sed -i "s/sqlalchemy_utils.types.choice.ChoiceType(length=255), /sa.String(), / " "$i"; + done +} + command_main() { INITIAL_COMMAND=$1 case "$INITIAL_COMMAND" in @@ -121,6 +148,10 @@ command_main() { ;; model) model "$@" ;; + db_migrate) db_migrate "$@" + ;; + db_upgrade) db_upgrade "$@" + ;; init) init "$@" ;; worker) worker "$@" diff --git a/oshipka/__init__.py b/oshipka/__init__.py index 7b92f26..85d6b62 100644 --- a/oshipka/__init__.py +++ b/oshipka/__init__.py @@ -1,7 +1,7 @@ -from oshipka.persistance import init_db -from oshipka.webapp import app - if __name__ == "__main__": + from oshipka.persistance import init_db + from oshipka.webapp import app + init_db(app) app.run(debug=True) diff --git a/oshipka/persistance/__init__.py b/oshipka/persistance/__init__.py index 52a8a5c..01171bb 100644 --- a/oshipka/persistance/__init__.py +++ b/oshipka/persistance/__init__.py @@ -7,7 +7,10 @@ from importlib import import_module from json import JSONEncoder from uuid import uuid4 -from config import SQLALCHEMY_DATABASE_URI, MAKEDIRS, DATABASE_FILE, SEARCH_INDEX_PATH, STATIC_DATA_DIR +from config import SQLALCHEMY_DATABASE_URI, MAKEDIRS, DATABASE_FILE, SEARCH_INDEX_PATH, STATIC_DATA_DIR, basepath +from flask_migrate import Migrate, Manager, MigrateCommand +from flask_migrate import upgrade as migrate_upgrade +from flask_migrate import init as migrate_init from flask_security import RoleMixin, UserMixin from flask_security import Security, SQLAlchemyUserDatastore from flask_sqlalchemy import SQLAlchemy @@ -20,9 +23,10 @@ from sqlalchemy_utils import Choice from tww.lib import solve_query, resolve_timezone, dt_tz_translation, time_ago from whooshalchemy import IndexService from oshipka.search import add_to_index, remove_from_index, query_index -from util.strings import camel_case_to_snake_case +from oshipka.util.strings import camel_case_to_snake_case db = SQLAlchemy() +migrate = Migrate() class Ownable(object): @@ -232,15 +236,19 @@ def init_db(app): app.register_blueprint(oshipka_bp) db.init_app(app) + migrate.init_app(app, db) security.init_app(app, user_datastore) register_filters(app) for dir in MAKEDIRS: os.makedirs(dir, exist_ok=True) + if not os.path.exists(os.path.join(basepath, 'migrations')): + with app.app_context(): + migrate_init() if not os.path.exists(DATABASE_FILE): with app.app_context(): - db.create_all() + migrate_upgrade() rv = True global index_service index_service.proxied = IndexService(config=app.config, session=db.session) diff --git a/util/__init__.py b/oshipka/util/__init__.py similarity index 100% rename from util/__init__.py rename to oshipka/util/__init__.py diff --git a/util/os.py b/oshipka/util/os.py similarity index 100% rename from util/os.py rename to oshipka/util/os.py diff --git a/util/process.py b/oshipka/util/process.py similarity index 100% rename from util/process.py rename to oshipka/util/process.py diff --git a/util/simple_file_cache.py b/oshipka/util/simple_file_cache.py similarity index 100% rename from util/simple_file_cache.py rename to oshipka/util/simple_file_cache.py diff --git a/util/strings.py b/oshipka/util/strings.py similarity index 100% rename from util/strings.py rename to oshipka/util/strings.py diff --git a/oshipka/webapp/views.py b/oshipka/webapp/views.py index 249807b..1dea866 100644 --- a/oshipka/webapp/views.py +++ b/oshipka/webapp/views.py @@ -5,7 +5,7 @@ import inflect from flask import flash, render_template, redirect, request, url_for from oshipka.persistance import db -from util.strings import camel_case_to_snake_case +from oshipka.util.strings import camel_case_to_snake_case def get_instance(model_view, uuid): diff --git a/requirements.txt b/requirements.txt index b1a0504..2205e1a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -10,6 +10,7 @@ Flask-Babel==1.0.0 Flask-BabelEx==0.9.4 Flask-Login==0.5.0 Flask-Mail==0.9.1 +Flask-Migrate==2.5.3 Flask-Principal==0.4.0 Flask-Security==3.0.0 Flask-SQLAlchemy==2.4.1 diff --git a/vm_gen/templates/_model_m_n_py b/vm_gen/templates/_model_m_n_py new file mode 100644 index 0000000..9db24ca --- /dev/null +++ b/vm_gen/templates/_model_m_n_py @@ -0,0 +1,5 @@ +[[ name ]] = db.Table('[[ name ]]', +[%- for column in columns %] + db.Column('[[ column.name ]]_id', db.Integer(), db.ForeignKey('[[ column.name ]].id')), +[%- endfor %] +) \ No newline at end of file diff --git a/vm_gen/templates/_model_py b/vm_gen/templates/_model_py new file mode 100644 index 0000000..f635d56 --- /dev/null +++ b/vm_gen/templates/_model_py @@ -0,0 +1,26 @@ +[%- if _choice_types %] +from sqlalchemy_utils import ChoiceType +[%- endif %] + +[%- if _secondaries %] +from webapp.models import [% for secondary in _secondaries %][[ secondary ]][%- if not loop.last %], [% endif %][% endfor %] +[%- endif %] + +class [[ name ]](db.Model, ModelController): + [%- include "_model_choice_header_py" %] + [%- include "_model_searchable_header_py" %] + + [%- for column in columns %] + [%- if column._type == 'relationship' %] + [%- include "_relationship_py" %] + [%- else %] + [[ column.name ]] = db.Column([[ column._type ]], + [%- if column.default %]default="[[ column.default ]]",[%- endif %] + [%- if column.index %]index=True,[%- endif %]) + [%- endif %] + [%- endfor %] + + [%- if extra_code %] + + [[ extra_code ]] + [%- endif %] diff --git a/vm_gen/templates/model_py b/vm_gen/templates/model_py index 49f73fc..fb12075 100644 --- a/vm_gen/templates/model_py +++ b/vm_gen/templates/model_py @@ -1,34 +1,13 @@ from oshipka.persistance import db, ModelController -[%- if _choice_types %] -from sqlalchemy_utils import ChoiceType -[%- endif %] - -[%- if _secondaries %] -from webapp.models import [% for secondary in _secondaries %][[ secondary ]][%- if not loop.last %], [% endif %][% endfor %] -[%- endif %] - [%- if imports %] [%- for import in imports %] [[ import ]] [%- endfor %] [%- endif %] -class [[ name ]](db.Model, ModelController): - [%- include "_model_choice_header_py" %] - [%- include "_model_searchable_header_py" %] - - [%- for column in columns %] - [%- if column._type == 'relationship' %] - [%- include "_relationship_py" %] - [%- else %] - [[ column.name ]] = db.Column([[ column._type ]], - [%- if column.default %]default="[[ column.default ]]",[%- endif %] - [%- if column.index %]index=True,[%- endif %]) - [%- endif %] - [%- endfor %] - - [%- if extra_code %] - - [[ extra_code ]] - [%- endif %] +[%- if type == 'm_n' %] +[% include "_model_m_n_py" %] +[%- else %] +[% include "_model_py" %] +[%- endif %] \ No newline at end of file diff --git a/vm_gen/vm_gen.py b/vm_gen/vm_gen.py index 56f1526..0896481 100644 --- a/vm_gen/vm_gen.py +++ b/vm_gen/vm_gen.py @@ -1,13 +1,12 @@ import os -import shutil import sys import autopep8 import inflect import yaml -from jinja2 import Environment, FileSystemLoader, select_autoescape +from jinja2 import Environment, FileSystemLoader -from util.strings import snake_case_to_camel_case, camel_case_to_snake_case +from oshipka.util.strings import snake_case_to_camel_case, camel_case_to_snake_case pep_options = {'max_line_length': 120}