Dash App Using Flask Factory Pattern and Blueprint - 2
Page content
강의 홍보
- 취준생을 위한 강의를 제작하였습니다.
- 본 블로그를 통해서 강의를 수강하신 분은 게시글 제목과 링크를 수강하여 인프런 메시지를 통해 보내주시기를 바랍니다.
스타벅스 아이스 아메리카노를 선물
로 보내드리겠습니다.
- [비전공자 대환영] 제로베이스도 쉽게 입문하는 파이썬 데이터 분석 - 캐글입문기
개요
- 기존 Flask-Dash-Heroku 연동 예제를 업그레이드 한다.
- Flask Factory Application의 기본 개념 및 Blueprint의 기본 개념을 이해한다.
- Dash App을 Flask Factory Application에 맞추어 가공 한다.
리뷰
- 기존 필자가 작성해두었던 Flask-Dash-Heroku App을 리뷰한다.
미리보기
- 다음과 같이 메뉴가 있도록 코드를 작성할 예정이다.
Flask Factory Pattern
- 구체적인 개념은 점프 투 플라스크를 참조한다.
- 간단한 앱을 구현할 때는 큰 문제가 되지 않는다. 그러나, 프로젝트 규모가 커지면 순환 참조(circular import) 오류가 생길 수 있다.
- 따라서, Flask 공식 홈페이지에서는 Application Factory 사용을 권한다.
Flask Blueprint
- Blueprint의 기본적인 개념은 공식 홈페이지에 잘 나와 있다.
- 블루프린트를 사용하면 보다 클래스 즉, 같은 종류의 페이지를 묶어서 처리할 수 있도록 도와준다.
- 예) /book/write/와 /book/read를 쓸 때 /book url로 묶을 수 있다.
배포
- Heroku에 배포하는 방법은 Flask-Dash-Heroku 연동 에서 확인하도록 한다.
Hello World 출력
- Blueprint와 Factory Method Pattern 방식으로 Hello World를 출력한다.
- 먼저 가상환경을 만든 후 접속한다.
virtualenv venv # 가상환경 생성
source venv/Scripts/activate # 가상환경 접속
- 필수 라이브러리를 설치한다.
pip install dash plotly Flask pandas gunicorn psycopg2-binary SQLAlchemy Flask-SQLAlchemy
- 프로젝트 폴더 (예:
dash-flask-factory-blueprint
)에서 app 폴더를 생성한다.__init__.py
파일을 만들고 다음과 같이 작성한다.
# app/__init__.py
import os
from flask import Flask
def create_app(test_config=None):
app = Flask(__name__, instance_relative_config=True)
app.config.from_mapping(
SECRET_KEY = 'development'
)
if test_config is None:
app.config.from_pyfile('config.py', silent=True)
else:
app.config.from_mapping(test_config)
try:
os.makedirs(app.instance_path)
except OSError:
pass
return app
- 이제 blueprint 코드를 작성하여 Hello World를 출력하도록 한다.
# app/main.py
from flask import Blueprint, render_template
bp = Blueprint('main', __name__, url_prefix='/')
@bp.route('/')
def main():
return "Hello World!"
- 이렇게 생성이 되었다면,
__init__.py
에서 해당main.py
의blueprint
를 등록해야 한다.
# app/__init__.py
import os
from flask import Flask
def create_app(test_config=None):
app = Flask(__name__, instance_relative_config=True)
app.config.from_mapping(
SECRET_KEY = 'development'
)
.
.
from . import main
app.register_blueprint(main.bp)
return app
- 이번에는
[wsgi.py](http://wsgi.py)
파일을 생성하고 아래와 같이 코드를 추가한다.
from app import create_app
app = create_app()
if __name__ == "__main__":
app.run(host='0.0.0.0', debug=True)
- 이제 서버 테스트를 해본다.
- 정상적으로 나오는지 확인한다.
flask run
* Debug mode: off
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
* Running on http://127.0.0.1:5000
Press CTRL+C to quit
templates 폴더
- 이번에는 app폴더 내에 templates 폴더를 만든다.
- bootstrap 5.2.0 버전을 활용하여 base.html 및 main.html 파일을 생성하여 Manu Bar와 Hello World를 출력하는 코드를 작성한다.
- 이 때 확장자명 html 파일은 모두 jinja2로 저장하는 것에 주의한다.
(1) header.jinja2 파일 작성
- 우선 아래와 같이 파일을 작성한다.
- 파일경로 : app/templates/header.jinja2
<ul class="nav">
<li class="nav-item">
<a class="nav-link active" aria-current="page" href="/main">Main</a>
</li>
<li class="nav-item">
<a class="nav-link active" href="/dashapp1">Dashapp1</a>
</li>
</ul>
(2) base.jinja2 파일 작성
- 이번에는 기 작성된 header.jinja2를 불러오도록 한다.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Bootstrap demo</title>
<link
href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0/dist/css/bootstrap.min.css"
rel="stylesheet"
integrity="sha384-gH2yIJqKdNHPEq0n4Mqa/HGKIhSkIHeL5AyhkYV8i59U5AR6csBvApHHNl/vI1Bx"
crossorigin="anonymous"
/>
<script
src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0/dist/js/bootstrap.bundle.min.js"
integrity="sha384-A3rJD856KowSb7dwlZdYEkO39Gagi7vIsF0jrRAoQmDKKtQBHUuLZ9AsSv4jD4Xa"
crossorigin="anonymous"
></script>
</head>
<body>
{% include 'header.jinja2' %}
<br />
<div class="container">{% block content %} {% endblock %}</div>
</body>
</html>
main 페이지 작성
- 우선 app/main 폴더를 생성한다.
- main 폴더 내부에서 templates 폴더와 main.py를 생성한다.
(1) main.jinja2
- templates 폴더 내부에서
main.jinja2
파일을 아래와 같이 작성한다.
{% extends "base.jinja2" %}
{% block content %}
<h1>Hello World</h1>
{% endblock %}
(2) main.py
- 이제, main.jinja2 파일을 불러와서 hello world를 출력한다.
# app/main/main.py
from flask import Blueprint, render_template
bp = Blueprint('main', __name__, template_folder = "templates", url_prefix='/')
@bp.route('/')
def main():
return render_template("main.jinja2", title = 'main page')
- 이제
app/__init__.py
파일에서 main 코드의 경로를 일부 수정한다.
# app/__init__.py
import os
from flask import Flask
def create_app(test_config=None):
app = Flask(__name__, instance_relative_config=True)
app.config.from_mapping(
SECRET_KEY = 'development'
)
.
.
from .main import main
app.register_blueprint(main.bp)
return app
- 이제 다시 flask run을 실행하고 달라진 웹페이지를 확인한다.
- 경로를 잠깐 수정하도록 한다.
- 현재 상태에서 Main, Dashapp1 메뉴를 클릭하면 모두 internal error가 뜬다.
- 따라서, 기본 html 페이지를 별도로 만들고 127.0.0.1:5000/main 이 나오면 다른 페이지가 나오도록 일부 코드를 변경한다.
- 우선
app/__init__.py
파일을 아래와 같이 변경한다.
# app/__init__.py
import os
from flask import Flask, render_template
def create_app(test_config=None):
app = Flask(__name__, instance_relative_config=True)
app.config.from_mapping(
SECRET_KEY = 'development'
)
.
.
@app.route('/')
def main():
return render_template("index.jinja2", title = 'index page')
from .main import main
app.register_blueprint(main.bp)
return app
- app/templates 폴더 내에
index.jinja2
파일을 작성하고 아래와 같이 작성한다.
{% extends "base.jinja2" %}
{% block content %}
<h1>Hello This is 기본 페이지</h1>
{% endblock %}
- 이번에는
app/main/main.py
를 수정한다.
from flask import Blueprint, render_template
bp = Blueprint('main', __name__, template_folder = "templates", url_prefix='/main')
@bp.route('/', methods=['GET'])
def main():
return render_template("main.jinja2", title = 'main page')
- 이제 다시 flask run을 실행하면 아래와 같이 정상적으로 나온다.
- 막상 만들고 나니 Home 메뉴가 있으면 좋겠다는 생각이 든다.
header.jinja2
의 메뉴바를 수정하면 되는 부분이기 때문에 각자 수정하도록 한다.
Dashapp1 페이지 만들기
- main 폴더를 그대로 복제한 후, 폴더명 및 파일명을 모두 dashapp1으로 변경한다.
- 방법은 main 폴더에서 했던 방식 그대로다.
- 이제,
app/__init__.py
파일에서 아래와 같이 파일명을 추가한다.
# app/__init__.py
import os
from flask import Flask, render_template
def create_app(test_config=None):
app = Flask(__name__, instance_relative_config=True)
app.config.from_mapping(
SECRET_KEY = 'development'
)
.
.
@app.route('/')
def main():
return render_template("index.jinja2", title = 'index page')
from .main import main
from .dashapp1 import dashapp1
app.register_blueprint(main.bp)
app.register_blueprint(dashapp1.bp)
return app
- 정상적으로 실행이 된 것을 확인할 수 있다.