플라스크에선 Flask-WTF 모듈을 이용해 폼을 쉽게 관리할 수 있다.
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 토큰은 자동으로 생성된다.
아래처럼 라우트 함수에서 생성한 폼을 사용하고, 검증 기준에 맞춰 검증할 수 있다.
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인지 확인
아래처럼 템플릿에서 폼 클래스를 사용할 수 있다.
<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>
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
어플리케이션 수준 데이터 요청, CLI 명령어 등 어플리케이션을 각 함수에 전달하지 않고 current_app, g 같은 프록시가 접근된다. 요청할 때 요청 수준 데이터를 추적하는 요청 컨텍스트와 비슷하다
플라스크 앱 객체는 view와 CLI 명령어에서 유용한 config같은 속성을 가지고 있다. 하지만 app 인스턴스를 모듈 내에서 임포트하는 것은 순환 참조1) 문제를 발생 시킬 수 있다. 어플리케이션 팩토리를 하거나 재사용 가능한 블루프린트/익스텐션을 작성하면 임포트할 앱 객체가 아예 없을 수도 있다. 플라스크에서는 어플리케이션 컨텍스트로 이런 문제들을 해결한다. app 객체를 직접 참조하지 않고 현재 동작을 처리하는 어플리케이션을 가리키는 current_app 프록시를 사용하는 것이 예시이다.
1) 순환 참조 : 두 개의 모듈이 서로를 참조하는 것으로 각 모듈이 서로를 통해 정의되어 발생한다. 코드의 유지보수가 어려워지고, 모듈끼리 강한 결합이되기 때문에 재사용성이 떨어지는 것은 물론, 무한 재귀, 메모리 누수 같은 에러가 발생할 수도 있다.
플라스크는 자동으로 내보낸다 어플리케이션 컨택스트를 요청을 처리할 때. 요청시 실행되는 view 함수, 에러 핸들러, 기타 다른 함수들은 current_app에 접근할 수 있다. Flask.cli에 등록된 CLI 명령어들을 실행할 때도 플라스크는 자동으로 어플리케이션 컨텐스트를 내보낸다.
어플리케이션 컨택스트는 요청 시 데이터를 저장하기에도 좋다. 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을 담는다