From d82e680036df842e6a4cf3f268f58f6ef0192338 Mon Sep 17 00:00:00 2001 From: kujyp Date: Wed, 11 Apr 2018 15:22:49 +0900 Subject: [PATCH 1/7] Add create_app (#6) --- app/__init__.py | 131 +++++------------------------------------------- app/views.py | 118 +++++++++++++++++++++++++++++++++++++++++++ wsgi.py | 5 +- 3 files changed, 134 insertions(+), 120 deletions(-) create mode 100644 app/views.py diff --git a/app/__init__.py b/app/__init__.py index 11ecdd9..c4bc4b8 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -1,129 +1,22 @@ -import json -import traceback - -from flask import Flask, request, render_template, session +from flask import Flask from flask_cors import CORS -from app.v1.controllers.libraries import libraries_api from app.database import db from app.database.models import Library -from app.config import Config -from app.v1.controllers.maps import maps_api - - -app = Flask(__name__) -app.secret_key = Config.secret_key -CORS(app, resources={r"/apply": {"origins": "*"}}) -app.register_blueprint(libraries_api, url_prefix='/api/v1') -app.register_blueprint(maps_api, url_prefix='/api/v1') - -libraries = db.query(Library) - - -def register_session(args): - session["name"] = args["name"] - session["location_road"] = args["roadAddress"] - session["location_number"] = args["numberAddress"] - session["location_detail"] = args["detailAddress"] - session["manager_name"] = args["managerName"] - session["manager_email"] = args["managerEmail"] - session["manager_phone"] = args["managerPhone"] - session["audiences"] = args["capacity"] - session["fac_beam_screen"] = 1 if args["facilityBeamOrScreen"] else 0 - session["fac_sound"] = 1 if args["facilitySound"] else 0 - session["fac_record"] = 1 if args["facilityRecord"] else 0 - session["fac_placard"] = 1 if args["facilityPlacard"] else 0 - session["fac_self_promo"] = 1 if args["facilitySelfPromo"] else 0 - session["fac_other"] = args["facilityOther"] - session["req_speaker"] = args["requirements"] - - -@app.route("/") -def index(): - return render_template("done.html") - - -@app.route("/success") -def success(): - return render_template("success.html") - - -@app.route("/failure") -def failure(): - return render_template( - "failure.html", - name=session.get("name", "정보 없음"), - location_road=session.get("location_road", "정보 없음"), - location_number=session.get("location_number", "정보 없음"), - location_detail=session.get("location_detail", "정보 없음"), - manager_name=session.get("manager_name", "정보 없음"), - manager_email=session.get("manager_email", "정보 없음"), - manager_phone=session.get("manager_phone", "정보 없음"), - audiences=session.get("audiences", "정보 없음"), - fac_beam_screen="가능" if session.get( - "fac_beam_screen", False) else "불가", - fac_sound="가능" if session.get("fac_sound", False) else "불가", - fac_record="가능" if session.get("fac_record", False) else "불가", - fac_placard="가능" if session.get("fac_placard", False) else "불가", - fac_self_promo="가능" if session.get("fac_self_promo", False) else "불가", - fac_other=session.get("fac_other", "정보 없음"), - req_speaker=session.get("req_speaker", "정보 없음") - ) - - -@app.route("/applylist") -def applylist(): - try: - return render_template("list.html", - libraries=libraries, - length=len(list(libraries))) - except: # noqa: E722 - print(traceback.format_exc()) -@app.route("/api/v1/apply", methods=["POST"]) -def apply(): - try: - args = json.loads(request.data.decode('utf-8')) - except: # noqa: E722 - print("Invalid Request Payload") - return json.dumps({ - "result": -1, - "cause": "Invalid request payload" - }) +def create_app(config): + _app = Flask(__name__) + _app.config.from_object(config) + CORS(_app, resources={r"/apply": {"origins": "*"}}) - register_session(args) + from app.views import views + _app.register_blueprint(views) - db.add_all([ - Library( - name=args["name"], - location_road=args["roadAddress"], - location_number=args["numberAddress"], - location_detail=args["detailAddress"], - manager_name=args["managerName"], - manager_email=args["managerEmail"], - manager_phone=args["managerPhone"], - audiences=args["capacity"], - fac_beam_screen=1 if args["facilityBeamOrScreen"] else 0, - fac_sound=1 if args["facilitySound"] else 0, - fac_record=1 if args["facilityRecord"] else 0, - fac_placard=1 if args["facilityPlacard"] else 0, - fac_self_promo=1 if args["facilitySelfPromo"] else 0, - fac_other=args["facilityOther"], - req_speaker=args["requirements"] - ) - ]) + from app.v1.controllers.libraries import libraries_api + from app.v1.controllers.maps import maps_api - try: - db.commit() + _app.register_blueprint(libraries_api, url_prefix='/api/v1') + _app.register_blueprint(maps_api, url_prefix='/api/v1') - return json.dumps({ - "result": 0 - }) - except: # noqa: E722 - db.rollback() - print("Unexpected DB server error") - return json.dumps({ - "result": 1, - "cause": "Unexpected DB server error" - }) + return _app diff --git a/app/views.py b/app/views.py new file mode 100644 index 0000000..3e307ac --- /dev/null +++ b/app/views.py @@ -0,0 +1,118 @@ +import json +import traceback + +from flask import request, render_template, session, Blueprint + +from app import Library, db + +views = Blueprint('views', __name__) +libraries = db.query(Library) + + +def register_session(args): + session["name"] = args["name"] + session["location_road"] = args["roadAddress"] + session["location_number"] = args["numberAddress"] + session["location_detail"] = args["detailAddress"] + session["manager_name"] = args["managerName"] + session["manager_email"] = args["managerEmail"] + session["manager_phone"] = args["managerPhone"] + session["audiences"] = args["capacity"] + session["fac_beam_screen"] = 1 if args["facilityBeamOrScreen"] else 0 + session["fac_sound"] = 1 if args["facilitySound"] else 0 + session["fac_record"] = 1 if args["facilityRecord"] else 0 + session["fac_placard"] = 1 if args["facilityPlacard"] else 0 + session["fac_self_promo"] = 1 if args["facilitySelfPromo"] else 0 + session["fac_other"] = args["facilityOther"] + session["req_speaker"] = args["requirements"] + + +@views.route("/") +def index(): + return render_template("done.html") + + +@views.route("/success") +def success(): + return render_template("success.html") + + +@views.route("/failure") +def failure(): + return render_template( + "failure.html", + name=session.get("name", "정보 없음"), + location_road=session.get("location_road", "정보 없음"), + location_number=session.get("location_number", "정보 없음"), + location_detail=session.get("location_detail", "정보 없음"), + manager_name=session.get("manager_name", "정보 없음"), + manager_email=session.get("manager_email", "정보 없음"), + manager_phone=session.get("manager_phone", "정보 없음"), + audiences=session.get("audiences", "정보 없음"), + fac_beam_screen="가능" if session.get( + "fac_beam_screen", False) else "불가", + fac_sound="가능" if session.get("fac_sound", False) else "불가", + fac_record="가능" if session.get("fac_record", False) else "불가", + fac_placard="가능" if session.get("fac_placard", False) else "불가", + fac_self_promo="가능" if session.get("fac_self_promo", False) else "불가", + fac_other=session.get("fac_other", "정보 없음"), + req_speaker=session.get("req_speaker", "정보 없음") + ) + + +@views.route("/applylist") +def applylist(): + try: + return render_template("list.html", + libraries=libraries, + length=len(list(libraries))) + except: # noqa: E722 + print(traceback.format_exc()) + + +@views.route("/api/v1/apply", methods=["POST"]) +def apply(): + try: + args = json.loads(request.data.decode('utf-8')) + except: # noqa: E722 + print("Invalid Request Payload") + return json.dumps({ + "result": -1, + "cause": "Invalid request payload" + }) + + register_session(args) + + db.add_all([ + Library( + name=args["name"], + location_road=args["roadAddress"], + location_number=args["numberAddress"], + location_detail=args["detailAddress"], + manager_name=args["managerName"], + manager_email=args["managerEmail"], + manager_phone=args["managerPhone"], + audiences=args["capacity"], + fac_beam_screen=1 if args["facilityBeamOrScreen"] else 0, + fac_sound=1 if args["facilitySound"] else 0, + fac_record=1 if args["facilityRecord"] else 0, + fac_placard=1 if args["facilityPlacard"] else 0, + fac_self_promo=1 if args["facilitySelfPromo"] else 0, + fac_other=args["facilityOther"], + req_speaker=args["requirements"] + ) + ]) + + try: + db.commit() + + return json.dumps({ + "result": 0 + }) + except: # noqa: E722 + db.rollback() + print("Unexpected DB server error") + return json.dumps({ + "result": 1, + "cause": "Unexpected DB server error" + }) diff --git a/wsgi.py b/wsgi.py index 9a6940c..006eb05 100644 --- a/wsgi.py +++ b/wsgi.py @@ -1,4 +1,7 @@ -from app import app +from app import create_app +from app.config import Config + if __name__ == "__main__": + app = create_app(Config) app.run(host="0.0.0.0", port=5000, debug=True) From 7fe8d001f53611383f99fd8cd6ebeacf025fb179 Mon Sep 17 00:00:00 2001 From: kujyp Date: Wed, 11 Apr 2018 16:05:30 +0900 Subject: [PATCH 2/7] App gonna be using Flask-SQLAlchemy instead using SQLAlchemy directly --- .env.sample | 2 ++ app/__init__.py | 2 ++ app/config.py | 4 ++- app/database/__init__.py | 15 ++-------- app/database/models.py | 51 +++++++++++++++++---------------- app/v1/controllers/__init__.py | 3 +- app/v1/controllers/libraries.py | 24 ++++++++-------- app/v1/controllers/maps.py | 3 +- app/views.py | 3 +- 9 files changed, 52 insertions(+), 55 deletions(-) diff --git a/.env.sample b/.env.sample index efef254..c8e97bb 100644 --- a/.env.sample +++ b/.env.sample @@ -5,3 +5,5 @@ DB_PASSWORD= DB_NAME=mydb DB_PORT=3306 SQLALCHEMY_DATABASE_URI=mysql+pymysql://${DB_USERNAME}:${DB_PASSWORD}@${DB_HOST}:${DB_PORT}/${DB_NAME}?charset=utf8 +SQLALCHEMY_TRACK_MODIFICATIONS=True +SQLALCHEMY_ECHO=True diff --git a/app/__init__.py b/app/__init__.py index c4bc4b8..1a288bc 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -19,4 +19,6 @@ def create_app(config): _app.register_blueprint(libraries_api, url_prefix='/api/v1') _app.register_blueprint(maps_api, url_prefix='/api/v1') + db.init_app(_app) + return _app diff --git a/app/config.py b/app/config.py index ee61e0a..56878a6 100644 --- a/app/config.py +++ b/app/config.py @@ -11,4 +11,6 @@ class Config: db_password = os.environ.get("DB_PASSWORD") db_name = os.environ.get("DB_NAME") db_port = int(os.environ.get("DB_PORT")) - db_uri = os.environ.get("SQLALCHEMY_DATABASE_URI") + SQLALCHEMY_DATABASE_URI = os.environ.get("SQLALCHEMY_DATABASE_URI") + SQLALCHEMY_TRACK_MODIFICATIONS = bool(os.environ.get("SQLALCHEMY_TRACK_MODIFICATIONS")) + SQLALCHEMY_ECHO = bool(os.environ.get("SQLALCHEMY_ECHO")) diff --git a/app/database/__init__.py b/app/database/__init__.py index c4a690d..f0b13d6 100644 --- a/app/database/__init__.py +++ b/app/database/__init__.py @@ -1,14 +1,3 @@ -from sqlalchemy import create_engine -from sqlalchemy.orm import scoped_session, sessionmaker -from sqlalchemy.ext.declarative import declarative_base -from app.config import Config +from flask_sqlalchemy import SQLAlchemy -engine = create_engine(Config.db_uri, echo=True, convert_unicode=True) -db = scoped_session(sessionmaker( - autocommit=False, autoflush=False, bind=engine)) -Base = declarative_base() -Base.query = db.query_property() - - -def initialize_database(): - Base.metadata.create_all(bind=engine) +db = SQLAlchemy() diff --git a/app/database/models.py b/app/database/models.py index dac7560..cafaeae 100644 --- a/app/database/models.py +++ b/app/database/models.py @@ -2,35 +2,35 @@ from sqlalchemy.dialects.mysql import TINYINT, INTEGER from sqlalchemy.orm import validates -from app.database import Base from app.utils.errors import InvalidArgumentError +from app.database import db -class Library(Base): +class Library(db.Model): __tablename__ = 'Libraries' - _id = Column('id', - INTEGER(11, unsigned=True), - primary_key=True, - autoincrement=True) - name = Column('name', Text) - location_road = Column('location_road', Text) - location_number = Column('location_number', Text) - location_detail = Column('location_detail', Text) - - manager_name = Column('manager_name', Text) - manager_email = Column('manager_email', Text) - manager_phone = Column('manager_phone', Text) - audiences = Column('audiences', Text) - - fac_beam_screen = Column('fac_beam_screen', TINYINT(1)) - fac_sound = Column('fac_sound', TINYINT(1)) - fac_record = Column('fac_record', TINYINT(1)) - fac_placard = Column('fac_placard', TINYINT(1)) - fac_self_promo = Column('fac_self_promo', TINYINT(1)) - - fac_other = Column('fac_other', Text) - req_speaker = Column('req_speaker', Text) + _id = db.Column('id', + INTEGER(11, unsigned=True), + primary_key=True, + autoincrement=True) + name = db.Column('name', Text) + location_road = db.Column('location_road', Text) + location_number = db.Column('location_number', Text) + location_detail = db.Column('location_detail', Text) + + manager_name = db.Column('manager_name', Text) + manager_email = db.Column('manager_email', Text) + manager_phone = db.Column('manager_phone', Text) + audiences = db.Column('audiences', Text) + + fac_beam_screen = db.Column('fac_beam_screen', TINYINT(1)) + fac_sound = db.Column('fac_sound', TINYINT(1)) + fac_record = db.Column('fac_record', TINYINT(1)) + fac_placard = db.Column('fac_placard', TINYINT(1)) + fac_self_promo = db.Column('fac_self_promo', TINYINT(1)) + + fac_other = db.Column('fac_other', Text) + req_speaker = db.Column('req_speaker', Text) @validates('name') def validate_not_empty(self, key, field): @@ -38,3 +38,6 @@ def validate_not_empty(self, key, field): raise InvalidArgumentError("{} must be not empty.".format(key)) return field + + def __repr__(self): + return '' % self.name diff --git a/app/v1/controllers/__init__.py b/app/v1/controllers/__init__.py index 3ae30df..ef8ff0a 100644 --- a/app/v1/controllers/__init__.py +++ b/app/v1/controllers/__init__.py @@ -1,9 +1,8 @@ -from app.database import db from app.utils.errors import DataNotFoundError def get_or_404(model_clazz, pk): - instance = db.query(model_clazz).filter_by(_id=pk).first() + instance = model_clazz.query.filter_by(_id=pk).first() if instance is None: raise DataNotFoundError( "{} {} Not found".format(model_clazz.__name__, pk)) diff --git a/app/v1/controllers/libraries.py b/app/v1/controllers/libraries.py index 64bd4c4..e20395f 100644 --- a/app/v1/controllers/libraries.py +++ b/app/v1/controllers/libraries.py @@ -99,7 +99,7 @@ def __init__(self): @marshal_with(library_fields) def get(self): - libraries = db.query(Library).all() + libraries = Library.query.all() return libraries @@ -110,15 +110,15 @@ def post(self): library = Library(**args) try: - db.add(library) - db.commit() + db.session.add(library) + db.session.commit() except IntegrityError as e: print(str(e)) - db.rollback() + db.session.rollback() raise DuplicatedDataError(str(e.orig)) except Exception as e: print(str(e)) - db.rollback() + db.session.rollback() raise e return library @@ -143,15 +143,15 @@ def put(self, pk): setattr(library, key, value) try: - db.merge(library) - db.commit() + db.session.merge(library) + db.session.commit() except IntegrityError as e: print(str(e)) - db.rollback() + db.session.rollback() raise DuplicatedDataError(str(e.orig)) except Exception as e: print(str(e)) - db.rollback() + db.session.rollback() raise e return library @@ -160,11 +160,11 @@ def delete(self, pk): library = get_or_404(Library, pk) try: - db.delete(library) - db.commit() + db.session.delete(library) + db.session.commit() except Exception as e: print(str(e)) - db.rollback() + db.session.rollback() raise e return '', 204 diff --git a/app/v1/controllers/maps.py b/app/v1/controllers/maps.py index 16fb98e..d203de4 100644 --- a/app/v1/controllers/maps.py +++ b/app/v1/controllers/maps.py @@ -2,7 +2,6 @@ from flask_restful import (Resource, fields, marshal_with, Api) -from app.database import db from app.database.models import Library map_fields = { @@ -19,7 +18,7 @@ def __init__(self): @marshal_with(map_fields) def get(self): - libraries = db.query(Library).all() + libraries = Library.query.all() for library in libraries: library.province = library.location_road[0:2] diff --git a/app/views.py b/app/views.py index 3e307ac..05e51c7 100644 --- a/app/views.py +++ b/app/views.py @@ -6,7 +6,6 @@ from app import Library, db views = Blueprint('views', __name__) -libraries = db.query(Library) def register_session(args): @@ -63,6 +62,8 @@ def failure(): @views.route("/applylist") def applylist(): try: + libraries = Library.query.all() + return render_template("list.html", libraries=libraries, length=len(list(libraries))) From a0abc3fdc9076a684000014e23c7b1948a591883 Mon Sep 17 00:00:00 2001 From: kujyp Date: Wed, 11 Apr 2018 16:06:31 +0900 Subject: [PATCH 3/7] Fix error due to None from database --- app/v1/controllers/maps.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/v1/controllers/maps.py b/app/v1/controllers/maps.py index d203de4..32779a4 100644 --- a/app/v1/controllers/maps.py +++ b/app/v1/controllers/maps.py @@ -21,7 +21,8 @@ def get(self): libraries = Library.query.all() for library in libraries: - library.province = library.location_road[0:2] + if library.location_road: + library.province = library.location_road[0:2] return libraries From 5ecaacc078d216b4436c60c5636d3e1f4668d755 Mon Sep 17 00:00:00 2001 From: kujyp Date: Wed, 11 Apr 2018 16:21:12 +0900 Subject: [PATCH 4/7] Fix .env.sample error due to READ comment (#15) --- .env.sample | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.env.sample b/.env.sample index c8e97bb..dedef6b 100644 --- a/.env.sample +++ b/.env.sample @@ -1,5 +1,6 @@ SECRET_KEY = localsecretkey -DB_HOST= # Blank means localhost +# Blank means localhost +DB_HOST= DB_USERNAME=root DB_PASSWORD= DB_NAME=mydb From a9e98d1285e297564af61e8e2bdb74e7fae8795d Mon Sep 17 00:00:00 2001 From: kujyp Date: Wed, 11 Apr 2018 16:43:33 +0900 Subject: [PATCH 5/7] Add flask_migrate as dependency (#17) --- README.md | 11 +++- app/__init__.py | 2 + migrations/README | 1 + migrations/alembic.ini | 45 ++++++++++++++ migrations/env.py | 87 ++++++++++++++++++++++++++++ migrations/script.py.mako | 24 ++++++++ migrations/versions/ad3c95a8a551_.py | 46 +++++++++++++++ requirements.txt | 1 + wsgi.py | 3 +- 9 files changed, 218 insertions(+), 2 deletions(-) create mode 100755 migrations/README create mode 100644 migrations/alembic.ini create mode 100755 migrations/env.py create mode 100755 migrations/script.py.mako create mode 100644 migrations/versions/ad3c95a8a551_.py diff --git a/README.md b/README.md index cf8ca89..38fe261 100644 --- a/README.md +++ b/README.md @@ -6,4 +6,13 @@ ``` cat ~/.ssh/id_rsa.pub | sudo ssh -i .pem ubuntu@18.219.223.174 "mkdir -p ~/.ssh && chmod 700 ~/.ssh && cat >> ~/.ssh/authorized_keys" -``` \ No newline at end of file +``` + + +## Database migration 가이드 + +migrations 스크립트를 수행하려면 터미널에서 아래 명령어 실행 +``` +export FLASK_APP=wsgi.py +flask db upgrade +``` diff --git a/app/__init__.py b/app/__init__.py index 1a288bc..ce6f5ca 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -1,5 +1,6 @@ from flask import Flask from flask_cors import CORS +from flask_migrate import Migrate from app.database import db from app.database.models import Library @@ -20,5 +21,6 @@ def create_app(config): _app.register_blueprint(maps_api, url_prefix='/api/v1') db.init_app(_app) + Migrate(_app, db) return _app diff --git a/migrations/README b/migrations/README new file mode 100755 index 0000000..98e4f9c --- /dev/null +++ b/migrations/README @@ -0,0 +1 @@ +Generic single-database configuration. \ No newline at end of file diff --git a/migrations/alembic.ini b/migrations/alembic.ini new file mode 100644 index 0000000..f8ed480 --- /dev/null +++ b/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 diff --git a/migrations/env.py b/migrations/env.py new file mode 100755 index 0000000..23663ff --- /dev/null +++ b/migrations/env.py @@ -0,0 +1,87 @@ +from __future__ import with_statement +from alembic import context +from sqlalchemy import engine_from_config, pool +from logging.config import fileConfig +import logging + +# 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) +logger = logging.getLogger('alembic.env') + +# 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. + + """ + + # this callback is used to prevent an auto-migration from being generated + # when there are no changes to the schema + # reference: http://alembic.zzzcomputing.com/en/latest/cookbook.html + def process_revision_directives(context, revision, directives): + if getattr(config.cmd_opts, 'autogenerate', False): + script = directives[0] + if script.upgrade_ops.is_empty(): + directives[:] = [] + logger.info('No changes in schema detected.') + + 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, + process_revision_directives=process_revision_directives, + **current_app.extensions['migrate'].configure_args) + + try: + with context.begin_transaction(): + context.run_migrations() + finally: + connection.close() + +if context.is_offline_mode(): + run_migrations_offline() +else: + run_migrations_online() diff --git a/migrations/script.py.mako b/migrations/script.py.mako new file mode 100755 index 0000000..2c01563 --- /dev/null +++ b/migrations/script.py.mako @@ -0,0 +1,24 @@ +"""${message} + +Revision ID: ${up_revision} +Revises: ${down_revision | comma,n} +Create Date: ${create_date} + +""" +from alembic import op +import sqlalchemy as sa +${imports if imports else ""} + +# revision identifiers, used by Alembic. +revision = ${repr(up_revision)} +down_revision = ${repr(down_revision)} +branch_labels = ${repr(branch_labels)} +depends_on = ${repr(depends_on)} + + +def upgrade(): + ${upgrades if upgrades else "pass"} + + +def downgrade(): + ${downgrades if downgrades else "pass"} diff --git a/migrations/versions/ad3c95a8a551_.py b/migrations/versions/ad3c95a8a551_.py new file mode 100644 index 0000000..2323832 --- /dev/null +++ b/migrations/versions/ad3c95a8a551_.py @@ -0,0 +1,46 @@ +"""empty message + +Revision ID: ad3c95a8a551 +Revises: +Create Date: 2018-04-11 16:30:59.069984 + +""" +from alembic import op +import sqlalchemy as sa +from sqlalchemy.dialects import mysql + +# revision identifiers, used by Alembic. +revision = 'ad3c95a8a551' +down_revision = None +branch_labels = None +depends_on = None + + +def upgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.create_table('Libraries', + sa.Column('id', mysql.INTEGER(display_width=11, unsigned=True), nullable=False), + sa.Column('name', sa.Text(), nullable=True), + sa.Column('location_road', sa.Text(), nullable=True), + sa.Column('location_number', sa.Text(), nullable=True), + sa.Column('location_detail', sa.Text(), nullable=True), + sa.Column('manager_name', sa.Text(), nullable=True), + sa.Column('manager_email', sa.Text(), nullable=True), + sa.Column('manager_phone', sa.Text(), nullable=True), + sa.Column('audiences', sa.Text(), nullable=True), + sa.Column('fac_beam_screen', mysql.TINYINT(display_width=1), nullable=True), + sa.Column('fac_sound', mysql.TINYINT(display_width=1), nullable=True), + sa.Column('fac_record', mysql.TINYINT(display_width=1), nullable=True), + sa.Column('fac_placard', mysql.TINYINT(display_width=1), nullable=True), + sa.Column('fac_self_promo', mysql.TINYINT(display_width=1), nullable=True), + sa.Column('fac_other', sa.Text(), nullable=True), + sa.Column('req_speaker', sa.Text(), nullable=True), + sa.PrimaryKeyConstraint('id') + ) + # ### end Alembic commands ### + + +def downgrade(): + # ### commands auto generated by Alembic - please adjust! ### + op.drop_table('Libraries') + # ### end Alembic commands ### diff --git a/requirements.txt b/requirements.txt index 4eb558e..8f6c43a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,3 +4,4 @@ PyMySQL==0.7.11 SQLAlchemy==1.1.15 python_dotenv==0.8.2 Flask-RESTful==0.3.6 +Flask-Migrate==2.1.1 diff --git a/wsgi.py b/wsgi.py index 006eb05..06ef8a6 100644 --- a/wsgi.py +++ b/wsgi.py @@ -2,6 +2,7 @@ from app.config import Config +app = create_app(Config) + if __name__ == "__main__": - app = create_app(Config) app.run(host="0.0.0.0", port=5000, debug=True) From 69d3886ecbdd7d413cd8cfbdd52f84efc056155c Mon Sep 17 00:00:00 2001 From: kujyp Date: Wed, 11 Apr 2018 16:47:13 +0900 Subject: [PATCH 6/7] Fix lint errors --- app/__init__.py | 1 - app/config.py | 3 ++- app/database/models.py | 2 +- app/views.py | 3 ++- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/app/__init__.py b/app/__init__.py index ce6f5ca..42e25f3 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -3,7 +3,6 @@ from flask_migrate import Migrate from app.database import db -from app.database.models import Library def create_app(config): diff --git a/app/config.py b/app/config.py index 56878a6..0705156 100644 --- a/app/config.py +++ b/app/config.py @@ -12,5 +12,6 @@ class Config: db_name = os.environ.get("DB_NAME") db_port = int(os.environ.get("DB_PORT")) SQLALCHEMY_DATABASE_URI = os.environ.get("SQLALCHEMY_DATABASE_URI") - SQLALCHEMY_TRACK_MODIFICATIONS = bool(os.environ.get("SQLALCHEMY_TRACK_MODIFICATIONS")) + SQLALCHEMY_TRACK_MODIFICATIONS\ + = bool(os.environ.get("SQLALCHEMY_TRACK_MODIFICATIONS")) SQLALCHEMY_ECHO = bool(os.environ.get("SQLALCHEMY_ECHO")) diff --git a/app/database/models.py b/app/database/models.py index cafaeae..3d6aaa2 100644 --- a/app/database/models.py +++ b/app/database/models.py @@ -1,4 +1,4 @@ -from sqlalchemy import Column, Text +from sqlalchemy import Text from sqlalchemy.dialects.mysql import TINYINT, INTEGER from sqlalchemy.orm import validates diff --git a/app/views.py b/app/views.py index 05e51c7..c075e49 100644 --- a/app/views.py +++ b/app/views.py @@ -3,7 +3,8 @@ from flask import request, render_template, session, Blueprint -from app import Library, db +from app import db +from app.database.models import Library views = Blueprint('views', __name__) From b37bd17464be6f870b93669cb8f5cc5c44026de3 Mon Sep 17 00:00:00 2001 From: kujyp Date: Wed, 11 Apr 2018 17:53:47 +0900 Subject: [PATCH 7/7] Fix lint error --- app/v1/controllers/__init__.py | 1 - 1 file changed, 1 deletion(-) diff --git a/app/v1/controllers/__init__.py b/app/v1/controllers/__init__.py index a60a0b1..16da706 100644 --- a/app/v1/controllers/__init__.py +++ b/app/v1/controllers/__init__.py @@ -3,7 +3,6 @@ from flask_restful import reqparse from app.config import Config -from app.database import db from app.utils.errors import DataNotFoundError, WrongSecretkeyError