[Flask] Flask

최더디·2020년 9월 2일
0

Flask-문서번역

목록 보기
1/4

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

A Minimal Application


Flask 애플리케이션을 작게 보면 아래와 같이 볼 수 있다.

from flask import Flask
app = Flask(__name__)

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

1. Flask class를 import 해야한다.


from flask import Flask

2. class의 instance를 생성한다. (첫 번째 인수는 응용 프로그램 모듈 or 패키지 이름이다)


app = Flask(__name__)

3. route() 를 통해 Flask 에게 어떤 URL이 함수를 트리거해야하는지 알려준다.


@app.route('/')

(route()는 decorator 라고 불린다.) (내 생각에는 아마 URL을 만들어주는 느낌이다..)

4. 함수 작성 (함수에는 특정 함수에 대한 URL을 생성하는데 사용되는 이름을 제공)


def 함수명():
	return 리턴값

Routing


route() 라는 decorator 를 사용하여 함수를 URL에 바인딩한다.
이것을 통해 URL의 일부를 동적으로 만들고 여러 규칙을 함수에 첨부 할 수 있다.

@app.route('/')
def index():
	return 'Index Page'
   
@app.route('/hello')
def hello():
	return 'Hello, World'

변수 규칙


<variable_name>을 사용해 URL에 변수를 추가할 수 있다.
그러면 함수는 <variable_name>을 키워드 인수로 받는다.
선택적으로 변환기를 사용하여 같은 인수 유형을 지정할 수 있다. ex) <converter : variable_name>

from markupsafe import escape

# show the user profile for that user
@app.route('/user/<username>)'
def show_user_profile(username):
    return 'User %s' % escape(username)

# show the post with the given id, the id is an integer
@app.route('/post/<int:post_id>)'
def show_post(post_id):
    return 'Post %d' % post_id

# show the subpath after /path/
@app.route('/path/<path:subpath>)'
def show_subpath(subpath):
    return 'Subpath %s' % escape(subpath)

Converter 유형

유형설명
string(default) /(슬래시) 없이 텍스트를 허용
int양의 정수를 허용
float양의 부동 소수점 값을 허용
path같은 string 뿐만 아니라 /(슬래시) 를 허용
uuiduuid 문자열을 허용

고유한URLs / Redirection 동작


@app.route('/projects/')
def projects():
	return 'The project page'

@app.route('/about')
def about():
	return 'The about page'

위에 두개의 route()안의 마지막 부분에 슬래시(/) 가 있느냐 vs 없느냐

  • 슬래시(/) 있는 경우 : 파일 시스템의 폴더와 비슷하다.
    후행 슬래시없이 URL에 엑세스하면 Flask가 후행 슬래시가 있는 표준 URL로 Redirection 한다.

  • 슬래시(/) 없는 경우 : 파일의 경로 이름과 비슷하다.
    슬래시가 있는 URL에 엑세스하면 'page 404' 오류 발생. 이렇게하면 이러한 리소스에 대한 URL에 대한 URL을 고유하게 유지하여 검색 엔진이 동일한 페이지를 두 번 색인 생성하는 것을 피할 수 있음

URL 구축


특정 함수에 대한 URL을 작성하려면 url_for() 함수를 사용하면 된다.
함수의 이름을 첫 번째 인수로, 키워드 인수의 수는 각각의 URL 규칙의 변수 부분에 해당한다.
(알 수 없는 변수 부분이 쿼리 매개 변수로 URL에 추가된다)

from flask import Flask, url_for
from markupsafe import escape

app = Flask(__name__)

@app.route('/')
def index():
	return 'index'
    
@app.route('/login')
def login():
	return 'login'
    
@app.route('/user/<username>')
def profile(username):
	return '{}\'s profile' .format(escape(username))
    
with app.test_request_context():
	print(url_for('index'))
    print(url_for('login'))
    print(url_for('login', next = '/'))
    print(url_for('profile', username='John Doe'))
# Result

/
/login
/login?next=/
/user/Jong%20Doe

왜 템플릿에 하드코딩 대신에 반전 기능이 있는 url_for() 함수를 사용하여 URL을 작성하느냐

  • Reversing은 종종 URLs를 하드코딩하는 것보다 설명을 쉽게 해줄 수 있다.
  • 하드 코딩된 URL을 수동으로 변경하는 것을 기억할 필요없이 URL을 한 번에 변경할 수 있다.
  • URL 작성은 특수 문자 및 유니 코드 데이터의 escape 처리를 투명하게 처리한다.
  • 생성 된 경로는 항상 절대적이므로 브라우저에서 상대 경로의 예기치 않은 동작이 발생하지 않는다.
  • 응용 프로그램이 URL root 외부에 배치되어있는 경우 적절하게 처리를 도와준다.
    ex) /myapplication → /, url_for()

HTTP Methods


웹 애플리케이션은 URL에 엑세스 할 때 다른 HTTP Methods 를 사용한다.
(Default 값은 GET 요청에만 응답한다)
route()의 인수인 methods 를 사용하여 다른 HTTP Methods를 처리할 수 있다.

from flask import request

@app.route('/login', methods = ['GET', 'POST'])
def login():
	if request.method == 'POST':
    	return do_the_login()
    else:
    	return show_the_login_form()

Static Files


동적 웹 애플리케이션에는 '정적 파일(Static Files)'도 필요하다.
(일반적으로 CSS 및 Javascirpt 파일이 제공된다.)
패키지 or 모듈 옆에 static 폴더를 만들면 /static 응용 프로그램에서 사용할 수 있다.

정적 파일의 URL을 생성하려면 'static' Endpoint 이름을 사용한다.
파일은 파일 시스템에 저장해야한다. static/style.css

url_for('static', filename='style.css')

Rendering Templates


템플릿을 렌더링하려면 render_templates() 방법을 사용하면 된다.
템플릿 이름과 템플릿 엔진에 전달할 변수를 키워드 인수로 제공하기만 하면 된다.
템플릿을 렌더링 하는 방법에 대한 간단한 예는 다음과 같다.

from flask import render_tmplate

@app.route('/hello/')
@app.route('/hello/<names>')
def hello(name=None):
	return render_template('hello.html', name=name)

Flask는 templates 폴더에서 템플릿을 찾는다.

  • module 인 경우
/application.py
/templates
	/hello.html
  • package 인 경우
/application
    /__init__.py
    /templates
    	/hello.html

템플릿의 경우 Jinja2 템플릿의 모든 기능을 사용할 수 있다.

<!doctype html>
<title>Hello from Flask</title>
{% if name %}
	<h1>Hello {{name}}</h1>
{% else %}
	<h1>Hello, World!</h1>
{% endif %}

템플릿은 상속도 가능하다.
기본적으로 템플릿 상속을 통해 각 페이지에 특정 요소 (ex: 메뉴, 바닥글)를 유지할 수 있다.

Accessing Request Data


웹 응용 프로그램의 경우 클라이언트가 서버로 보내는 데이터에 반응하는 것이 중요하다.
Flask에서 이 정보는 전역 request 객체에 의해 제공된다.
파이썬에 대한 경험이 있다면, 그 객체가 어떻게 전역화되고 Flask가 여전히 스레드 안전을 유지하는지 궁금 할 것이다.
정답은 Context Locals 이다.

Context Locals


Flask의 특정 개체는 전역 개체이지만 일반적인 종류는 아니다.
이러한 객체는 실제로 특정 컨텍스트에 로컬인 객체에 대한 프록시이다.
그러나 이것은 실제로 이해하기 매우 쉽다.

context가 처리 스레드라고 가정하자.
요청이 들어오고 웹 서버는 새 스레드를 생성하기로 결정한다.(또는 다른 기본 개체는 스레드 이외의 동시성 시스템을 처리할 수 있음)
Flask가 내부 요청 처리를 시작하면, 현재 스레드가 활성 컨텍스트이고 현재 응용 프로그램 및 WSGI 환경을 해당 context(스레드)에 바인딩한다.
한 응용 프로그램이 다른 응용 프로그램을 중단하지 않고 호출할 수 있도록 지능적인 방식으로 수행한다.

이것이 당신에게 무엇을 의미하는가?
기본적으로 단위 테스트와 같은 작업을 수행하지 않는 한 이것이 사실임을 완전히 무시할 수 있다.
요청 객체가 없기 때문에 요청 객체에 의존하는 코드가 갑자기 중단된다.
해결책은 요청 객체를 직접 만들고 context에 바인딩하는 것이다.
단위 테스트를 위한 가장 쉬운 솔루션은 test_request_context() 컨텍스트 관리자를 사용하는 것이다.
with 명령문과 함께 테스트 요청을 바인드하여 상호 작용할 수 있다.
예를 들면 다음과 같다.

from flask import Flask

# now you can do something with the request until the
# end of the with block, such as basic assertions:
with app.test_request_context('/hello', method='POST'):
	assert request.path == '/hello'
        assert reqeust.method == 'POST'

다른 가능성은 전체 WSGI 환경을 request_context() 메소드에 전달하는 것이다.

from flask import request

with app.request_context(environ):
	assert request.method == 'POST'

The Request Object


Flask 모듈에서 request 를 import 해야한다.
from flask import request

현재 요청 방법은 method 속성을 사용하여 사용할 수 있다.
양식 데이터 (POST or PUT 요청으로 전송된 데이터)에 엑세스하려면 form 속성을 사용할 수 있다.
위에서 언급한 두가지 속성의 전체 예는 다음과 같다.

@app.route('/login', methods=['POST', 'GET'])
def login():
    error = None
    if request.method == 'POST':
    	if valid_login(request.form['username'], request.form['password']):
        	return log_the_user_in(request.form(['username'])
        else:
        	error = 'Invalid username/password'
    # the code below is executed if the request method
    # was GET or the credetials were invalid
    return render_template('login.html', error=error)

form 속성에 키가 없으면 특수한 KeyError가 발생한다.
표준 KeyError는 잡을 수 있지만 그렇게 하지 않으면 HTTP 400 Bad Request 오류 페이지가 대신 표시된다.
따라서 많은 상황에서 그 문제를 다룰 필요가 없다.

URL(?key=value)에 제출 된 매개 변수에 엑세스하려면 다음 args 속성을 사용할 수 있다.
searchword = request.args.get('key', '')

File Uploads


Flask로 업로드 된 파일을 쉽게 처리할 수 있다.
enctype = "multipart/form-data" HTML 양식에 속성을 설정하는 것을 잊지마라.
그렇지 않으면 브라우저가 파일을 전송하지 않을 것이다.

업로드 된 파일은 메모리나 파일 시스템의 임시 위치에 저장된다.
요청 오브젝트의 files 속성을 보고 해당 파일에 엑세스 할 수 있다.
업로드 된 각 파일은 해당 사전에 저장된다.표준 Python file 객체처럼 작동 하지만, 서버의 파일 시스템에 해당 파일을 저장할 수 있게 해주는 save() method를 가진다.
작동 방식을 보여주는 간단한 예는 다음와 같다.

from flask import request

@app.route('/upload', methods=['GET', 'POST'])
def upload_file():
	if request.method == 'POST':
    	f = request.files['the_file']
        f.save('/var/www/uploads/uploaded_file.txt')
    ...

애플리케이션에 파일을 업로드하기 전에 클라이언트에서 파일 이름을 지정한 방법을 알고 싶다면 filename 속성에 엑세스 할 수 있다.
그러나 이 값은 위조 될 수 있으므로 절대 그 값을 신뢰하지 마라.
클라이언트의 파일 이름을 사용하여 파일을 서버에 저장하려면 secure_filename() 기능을 통해 파일을 전달해라.

from flask import request
from werkzeug.utils import secure_filename

@app.route('/upload', methods=['GET', 'POST'])
def upload_file():
	if request.method == 'POST':
    	f = request.files['the_file]
        f.save('/var/www/uploads/' + secure_filename(f.filename))
    ...

Cookies


쿠키에 엑세스하기 위해 cookies 속성을 사용할 수 있다.
쿠키를 설정하기 위해 응답 객체의 set_cookie() 메서드를 사용할 수 있다.
요청 객체의 cookies 속성은 클라이언트가 전송하는 모든 쿠키가 포함된 사전이다.
세션을 사용하려면 쿠키를 직접 사용하지 말고 Flask의 세션을 사용하여 쿠키 위에 보안을 강화해라.

쿠키 읽기

from flask import request

@app.route('/')
def index():
	username = request.cookies.get('username')
    # use cookies.get(ket) instead of cookies[key] to not get a
    # KeyError if the cookies is missing

쿠키 저장

from flask import make_response

@app.route('/')
def index():
	resp = make_response(render_template(...))
   	resp.set_cookie('username', 'the username')
    	return resp

쿠키는 응답 객체에 설정되어 있다.
일반적으로 뷰 함수에서 문자열을 반환하기 때문에, Flask는 문자열을 응답 객체로 변환한다.
명시적으로 그렇게 하려면 make_response() 함수를 사용한 다음 수정할 수 있다.

응답 개체가 아직 없는 지점에서 쿠키를 설정하려는 경우가 있다.
Deferred Request Callbacks(지연된 요청 콜백) 패턴을 사용하려 가능하다.

Redirects and Errors


사용자를 다른 Endpoint 로 Redirect 하려면 redirect() 기능을 사용한다.
오류 코드로 요청을 일찍 중단하려면 다음 abort() 함수를 사용한다.

from flask import abort, redirect, url_for

@app.route('/')
def index():
    return redirect(url_for('login'))
    
@app.route('/login')
def login():
    abort(401)
    this_is_never_executed()

이는 사용자가 색인에서 엑세스 할 수 없는 페이지로 Redirect 되므로 (401은 엑세스 거부를 의미)
작동 방식을 보여주기 때문에 다소 의미가 없다.

기본적으로 각 오류 코드마다 흑백 오류 페이지가 표시된다.
오류 페이지를 사용자 정의하려면 errorhandler() Decorator를 사용할 수 있다.

from flask import render_template

@app.errorhandler(404)
def page_not_found(error):
    return render_template('page_not_found.html'), 404

render_template()를 부른 이후, 404 를 유의해야 한다.
페이지의 상태 코드가 404는 Flask에서 페이지를 찾을 수 없다는 것을 의미한다.

About Responses


뷰 함수의 반환 값은 자동으로 응답 객체로 변환된다.
반환 값이 문자열인 경우 문자열을 응답 본문, '200 OK' 상태 코드 및 text/html mimetype 으로하여 응답 객체로 변환된다.
반환 값이 dict 이면 응답을 생성하기 위해 jsonify() 이 호출된다.
Flask가 반환 값을 응답 객체로 변환하는데 적용되는 논리는 다음과 같다.

  • 올바른 유형의 응답 객체가 반환되면 뷰에서 직접 반환된다.
  • 문자열인 경우 해당 데이터와 기본 매개변수를 사용하여 응답 오브젝트가 생성된다.
  • dict 인 경우 jsonify 를 사용하여 응답 객체를 만든다.
  • 튜플이 반환되면 튜플의 항목이 추가 정보를 제공할 수 있다.
    이러한 튜플은 (response, status), (response, header), (response, status, headers) 이러한 형식이어야 한다.
    status 값은 상태 코드를 무시하고 headers 추가 헤더 값의 목록이나 사전이 될 수 있습니다.
  • 그 중 어느 것도 작동하지 않으면, Flask는 반환 값이 유효한 WSGI 응용 프로그램이라고 가정하고 응답 객체로 변환한다.

뷰 내에서 결과 응답 객체를 유지하려면 make_response() 함수를 사용할 수 있다.
다음과 같은 관점이 있다고 상상해보자

@app.errorhandler(404)
def not_found(error):
    return render_template('error.html'), 404

make_response() 를 사용해 리턴 표현식을 랩핑하고,응답 오브젝트를 가져와서 수정한 다음 리턴하면 된다.

@app.errorhandler(404)
def not_found(error):
    resp = make_response(render_template('error.html'), 404)
    resp.headers['X-Something'] = 'A value'
    return resp

APIs with JSON


API를 작성할 때 일반적인 응답 형식은 JSON 이다.
Flask로 그러한 API를 작성하기 쉽다.
뷰에서 dictionary 를 반환하면 JSON 응답으로 변환된다.

@app.route('/me')
def me_api():
    user = get_current_user()
    return {
        "username" : user.username,
        "theme" : user.theme,
        "image" : url_for("user_image", filename=user.image),
    }

API 디자인에 따라 dictionary 이외의 유형에 대한 JSON 응답을 만들 수 있다.
이 경우 모든 JSON 데이터 유형을 직렬화할 수 있는 jsonify() 함수를 사용해라.
또는 더 복잡한 응용 프로그램을 지원하는 Flask 커뮤니티 확장을 살펴봐라

@app.route('/users')
def users_api():
    users = get_all_users()
    return jsonify([user.to_json() for user in users])

Sessions


요청 객체 외에도 요청에서 다음 요청까지 사용자에게 특정한 정보를 저장할 수 있는 session을 부르는 두 번째 객체가 있다.
이것은 쿠키 위에 구현되어 쿠키에 암호화적으로 서명한다.
이는 사용자가 서명에 사용된 비밀키를 모르는 경우 쿠키의 내용을 볼 수는 있지만 수정할 수는 없다는 것을 의미한다.

세션을 사용하려면 비밀키를 설정해야한다. (세션 작동 방식은 다음과 같다)

from flask import Flask, session, redirect, url_for, request
from markupsafe import escape

app = Flask(__name__)

# Set the secret key to some random bytes. Keep this really secret!
app.secret_key = b'_5$2L"F4Q8z\n\xec]/'

@app.route('/')
def index():
    if 'username' in session:
        return 'Logged in as %s' %escape(session['username'])
    return 'You are not logged in'

@app.route('/login', methods = ['GET', 'POST'])
def login():
    if request.method == 'POST':
        session['username'] = request.form['username']
        return redirect(url_for('index'))
    return '''
        <form method = "post">
            <p><input type=text name=username>
            <p><input type=submit value=Login>
        </form>
    '''

@app.route('/logout')
def logout():
    # remove the username from the session if it's there
    session.pop('username', None)
    return redirect(url_for('index'))

escape()는 템플릿 엔진을 사용하지 않는 경우 탈출하지 않는다.

<쿠키 기반 세션에 대한 참고 사항>Flask는 세션 객체에 입력한 값을 가져오고 쿠키로 직렬화합니다.요청에서 일부 값이 유지되지 않고, 쿠키가 실제로 활성화되어 있고, 명확한 오류 메시지가 표시되지 않으면 웹 브라우저에서 지원하는 크기와 비교하여 페이지 응답에서 쿠키 크기를 확인하십쇼.

기본 클라이언트 측 기반 세션 외에도 서버 측에서 세션을 처리하려는 경우 이를 지원하는 여러 Flask 확장이 있다.

Message Flashing


좋은 응용 프로그램과 사용자 인터페이스는 모두 피드백에 관한 것이다.
사용자가 충분한 피드백을 얻지 못하면 응용 프로그램을 싫어하게 될 것이다.
플라스크는 Flashing 시스템을 사용하는 사용자에게 피드백을 제공하는 매우 간단한 방법을 제공한다.
Flashing 시스템은 기본적으로 요청이 끝날 때 메시지를 기록하고 다음 요청에서만 엑세스를 할 수 있다.일반적으로 메시지를 노출시키기 위해 레이아웃 템플릿과 결합된다.

메시지를 Flash하려면 flash() 메서드를 사용한다.get_flashed_message() 사용하여 템플릿에서 사용 가능한 메시지를 보유한다.

Logging


때로는 정확해야하지만 실제로는 올바르지 않은 데이터를 처리하는 상황에 처할 수 있다.
예를 들어 서버에 HTTP 요청을 보내는 일부 클라이언트 측 코드가 있을 수 있지만 분명히 잘못된 형식이다.
사용자가 데이터를 변경하거나 클라이언트 코드가 실패하여 발생할 수 있다.
대부분의 경우 해당 상황에서 '400 Bad Request' 답장을 해도 괜찮지만, 때로는 그렇지 않으며 코드가 계속 작동해야한다.

오류 발생한 것을 기록하고 싶을 수도 있다.
여기에서 로거가 편리하다.
Flask 0.3부터 logger는 사용할 수 있도록 사전 구성되어 있다.

다음은 몇 가지 로그 호출 예이다.

app.logger.debug('A value for debugging')
app.logger.warning('A warning occurred (%d apples)', 42)
app.logger.error('An error occurred')

Hooking in WSGI Middleware


Flask 애플리케이션에 WSGI 미들웨어를 추가하려면 애플리케이션 wsgi_app 속성을 랩핑해야 한다.예를 들어, Werkzeug의 ProxyFix 미들웨어를 Nginx 뒤에서 실행하기 위해 적용하려면 다음을 수행해야 한다.

from werkzeug.middleware.proxy_fix import ProxyFix
app.wsgi_app = ProxyFix(app.wsgi_app)

app 대신 app.wsgi_app 랩핑은 app이 미들웨어가 아닌 플라스크 애플리케이션을 가리키는 것을 의미한다.그래서 계속 사용하고 app 직접 구성할 수 있다.

profile
focus on why

0개의 댓글