[오늘의배움] 036 Flask-WTF, ORM 릴세이션십, 어플리케이션 컨택스트

이상민·2021년 1월 24일
0

[오늘의 배움]

목록 보기
39/70
post-thumbnail

1. 폼

플라스크에선 Flask-WTF 모듈을 이용해 폼을 쉽게 관리할 수 있다.

1-1. 폼 생성

from flask_wtf import FlaskForm
from wtforms import StringField
from wtforms.validators import DataRequired

class MyForm(FlaskForm):
    name = StringField('name', validators=[DataRequired()])

슈퍼 클래스인 FlaskForm을 임포트하고 다양한 필드와 validator를 임포트해서 사용한다. CSFR 토큰은 자동으로 생성된다.

1-2. 폼 검증

아래처럼 라우트 함수에서 생성한 폼을 사용하고, 검증 기준에 맞춰 검증할 수 있다.

from forms import MyForm

@app.route('/submit', methods=('GET', 'POST'))
def submit():
    form = MyForm()
    if form.validate_on_submit():
        return redirect('/success')
    return render_template('submit.html', form=form)```

validate_on_submit : 검증 + POST request인지 확인

1-3. 템플릿에서 폼 사용

아래처럼 템플릿에서 폼 클래스를 사용할 수 있다.

<form method="POST" action="/">
    {{ form.csrf_token }}
    {{ form.name.label }}
    {{ form.name }}
    <input type="submit" value="Go">
</form>

css를 더 자유롭게 사용하고 싶다면 아래처럼 사용도 가능하다.

<div class="form-group">
    <label for="username">아이디</label>
    <input type="text" class="form-control" name="username" id="username"
           value="{{ form.username.data or '' }}">
</div>

2. ORM 릴레이션십

ORM 클래스에서 릴레이션십으로 설정한 애트리뷰트에 인스턴스를 넣는게 이해가 안돼서 찾아봤지만 결국 이해 못해서 그냥 그렇게 작동하는구나하고 일단 넘어가기로 했다.

class Posts(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    user_id = db.Column(db.Integer, db.ForeignKey('users.id', ondelete='CASCADE'))
    user = db.relationship('Users', backref=db.backref('post_set'))
    img_path = db.Column(db.String(200))
    desc = db.Column(db.Text(), nullable=True)
    created_date = db.Column(db.DateTime(), nullable=False)
    updated_date = db.Column(db.DateTime(), nullable=True)
    
class Users(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(50), unique=True, nullable=False)
    username = db.Column(db.String(50), unique=True, nullable=False)
    password = db.Column(db.String(100), nullable=False)
    email = db.Column(db.String(100), unique=True, nullable=False)

위처럼 릴레이션십을 설정했을 때 Posts.user 객체에 Users 객체를 넣으면 자동으로 설정되어 아래 예시처럼 해도 user_id 값이 채워진다.

>>> post = Posts(created_date=datetime.now(), user=g.user)

user는 테이블상 애트리뷰트가 아니기 때문에 DB상의 테이블에서 볼 수 없다.

https://docs.sqlalchemy.org/en/14/orm/tutorial.html#building-a-relationship


3. 어플리케이션 컨텍스트

어플리케이션 수준 데이터 요청, CLI 명령어 등 어플리케이션을 각 함수에 전달하지 않고 current_app, g 같은 프록시가 접근된다. 요청할 때 요청 수준 데이터를 추적하는 요청 컨텍스트와 비슷하다

3-1. 컨택스트의 목적

플라스크 앱 객체는 view와 CLI 명령어에서 유용한 config같은 속성을 가지고 있다. 하지만 app 인스턴스를 모듈 내에서 임포트하는 것은 순환 참조1) 문제를 발생 시킬 수 있다. 어플리케이션 팩토리를 하거나 재사용 가능한 블루프린트/익스텐션을 작성하면 임포트할 앱 객체가 아예 없을 수도 있다. 플라스크에서는 어플리케이션 컨텍스트로 이런 문제들을 해결한다. app 객체를 직접 참조하지 않고 현재 동작을 처리하는 어플리케이션을 가리키는 current_app 프록시를 사용하는 것이 예시이다.

1) 순환 참조 : 두 개의 모듈이 서로를 참조하는 것으로 각 모듈이 서로를 통해 정의되어 발생한다. 코드의 유지보수가 어려워지고, 모듈끼리 강한 결합이되기 때문에 재사용성이 떨어지는 것은 물론, 무한 재귀, 메모리 누수 같은 에러가 발생할 수도 있다.

3-2. current_app

플라스크는 자동으로 내보낸다 어플리케이션 컨택스트를 요청을 처리할 때. 요청시 실행되는 view 함수, 에러 핸들러, 기타 다른 함수들은 current_app에 접근할 수 있다. Flask.cli에 등록된 CLI 명령어들을 실행할 때도 플라스크는 자동으로 어플리케이션 컨텐스트를 내보낸다.

3-3. g

어플리케이션 컨택스트는 요청 시 데이터를 저장하기에도 좋다. g object가 이런 목적으로 사용된다. g는 글로벌의 약자로 컨택스트가 유지되는한 정보를 저장할 수 있지만, 컨택스트가 끝나면 정보가 사라지기 때문에 요청 간 데이터를 저장하려면 세션이나 데이터베이스를 사용하는게 좋다.

@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 = Users.query.get(user_id)

위 함수를 작성하면 모든 요청 전에 세션에서 user_id를 가져와 로그인 되어있다면 유저 객체를 g.user를 생성하여 담고 로그인 되어있지않다면 None을 담는다

profile
편하게 읽기 좋은 단위의 포스트를 추구하는 개발자입니다

0개의 댓글