├── .gitignore ├── README.md ├── instance └── config.py ├── manage.py ├── myapp ├── __init__.py ├── database.py ├── default_config.py ├── home │ ├── __init__.py │ └── views.py ├── models.py ├── static │ ├── css │ │ └── base.css │ └── js │ │ └── base.js ├── templates │ ├── 404.html │ ├── base.html │ ├── forms.html │ ├── home │ │ └── home.html │ └── users │ │ └── login.html └── users │ ├── __init__.py │ ├── forms.py │ └── views.py ├── runserver.py └── shell.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.py[co] 2 | *.project 3 | *.pydevproject 4 | # Packages 5 | *.egg 6 | *.egg-info 7 | dist 8 | build 9 | eggs 10 | parts 11 | bin 12 | var 13 | sdist 14 | develop-eggs 15 | .installed.cfg 16 | 17 | # Installer logs 18 | pip-log.txt 19 | 20 | # Unit test / coverage reports 21 | .coverage 22 | .tox 23 | 24 | #Translations 25 | *.mo 26 | 27 | #Mr Developer 28 | .mr.developer.cfg 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | flask-app-structure 2 | =================== 3 | 4 | Flask-App-Structure 5 | 6 | This is the basic flask app structure for larger applications 7 | which I use in my projects. 8 | This structure follows lots of advice from here 9 | https://github.com/mitsuhiko/flask/wiki/Large-app-how-to 10 | and here 11 | http://flask.pocoo.org/snippets/22/ 12 | 13 | Dependencies of the code: 14 | Sqlalchemy 15 | Psycopg2 16 | Flask-WTF 17 | Flask-Bcrypt 18 | Flask-Manager 19 | Flask-Login 20 | -------------------------------------------------------------------------------- /instance/config.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codecool/flask-app-structure/0e4273562699cad02fb317f35ce36f083d7cb0f2/instance/config.py -------------------------------------------------------------------------------- /manage.py: -------------------------------------------------------------------------------- 1 | from flask.ext.script import Manager 2 | 3 | from myapp import create_app 4 | from myapp.database import init_db, drop_db 5 | 6 | manager = Manager(create_app) 7 | 8 | 9 | @manager.command 10 | def initdb(): 11 | print 'Initialising database' 12 | init_db() 13 | print 'Database initialized' 14 | 15 | 16 | @manager.command 17 | def dropdb(): 18 | print 'Dropping database' 19 | drop_db() 20 | print 'Database dropped' 21 | 22 | -------------------------------------------------------------------------------- /myapp/__init__.py: -------------------------------------------------------------------------------- 1 | import os 2 | import logging 3 | 4 | from flask import Flask, render_template, abort 5 | from flask.ext.login import LoginManager 6 | from flask.ext.bcrypt import Bcrypt 7 | from flask.ext.openid import OpenID 8 | from flask.ext.script import Manager 9 | from flask.ext.assets import Environment, Bundle 10 | 11 | from .database import init_engine, init_db, db_session 12 | from .models import User, bcrypt 13 | #from .utils import q 14 | 15 | from .home.views import mod as main_blueprint 16 | from .users.views import mod as user_blueprint 17 | 18 | 19 | login_manager = LoginManager() 20 | assets = Environment() 21 | 22 | 23 | 24 | def create_app(db_uri='any'): 25 | 26 | app = Flask(__name__) 27 | app.config.from_object('myapp.default_config') 28 | app.config.from_pyfile(os.path.join(app.instance_path, 'config.py')) 29 | 30 | if db_uri == 'Test': 31 | init_engine(app.config['TEST_DATABASE_URI']) 32 | else: 33 | init_engine(app.config['DATABASE_URI']) 34 | 35 | #Register Blueprints 36 | app.register_blueprint(main_blueprint) 37 | app.register_blueprint(user_blueprint, url_prefix="/users") 38 | 39 | #App logging 40 | # app.logger.setLevel(logging.WARNING) 41 | # logger_handler = logging.FileHandler(os.path.join(app.config['LOG_LOCATION'], 42 | # 'app_errors.log')) 43 | # formatter = logging.Formatter('%(asctime)s %(levelname)s - %(message)s' 44 | # ' [in %(pathname)s:%(lineno)d]') 45 | # logger_handler.setFormatter(formatter) 46 | # app.logger.addHandler(logger_handler) 47 | 48 | 49 | @app.teardown_request 50 | def shutdown_session(exception=None): 51 | db_session.remove() 52 | 53 | @app.errorhandler(404) 54 | def page_not_found(e): 55 | return render_template('404.html') 56 | 57 | @app.errorhandler(500) 58 | def internal_error(exception): 59 | app.logger.exception(exception) 60 | return "Some Internal error has taken place." 61 | 62 | 63 | #Extensions registration 64 | bcrypt.init_app(app) 65 | login_manager.setup_app(app) 66 | login_manager.login_view = 'users.login' 67 | assets.init_app(app) 68 | 69 | #CSS assets registration 70 | css = Bundle('base.css') 71 | assets.register('css_all', css) 72 | 73 | #JS assets registration 74 | js = Bundle('base.js') 75 | assets.register('js_all', js) 76 | 77 | 78 | return app 79 | 80 | 81 | @login_manager.user_loader 82 | def load_user(userid): 83 | return User.query.get(userid) 84 | -------------------------------------------------------------------------------- /myapp/database.py: -------------------------------------------------------------------------------- 1 | from sqlalchemy import create_engine 2 | from sqlalchemy.orm import scoped_session, sessionmaker, create_session 3 | from sqlalchemy.ext.declarative import declarative_base 4 | from sqlalchemy import engine 5 | from sqlalchemy.schema import ( 6 | MetaData, 7 | Table, 8 | DropTable, 9 | ForeignKeyConstraint, 10 | DropConstraint, 11 | ) 12 | 13 | 14 | 15 | 16 | db_engine = None 17 | 18 | 19 | def init_engine(uri, **kwargs): 20 | global db_engine 21 | db_engine = create_engine(uri, **kwargs) 22 | return db_engine 23 | 24 | 25 | db_session = scoped_session(lambda: create_session(bind=db_engine, 26 | autoflush=True, 27 | autocommit=False, 28 | expire_on_commit=True)) 29 | 30 | 31 | Base = declarative_base() 32 | 33 | Base.query = db_session.query_property() 34 | 35 | 36 | 37 | 38 | def init_db(): 39 | import myapp.models 40 | Base.metadata.create_all(db_engine) 41 | 42 | 43 | def drop_db(): 44 | """It is a workaround for dropping all tables in sqlalchemy. 45 | """ 46 | if db_engine is None: 47 | raise Exception 48 | conn = db_engine.connect() 49 | trans = conn.begin() 50 | inspector = engine.reflection.Inspector.from_engine(db_engine) 51 | # gather all data first before dropping anything. 52 | # some DBs lock after things have been dropped in 53 | # a transaction. 54 | 55 | metadata = MetaData() 56 | 57 | tbs = [] 58 | all_fks = [] 59 | 60 | for table_name in inspector.get_table_names(): 61 | fks = [] 62 | 63 | for fk in inspector.get_foreign_keys(table_name): 64 | if not fk['name']: 65 | continue 66 | fks.append(ForeignKeyConstraint((), (), name=fk['name'])) 67 | t = Table(table_name, metadata, *fks) 68 | tbs.append(t) 69 | all_fks.extend(fks) 70 | 71 | for fkc in all_fks: 72 | conn.execute(DropConstraint(fkc)) 73 | 74 | for table in tbs: 75 | conn.execute(DropTable(table)) 76 | 77 | trans.commit() 78 | -------------------------------------------------------------------------------- /myapp/default_config.py: -------------------------------------------------------------------------------- 1 | import os 2 | 3 | 4 | DEBUG = True 5 | CSRF_TOKEN = True 6 | SECRET_KEY = 'mysecretkey' 7 | DB_USER = '' 8 | DB_PASSWORD = '' 9 | DB_NAME = '' 10 | DB_PORT = 5432 11 | 12 | DATABASE_URI_FMT = "postgresql+psycopg2://%(db_user)s:%(db_password)s@localhost:%(db_port)s/%(db_name)s" 13 | DATABASE_URI = DATABASE_URI_FMT % {'db_user': DB_USER, 14 | 'db_password': DB_PASSWORD, 15 | 'db_port': DB_PORT, 16 | 'db_name': DB_NAME} 17 | 18 | LOG_LOCATION = '' -------------------------------------------------------------------------------- /myapp/home/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/codecool/flask-app-structure/0e4273562699cad02fb317f35ce36f083d7cb0f2/myapp/home/__init__.py -------------------------------------------------------------------------------- /myapp/home/views.py: -------------------------------------------------------------------------------- 1 | """Create home blueprint and all the views in it here. 2 | """ 3 | 4 | from flask import Blueprint, render_template 5 | 6 | mod = Blueprint('home', __name__, 7 | template_folder='templates', 8 | static_folder='static') 9 | 10 | 11 | @mod.route('/') 12 | def home(): 13 | return render_template('home/home.html') 14 | -------------------------------------------------------------------------------- /myapp/models.py: -------------------------------------------------------------------------------- 1 | from sqlalchemy import Column, Integer, ForeignKey, String, DateTime, text 2 | from sqlalchemy.ext.hybrid import hybrid_property 3 | from flask.ext.bcrypt import Bcrypt 4 | from flask.ext.login import UserMixin 5 | 6 | from .database import Base 7 | 8 | bcrypt = Bcrypt() 9 | 10 | class User(Base, UserMixin): 11 | __tablename__ = 'users' 12 | 13 | id = Column(Integer, primary_key=True) 14 | username = Column(String(50), nullable=False) 15 | email = Column(String(100), unique=True) 16 | _password = Column('password', String(120), nullable=False) 17 | creation_date = Column(DateTime, default=text("current_timestamp")) 18 | 19 | def __init__(self, username, email, password): 20 | self.username = username 21 | self.email = email 22 | self.password = password 23 | 24 | @hybrid_property 25 | def password(self): 26 | return self._password 27 | 28 | @password.setter 29 | def password(self, value): 30 | self._password = bcrypt.generate_password_hash(value) 31 | 32 | def get_id(self): 33 | return unicode(self.id) 34 | 35 | def match_password(self, value): 36 | return bcrypt.check_password_hash(self._password, value) 37 | 38 | def __repr__(self): 39 | return u"User<%s>" % self.username 40 | -------------------------------------------------------------------------------- /myapp/static/css/base.css: -------------------------------------------------------------------------------- 1 | body { 2 | text-align: center; 3 | } 4 | -------------------------------------------------------------------------------- /myapp/static/js/base.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @author Manav Goel 3 | */ 4 | -------------------------------------------------------------------------------- /myapp/templates/404.html: -------------------------------------------------------------------------------- 1 | 3 | 4 |
5 |