Skip to content

Commit

Permalink
initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
pikulet committed Dec 4, 2020
1 parent c93a719 commit f036d6d
Show file tree
Hide file tree
Showing 24 changed files with 357 additions and 1 deletion.
1 change: 1 addition & 0 deletions Procfile
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
web: flask db upgrade; flask translate compile; gunicorn microblog:app
43 changes: 42 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,43 @@
# fast-flask
template flask app (with postgres + heroku deploy)

A flask starter template with postgreSQL database and deployed to Heroku.

## Getting Started (Development)

Clone the repository.

`git clone https://github.com/pikulet/fast-flask-example`
`cd fast-flask-example`

Install virtualenv.

`pip3 install virtualenv`

Activate the virtual environment.

`source venv\bin\activate`

## Deployment to Heroku

Login to Heroku.

`heroku login`

Create Heroku app.

`heroku apps:create <heroku_app_name>`

Create a PostgreSQL database on Heroku. We use the free plan.

`heroku addson:add heroku-postgresql:hobby-dev`

Configure environment variables for Heroku.

`heroku config:set FLASK_APP=fastflask.py`

Push to Heroku remote.

`git push heroku master`



Binary file added __pycache__/config.cpython-36.pyc
Binary file not shown.
Binary file added __pycache__/fastflask.cpython-36.pyc
Binary file not shown.
17 changes: 17 additions & 0 deletions app/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
from flask import Flask
from flask_bootstrap import Bootstrap
from flask_sqlalchemy import SQLAlchemy
from config import DevelopmentConfig, DeployConfig

def create_app():
app = Flask(__name__)
Bootstrap(app)

app.config.from_object(DevelopmentConfig)
#app.config.from_object(DeployConfig)
return app

app = create_app()
db = SQLAlchemy(app)

from app import routes
Binary file added app/__pycache__/__init__.cpython-36.pyc
Binary file not shown.
Binary file added app/__pycache__/forms.cpython-36.pyc
Binary file not shown.
Binary file added app/__pycache__/models.cpython-36.pyc
Binary file not shown.
Binary file added app/__pycache__/routes.cpython-36.pyc
Binary file not shown.
7 changes: 7 additions & 0 deletions app/forms.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
from flask_wtf import FlaskForm
from wtforms import StringField, SubmitField
from wtforms.validators import DataRequired

class InputForm(FlaskForm):
text = StringField('Enter text here...', validators=[DataRequired()])
submit = SubmitField('Confirm')
19 changes: 19 additions & 0 deletions app/models.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
from app import db

class Text(db.Model):

__tablename__ = 'fastflask'

text_id = db.Column(db.Integer, primary_key=True)
text_value = db.Column(db.String(255), unique=True, nullable=False)

def __init__(self, value):
self.text_value = value

def __repr__(self):
return self.value

def serialise(self):
return {
'value' : self.value
}
31 changes: 31 additions & 0 deletions app/routes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
from flask import render_template, redirect, url_for, request
from app import app, db
from app.forms import InputForm
from app.models import Text

@app.route('/')
def index():
return render_template('index.html', title='Home')

@app.route('/input', methods=['GET', 'POST'])
def input():
form = InputForm()
if form.validate_on_submit():
return redirect(url_for('process', text=form.text.data))
return render_template('input.html',
title='Input',
form=form)

@app.route('/process', methods=['GET'])
def process():
value = request.args['text']

existing_entry = Text.query.filter_by(text_value=value).first()
if existing_entry is None:
new_db_entry = Text(value)
db.session.add(new_db_entry)
db.session.commit()

return render_template('index.html',
title='Input Received',
text=value)
10 changes: 10 additions & 0 deletions app/templates/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{% extends "bootstrap/base.html" %}
{% block title %} {{ title }} {% endblock %}

{% block content %}
{% if text %}
<h1> {{ text }} </h1>
{% else %}
<h1> Welcome! </h1>
{% endif %}
{% endblock %}
6 changes: 6 additions & 0 deletions app/templates/input.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{% import "bootstrap/wtf.html" as wtf %}
{% extends "index.html" %}

{% block content %}
{{ wtf.quick_form(form) }}
{% endblock %}
21 changes: 21 additions & 0 deletions config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import os

class DevelopmentConfig:
SECRET_KEY = os.environ.get('SECRET_KEY') or 'csrf key'

POSTGRES_URL = os.environ.get("POSTGRES_URL")
POSTGRES_USER = os.environ.get("POSTGRES_USER")
POSTGRES_PW = os.environ.get("POSTGRES_PW")
POSTGRES_DB = os.environ.get("POSTGRES_DB")

DB_URL = 'postgresql+psycopg2://{user}:{pw}@{url}/{db}'.format(
user=POSTGRES_USER,pw=POSTGRES_PW,url=POSTGRES_URL,db=POSTGRES_DB)
SQLALCHEMY_DATABASE_URI = DB_URL
SQLALCHEMY_TRACK_MODIFICATIONS = False

class DeployConfig:
SECRET_KEY = os.environ.get('SECRET_KEY') or 'csrf key'
SQLALCHEMY_DATABASE_URI = os.environ.get("DATABASE_URL")
SQLALCHEMY_TRACK_MODIFICATIONS = False


1 change: 1 addition & 0 deletions fastflask.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
from app import app
12 changes: 12 additions & 0 deletions manage.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
from flask_script import Manager
from flask_migrate import Migrate, MigrateCommand

from app import app, db

migrate = Migrate(app, db)
manager = Manager(app)

manager.add_command('db', MigrateCommand)

if __name__ == '__main__':
manager.run()
1 change: 1 addition & 0 deletions migrations/README
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Generic single-database configuration.
Binary file added migrations/__pycache__/env.cpython-36.pyc
Binary file not shown.
45 changes: 45 additions & 0 deletions migrations/alembic.ini
Original file line number Diff line number Diff line change
@@ -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
96 changes: 96 additions & 0 deletions migrations/env.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
from __future__ import with_statement

import logging
from logging.config import fileConfig

from sqlalchemy import engine_from_config
from sqlalchemy import pool

from alembic import context

# 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',
str(current_app.extensions['migrate'].db.engine.url).replace('%', '%%'))
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, target_metadata=target_metadata, literal_binds=True
)

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.')

connectable = engine_from_config(
config.get_section(config.config_ini_section),
prefix='sqlalchemy.',
poolclass=pool.NullPool,
)

with connectable.connect() as connection:
context.configure(
connection=connection,
target_metadata=target_metadata,
process_revision_directives=process_revision_directives,
**current_app.extensions['migrate'].configure_args
)

with context.begin_transaction():
context.run_migrations()


if context.is_offline_mode():
run_migrations_offline()
else:
run_migrations_online()
24 changes: 24 additions & 0 deletions migrations/script.py.mako
Original file line number Diff line number Diff line change
@@ -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"}
20 changes: 20 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
alembic==1.4.3
click==7.1.2
dominate==2.6.0
Flask==1.1.2
Flask-Bootstrap==3.3.7.1
Flask-Migrate==2.5.3
Flask-SQLAlchemy==2.4.4
Flask-WTF==0.14.3
itsdangerous==1.1.0
Jinja2==2.11.2
Mako==1.1.3
MarkupSafe==1.1.1
python-dateutil==2.8.1
python-dotenv==0.15.0
python-editor==1.0.4
six==1.15.0
SQLAlchemy==1.3.20
visitor==0.1.3
Werkzeug==1.0.1
WTForms==2.3.3
4 changes: 4 additions & 0 deletions schema.psql
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
CREATE TABLE fastflask (
text_id serial PRIMARY KEY,
text_value VARCHAR(255) UNIQUE NOT NULL
);

0 comments on commit f036d6d

Please sign in to comment.