[Flask] Flask - Blueprints and Views

최더디·2020년 9월 2일
0

Flask-문서번역

목록 보기
3/4

해당 글은 공부를 위해 https://flask.palletsprojects.com/en/1.1.x/tutorial/views/ 을 번역한 글입니다.

Blueprints and Views


View 함수는 애플리케이션에 대한 요청에 응답하기 위해 작성하는 코드이다.
Flask는 패턴을 사용하여 수신 요청 URL을 처리해야하는 View에 일치시킨다.
View는 Flask가 발신 응답으로 변환하는 데이터를 반환한다.
Flask는 다른 방향으로 이동하여 이름과 인수를 기반으로 View에 대한 URL을 생성할 수 있다.

Blueprint 생성


하나의 Blueprint는 관련 뷰 및 기타 코드 그룹을 구성하는 방법이다.
뷰 및 기타 코드를 애플리케이션에 직접 등록하는 대신 Blueprint에 등록한다.
그런 다음 Factory 함수에서 사용할 수 있을 때, Blueprint는 응용 프로그램에 등록된다.

Flaskr 에는 두 개의 Blueprint가 있는데 하나는 인증 기능용이고, 다른 하나는 블로그 게시물 기능용이다.각 Blueprint에 대한 코드는 별도의 모듈로 이동한다.블로그에서 인증에 대해 알아야하므로 먼저 인증을 작성하자.

# flaskr/auth.py

from flask import (
    Blueprint, flash, g, redirect, render_template, request, session, url_for
)
from werkzeug.security import check_password_hash, generate_password_hash

from flaskr.db import get_db

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

'auth' 라는 이름을 가진 Blueprint 를 생성한다.
애플리케이션 객체와 마찬가지로 Blueprint는 정의된 위치를 알아야하므로 __name__을 두번째 인수로 전달된다.
url_prefix 는 Blueprint와 관련된 모든 URL 앞에 추가된다.

# flaskr/__init__.py

def create_app():
    app = ...
    # existing code omitted

    from . import auth
    app.register_blueprint(auth.bp)

    return app

app.register_blueprint()를 사용하여 Factory에서 Blueprint를 가져오고 등록한다.
앱을 반환하기 전에 Factory 함수 끝에 새 코드를 배치한다.

인증 Blueprint에는 새 사용자를 등록하고 로그인 및 로그아웃하는 보기가 있을 것이다.

The First View : 등록하기(Register)


사용자가 /auth/register URL에 방문하면 register 뷰는 사용자가 작성할 양식과 함께 HTML을 반환한다.
양식을 제출하면 입력 내용을 확인하고 오류 메시지와 함께 양식을 다시 표시하거나 새 사용자를 만들고 로그인 페이지로 이동한다.

# flaskr/auth.py

@bp.route('/register', methods=('GET', 'POST'))
def register():
    if request.method == 'POST':
        username = request.form['username']
        password = request.form['password']
        db = get_db()
        error = None

        if not username:
            error = 'Username is required.'
        elif not password:
            error = 'Password is required.'
        elif db.execute(
            'SELECT id FROM user WHERE username = ?', (username,)
        ).fetchone() is not None:
            error = 'User {} is already registered.'.format(username)

        if error is None:
            db.execute(
                'INSERT INTO user (username, password) VALUES (?, ?)',
                (username, generate_password_hash(password))
            )
            db.commit()
            return redirect(url_for('auth.login'))

        flash(error)

    return render_template('auth/register.html')
  • @bp.route 는 URL /register를 register 뷰 함수와 연결한다.
    Flask가 /auth/register에 대한 요청을 받으면, register를 호출하고 반환 값을 응답으로 사용한다.
  • 사용자가 양식을 제출한 경우는 request.method 가 'POST' 이다.
    이 경우 입력 유효성 검사를 시작한다.
  • request.form 은 제출된 양식 키 및 값을 매핑하는 dictionary의 특수 유형이다.
    사용자는 username 및 password를 입력할 것이다.
  • username 과 password 가 비어 있지 않은지 확인해라.
  • 데이터베이스를 쿼리하고 결과가 반환되는지 확인하여 username이 아직 등록되지 않았는지 확인한다.
    db.execute 는 모든 사용자 입력에 대한 자리 표시자 및 자리 표시자를 대체할 값의 '?'를 사용하여 SQL 쿼리를 취한다.
    데이터베이스 라이브러리는 값을 escaping 처리하므로 SQL 삽입 공격에 취약하지 않는다.
    fetchone()은 쿼리에서 한 행을 반환한다. 쿼리가 결과를 반환하지 않으면 None으로 반환된다.
    나중에 fetchall()을 사용하여 모든 결과의 목록을 반환한다.
  • 유효성 검사에 성공하면 새 사용자 데이터를 DB에 삽입한다.
    보안을 위해 암호를 데이터에빙스에 직접 저장해서는 안 된다.
    대신 generate_password_hash() 를 사용하여 암호를 안전하게 해시하고 해당 해시가 저장된다.
    이 쿼리는 데이터를 수정하므로 변경 내용을 저장하기 위해 db.commit()를 나중에 호출해야 한다.
  • 사용자를 저장하면 로그인 페이지로 Redirect 된다.
    url_for()은 이름을 기준으로 login View 에 대한 URL을 생성한다.
    이는 URL에 연결되는 모든 코드를 변경하지 않고 나중에 URL을 변경할 수 있기 때문에 URL을 직접 쓰는 것보다 바람직하다.
    redirect()는 생성된 URL에 대한 redirect 응답을 생성한다.
  • 유효성 검사에 실패하면 오류가 사용자에게 표시된다.
    flash()는 템플릿을 렌더링 할 때 검색할 수 있는 메시지를 저장한다.
  • 사용자가 처음에 auth/register 로 이동하거나 검증 오류가 있을 때, 등록 양식이 있는 HTML 페이지를 표시해야한다.
    render_template()는 HTML이 포함된 템플릿을 렌더링하며, 이 템플릿은 다음 tutorial에서 작성한다.

Login


Login 뷰는 Register 뷰와 비슷한 패턴을 가진다.

# flaskr/auth.py

@bp.route('/login', methods=('GET', 'POST'))
def login():
    if request.method == 'POST':
        username = request.form['username']
        password = request.form['password']
        db = get_db()
        error = None
        user = db.execute(
            'SELECT * FROM user WHERE username = ?', (username,)
        ).fetchone()

        if user is None:
            error = 'Incorrect username.'
        elif not check_password_hash(user['password'], password):
            error = 'Incorrect password.'

        if error is None:
            session.clear()
            session['user_id'] = user['id']
            return redirect(url_for('index'))

        flash(error)

    return render_template('auth/login.html')

register 뷰 몇가지 차이점이 있다.

  • 사용자는 먼저 쿼리되고, 나중에 사용하기 위해 변수에 저장된다.
  • check_password_hash()는 제출된 암호를 저장된 해시와 동일한 방법으로 해시하고 안전하게 비교한다.
    만약 그들이 일치한다면 비밀번호는 유효하다.
  • 세션은 요청간에 데이터를 저장하는 Dictionary 다.
    유효성 검사에 성공하면 사용자의 ID가 새 세션에 저장된다.
    데이터는 브라우저로 전송되는 쿠기에 저장되고, 브라우저는 이후 요청과 함께 데이터를 다시 보낸다.
    Flask는 데이터 손상이 없도록 안전하게 서명한다.

이제 사용자의 ID가 세션에 저장되었으므로, 이후 요청 시 사용할 수 있게 된다.
각 요청이 시작될 때 사용자가 자신의 정보에 로그인한 경우 로드하여 다른 보기에 제공해야 한다.

# flaskr/auth.py

@bp.before_app_request
def load_logged_in_user():
    user_id = session.get('user_id')

    if user_id is None:
        g.user = None
    else:
        g.user = get_db().execute(
            'SELECT * FROM user WHERE id = ?', (user_id,)
        ).fetchone()

bp.before_app_request() 는 어떤 URL을 요청하든 View 함수보다 먼저 실행되는 함수를 등록한다.
load_logged_in_user()는 사용자 ID가 세션에 저장되어 있는지 확인하고, 데이터베이스에서 해당 사용자의 데이터를 가져와 g.user에 저장하며, 요청 기간동안 지속된다.
사용자 ID가 없거나 ID가 존재하지 않는 경우, g.user는 None 이다.

Logout


로그아웃하려면 세션에서 사용자 ID를 제거하면 된다.
그러면 load_logged_in_user 는 후석 요청에 대해 사용자를 로드하지 않는다.

# flaskr/auth.py

@bp.route('/logout')
def logout():
    session.clear()
    return redirect(url_for('index'))

다른 View에서 인증이 필요한 경우(인증 요청)


블로그 게시물 작성, 편집 및 삭제하려면 사용자가 로그인해야 한다.
Decorator 를 사용하여 적용되는 각 View에 대해 이를 확인할 수 있다.

# flaskr/auth.py

def login_required(view):
    @functools.wraps(view)
    def wrapped_view(**kwargs):
        if g.user is None:
            return redirect(url_for('auth.login'))

        return view(**kwargs)

    return wrapped_view

Decorator 는 적용된 원래 뷰를 래핑하는 새로운 뷰 함수를 반환한다.
새 함수는 사용자가 로드되었는지 확인하고 그렇지 않은 경우 Login 페이지로 Redirect 한다.
사용자가 로드된 경우 원래 View가 호출되고 정상으로 계속된다.

profile
focus on why

0개의 댓글