[Python] Flask

Flask

  • Web APP Framework
  • Django에 비해 가벼움

Flask란?

  • Flask Homepage
  • Micro Web Framework
  • Web APP을 개발할 수 있도록 Web 서비스(Web Service)나 Web API(Web API) 등을 제공하고 Web 개발과 배포를 할 수 있는 특정 방법 제공
  • Jinja, Werkzeug, Flask-SQLAlchemy 등 다양한 패키지와 라이브러리가 존재

Flask 시작

설치

1
$ pip install flask

폴더 생성

1
2
flask_app
└── __init__.py

APP 생성

1
2
3
4
5
6
7
8
9
10
# __init__.py

from flask import Flask

app = Flask(__name__)

@app.route('/')
def index():
    return 'Hello World!'

  • CLI 실행
1
2
$ FLASK_APP=flask_app flask run

Route 기능

HTTP Request method

  • 기본적인 Flask route decorator를 사용하면 GET, HEAD, OPTIONS method를 허용한다.
  • 이외의 method를 통과하기 위해서는 methods 인수를 추가
1
2
3
@app.route('/', methods=['POST', 'GET'])
def index():
    ...
  • GET과 POST라는 method가 통과되지만 HEAD와 OPTIONS라는 method는 이제 사용이 불가능하다.

세부 엔드 포인트

1
2
3
4
@app.route('/user/', defaults={'user_id':0})
@app.route('/user/<user_id>')
def user_index(user_id):
    return f'Your ID is : {user_id}'

스크린샷 2021-09-29 14 00 33

Blueprint

  • Route를 기능별로 나눠 Blueprint 기능을 사용한다.
  • 폴더 구조
1
2
3
4
flask_app
├── __init__.py
└── routes
    └── main_routes.py
1
2
3
4
5
6
7
8
# main_route.py
from flask import Blueprint

bp = Blueprint('main', __name__, url_prefix='/main')

@bp.route('/')
def index():
    return 'Hello main!'
  • ‘main’ : Blueprint의 명칭
  • name : Blueprint의 import 이름
  • url_prefix=’/main’ : URL 접두어 설정 (해당 Blueprint의 Route는 URL 앞에 ‘/main’이 자동으로 붙는다.)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# __init__.py 파일 수정
from flask import Flask
from flask_app.routes.main_route import bp as main_bp # 추가

app = Flask(__name__)
app.register_blueprint(main_bp) # 추가

@app.route('/')
def index():
    return 'Hello World!'

@app.route('/user/', defaults={'user_id':0})
@app.route('/user/<user_id>')
def user_index(user_id):
    return f'Your ID is : {user_id}'
  • circular import 를 피하기 위해서 Flask 에서는 APP 팩토리 패턴을 추천 스크린샷 2021-09-29 13 59 42

APP Factory

  • circular import 를 피하기 위해서 Flask 에서는 APP 팩토리 패턴을 추천
  • 말그대로 ‘공장’을 세우는 건데, 글로벌한 context에서 app을 선언하고 사용하게 되면 여러개의 APP을 동시에 사용하거나 파일의 일부분만 사용하게 될때도 문제가 발생할 수 있다.
  • 이런 잠재적인 문제점을 피하기 위해 함수를 따로 만든다.
  • 기본적인 패턴
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from flask import Flask

def create_app():
    app = Flask(__name__)

    from yourapplication.views.admin import admin
    from yourapplication.views.frontend import frontend
    app.register_blueprint(admin)
    app.register_blueprint(frontend)

    return app

if __name__ == "__main__":
  app = create_app()
  app.run()

Jinja

Flask의 Html 렌더링

  • template이라는 폴더 생성 후 index.html을 만든다.
1
2
3
4
5
6
7
8
9
10
<html>
  <head>
    <title>
      New HTML Page
    </title>
  </head>
  <body>
    <h1>I am in templates folder</h1>
  </body>
</html>
  • 이러한 형태로 index.html을 보여줄 수 있다.
1
2
3
4
5
6
7
from flask import Flask, render_template

app = Flask(__name__)

@app.route('/')
def index():
  return render_template('index.html')

Jinja란?

  • Web Template Engine : 맞춤형 Web 페이지를 자동으로 생산할 수 있도록 도와준다.

Jinja 표현

  • 중괄호 ‘{}’fmf 많이 이용
1
2
3
<body>
  <h1></h1>
</body>
  • {% … %} : 구문에 사용(if, for 등).
  • {{ … }} : 템플렛 결과 (html)에 출력할 표현(변수 등).
  • {# … #} : 주석 처리할 때 사용

변수 넘기기

1
2
3
4
5
6
(... 생략 ...)
@app.route('/')
def index():
    apple = 'red'
    apple_count = 10
    return render_template('index.html', fruit_color=apple, number=apple_count)
1
2
3
4
<body>
  <h2>Apple is {{ fruit_color }}</h2>
  <h2>{{ apple_count }} 개의 과일이 있습니다.</h2>
</body>

기능

객체 태그

1
2
3
var=[1, 2, 3]
fruits = { 'apple' : 'red', 'banana' : 'yellow' }
vegetables = [ 'cucumber', 'spinach' ]
1
2
3
{{ var }}
{{ fruits.apple }}
{{ vegetables[0] }}

if 구문

1
2
3
4
5
6
7
{% if True %}
<h1>It is True</h1>
{% elif False %}
<h1>It is False</h1>
{% else %}
<h1>It is not True</h1>
{% endif %}

for 구문

1
item_list = ['book', 'keyboard', 'window']
1
2
3
{% for item in item_list %}
<p>{{ item }}</p>
{% endfor %}
loop 속성 설명
loop.index 반복 순서 1부터 1씩 증가
loop.index0 반복 순서 0부터 1씩 증가
loop.first 반복 순서가 처음일 경우 True 아니면 False
loop.last 반복 순서가 마지막일 경우 True 아니면 False
1
2
3
{% for item in item_list %}
<p>인덱스 : {{ loop.index0 }}, 이름 : {{ item }}</p>
{% endfor %}

상속

  • 반복적으로 사용되는 부분을 하나로 묶어서 사용할 수 있게끔 해주는 기능
  • {% extends %}
  • {% block %} … {% endblock %}

자식 상속

  • 부모 template의 경로를 자식 template 파일의 첫 줄에 적어주게 되면 Jinja의 template 엔진이 해당 파일을 이용
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<!-- 부모 템플릿 -->
<!DOCTYPE html>
<html lang="en">
<head>
    {% block head %}
    <title>{% block title %}{% endblock %} - My Webpage</title>
    {% endblock %}
</head>
<body>
    <div id="content">{% block content %}{% endblock %}</div>
    <div id="footer">
        {% block footer %}
        &copy; Copyright 2008 by <a href="http://domain.invalid/">you</a>.
        {% endblock %}
    </div>
</body>
</html>

<!-- 자식 템플릿 -->
{% extends "base.html" %}
{% block title %}Index{% endblock %}
{% block head %}
    {{ super() }}
{% endblock %}
{% block content %}
    <h1>Index</h1>
    <p class="important">
      Welcome to my awesome homepage.
    </p>
{% endblock %}

진자 블록

  • 상속된 파일에서 변경할 수 있는 일종의 HTML ‘블록’을 지정
  • 부모의 ‘head’ 블록 내용을 가져오고 싶을 때
1
2
3
4
5
6
7
8
{% extends "base.html" %}
{% block title %}Index{% endblock %}
{% block head %}
    {{ super() }}
    <h1>자식 블록에서 출력한 h1 입니다.</h1>
{% endblock %}

(... 생략 ...)

Boostrap

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
<!DOCTYPE html>
<html>
  <head>
    <!-- CSS only -->
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-giJF6kkoqNQ00vy+HMDP7azOuL0xtbfIcaT9wjKHr8RbDVddVHyTfAAsrekwKmP1" crossorigin="anonymous">

    <!-- JavaScript Bundle with Popper -->
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta1/dist/js/bootstrap.bundle.min.js" integrity="sha384-ygbV9kiqUc6oa4msXn9868pTtWMgiQaeYH7/t7LECLbyPA2x65Kgf80OJFdroafW" crossorigin="anonymous"></script>
  </head>
  <body>
    <nav class="navbar navbar-expand-lg navbar-light bg-light">
    <div class="container-fluid">
        <a class="navbar-brand" href="#">Navbar</a>
        <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
        <span class="navbar-toggler-icon"></span>
        </button>
        <div class="collapse navbar-collapse" id="navbarSupportedContent">
        <ul class="navbar-nav me-auto mb-2 mb-lg-0">
            <li class="nav-item">
            <a class="nav-link active" aria-current="page" href="#">Home</a>
            </li>
            <li class="nav-item">
            <a class="nav-link" href="#">Link</a>
            </li>
            <li class="nav-item dropdown">
            <a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-bs-toggle="dropdown" aria-expanded="false">
                Dropdown
            </a>
            <ul class="dropdown-menu" aria-labelledby="navbarDropdown">
                <li><a class="dropdown-item" href="#">Action</a></li>
                <li><a class="dropdown-item" href="#">Another action</a></li>
                <li><hr class="dropdown-divider"></li>
                <li><a class="dropdown-item" href="#">Something else here</a></li>
            </ul>
            </li>
        </ul>
        <form class="d-flex">
            <input class="form-control me-2" type="search" placeholder="Search" aria-label="Search">
            <button class="btn btn-outline-success" type="submit">Search</button>
        </form>
        </div>
    </div>
    </nav>
  </body>
</html>

Reference

0%