Создание темплейта Flask-Vue.js-Heroku (2 часть)

Продолжение первой части

Попробуем навести феншуй в нашем простейшем приложении. Создадим отдельное приложение flask, уберем хардкод, установим базу данных и орм и так далее.

Flask

package app

Перемести логику фласк из корня в пакет. Подробнее про организацию проекта написано Organizing your project . Для этого пересестим файл app.py в директорию app и переименуем его в __init__.py - он будет выполняться при инициализации пакета app. А в корень поместим run.py:

from app import app
app.run(port=5000)
handling env

Удобно управлять переменнымы окружения, записав их в файл .env в корне и установив пакет python-dotenv. При запуске через flask run переменные из этого файла будут автоматически загружены. При запуске же на продакшене они загружены не будут. Так можно разделить dev и prod среды исполнения. Подробнее.

В коде можно так обртиться к переменны:

s = os.getenv("SECRET_KEY")

В корне проекта разместим два файла - .env и .flaskenv. Первый файл у нас с секретными настройками, а второй дублирует эти настройки с фейковыми данными, в качестве примера, и находится в репозитории. Настройки читаются сначала из .flaskenv, затем перезаписываются имеющимися в .env.

config.py

Добавим в корень сайта файл config.py с объектом конфигурации.

import os
from app import app

class Config(object):
    APP_DIR = os.path.dirname(__file__)
    ROOT_DIR = os.path.dirname(APP_DIR)
    DIST_DIR = os.path.join(ROOT_DIR, 'dist')

    FLASK_ENV = os.getenv('FLASK_ENV')
    FLASK_ADMIN_SWATCH = os.getenv('FLASK_ADMIN_SWATCH')
    SECRET_KEY = os.getenv('SECRET_KEY')
    DATABASE_URL = os.getenv('DATABASE_URL')

    if not os.path.exists(DIST_DIR):
        raise Exception(
            f'DIST_DIR not found: {DIST_DIR}')

app.config.from_object('app.config.Config')

Далее этот объект загрущим в app/__init.py командой

from .config import Config
peewee
$ pipenv install peewee

создаем models.py. В нем описывам модели и подключение к БД. В моем конфиге при работе в dev окружении мы работаем с SQLite, а в prod - подключаемся к Postgres. Это реализовано через проксирование БД, подробно тут: Dynamically defining a database В функции initialize_db мы создаем таблицы в базе. Под heroku все это требует допиливания.

import os
from peewee import *

FLASK_ENV = os.getenv("FLASK_ENV")


database_proxy = DatabaseProxy()


class Effect(Model):
    name = CharField(unique=True)

    def __str__(self):
        return self.name

    class Meta:
        database = database_proxy  # This model uses the "people.db" database.


class Ingredient(Model):
    name = CharField(unique=True)
    effect1 = ForeignKeyField(Effect)
    effect2 = ForeignKeyField(Effect)
    effect3 = ForeignKeyField(Effect)
    effect4 = ForeignKeyField(Effect)

    class Meta:
        database = database_proxy  # This model uses the "people.db" database.


# Based on configuration, use a different database.
if FLASK_ENV == 'development':
    database = SqliteDatabase('base.db')
elif FLASK_ENV == 'production':
    database = PostgresqlDatabase('my_app', user='postgres', password='secret', host='10.1.0.9', port=5432)
else:
    raise Exception('No FLASK_ENV environment during db init.')


# Configure our proxy to use the db we specified in config.
database_proxy.initialize(database)


def initialize_db():
    database_proxy.connect()
    database_proxy.create_tables([Effect], safe=True)
    database_proxy.create_tables([Ingredient], safe=True)
    database_proxy.close()
initialize_db()

или можно использовать враппер из библиотеки playhouse, что дает еще более элегантный код. В config.py добавим конфигурацию БД:

class Config(object):
    if FLASK_ENV == 'development':
        app.config['DATABASE'] = 'sqlite:///base.db'
    # else using heroku DATABASE_URL

и упростим models.py:

from app import app
from peewee import *
from playhouse.flask_utils import FlaskDB

db_wrapper = FlaskDB(app)

class Effect(db_wrapper.Model):
    ...

class Ingredient(db_wrapper.Model):
    ...

def initialize_db():
    with db_wrapper.database as db:
        db.create_tables([Effect, Ingredient], safe=True)
initialize_db()

FlaskDB работает следующим образом - он создает соеденение с базой данных, используя app.config['DATABASE'], а если не находит его, то тогда использует app.config['DATABASE_URL']. Таким образом, в dev мы определяем 'DATABASE' в config.py, а в prod на heroku у нас автоматически существует DATABASE_URL.

abstraction

Добавим немного абстракции для феншуя. Отдавать входную точку (/) будем так:

def index():
    dist_dir = current_app.config['DIST_DIR']
    entry = os.path.join(dist_dir, 'index.html')
    return send_file(entry)
admin
pipenv install Flask-admin
pipenv install wtf-peewee - необходимо для Flask-admin

Добавьте в init.py

from flask_admin import Admin
from flask_admin.contrib.peewee import ModelView
from .models import Effect, Ingredient

# admin -----------------------
app.config['FLASK_ADMIN_SWATCH'] = os.getenv("FLASK_ADMIN_SWATCH")
admin = Admin(app, name='Skyrim Alchemy', template_mode='bootstrap3')
admin.add_view(ModelView(Effect))
admin.add_view(ModelView(Ingredient))

Чтобы использовать тему, укажите ее в .flaskenv:

FLASK_ADMIN_SWATCH = darkly
bootstrap

Для использования компонентов бутстрап достаточно установить пакет плагин и классы бутстрап начнут отображаться:

$ vue add bootstrap-vue
heroku

Подключаем репозиторий к heroku, тут подробно останавливаться не буду, нам главное, чтобы по команде git push heroku запускался деплой.

Убеждаемся, что мы установили buildpack для питона и ноды (первый питон, это важно!):

$ heroku buildpacks:set heroku/python
$ heroku buildpacks:add --index 1 heroku/nodejs

Устанавливаем аддон для постгреса:

$ heroku addons:create heroku-postgresql:hobby-dev --app:<our-app-name>

Вносим изменения в models.py, чтобы мы в окружении dev подключались к heroku-postgresql. Настройки для подключения хранятся на heroku в переменной DATABASE_URL, она создается автоматически после запуска предыдущей команды.

....
elif FLASK_ENV == 'production':
    # connect to heroku POSTGRES
    DATABASE_URL = os.environ['DATABASE_URL']
    database = connect(DATABASE_URL)

Не забываем создать на heroku нужные переменные, типа SECRET_KEY.

Comments !

blogroll

social