지난 정리의 회원가입과 로그인의 template 코드를 보면 둘 다 method 방식이 POST인 것을 확인할 수 있습니다.
오늘은 로그인과 로그아웃 회원가입에 대해 정리해 보려고 하기에 로그인과 관련된 내용을 적어둔 auth.py 파일에서 로그인과 회원가입 부분을 수정해 줄 겁니다.
from flask import Blueprint, render_template
views = Blueprint("views", __name__)
@views.route("/")
def blog_home():
return render_template("index.html")
@views.route("/about")
def about_me():
return render_template("about.html")
@views.route("/contact")
def contact():
return render_template("contact.html")
@views.route("/categories-list")
def categories_list():
return render_template("categories-list.html")
method = ['GET', 'POST']를 써줌으로써 POST요청을 처리할 수 있도록 코드를 수정해주었습니다
👌request로 폼에서 데이터 받아오기
from flask import Blueprint, render_template, redirect, request
auth = Blueprint("auth", __name__)
@auth.route("/login",methods=['GET','Post'])
def login():
return render_template("login.html")
@auth.route("/logout")
def logout():
return redirect("views.blog_home")
@auth.route("/sign-up",methods=['GET','Post'])
def signup():
email = request.form.get('email')
print(email)
username = request.form.get('username')
print(username)
password1 = request.form.get('password1')
print(password1)
password2 = request.form.get('password2')
print(password2)
return render_template("signup.html")
request를 import 해줍니다 또한 get() 메서드의 인자로 name 속성에 사용되었던 email, username 등으로 데이터를 받아와 줍니다.
정보를 입력해 준 뒤 sign up을 누르면 터미널에 입력한 정보가 출력됩니다
Flask - SQLAlchemy 를 이용한 데이터베이스 처리❓
우리는 기존 사용했던 파이썬 sqlite3 모듈을 사용해서 DB에 값을 넣는 등 쿼리를 '직접' 날려서 데이터 베이스와 소통했습니다.
파이썬에서 그런 방식으로 데이터베이스를 다룰 수 있다는 것은 좋은일이지만, 복잡하다는 단점이 있습니다.
따라서 이번에는 SQLAlchemy를 사용해 볼 겁니다. 하지만 SQLAlchemy를 알기 전에는 ORM이란 개념을 알아야 합니다.
ORM은 간단하게 객체와 데이터베이스의 데이터를 매핑해주는 것을 의미하며, SQLAlchemy는 이러한 ORM의 한 종류 입니다.
앞서 기존에는 쿼리를 직접 날려서 데이터베이스를 다뤘지만, 지금은 쿼리를 직접 날리는게 아닌 파이썬 클래스로 데이터베이스를 다루게 될 겁니다.
먼저, 우리는 폼으로부터 이메일, 비밀번호 등의 정보를 받아왔습니다. 그리고 그 데이터들을 데이터베이스에 넣어야 회원가입이라는 절차가 마무리 됩니다. 따라서 유저 테이블을을 만들어야 합니다.
먼저 데이터베이스 설정을 하기 위해 __init__.py를 수정해 주겠습니다.
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from os import path
from flask_login import LoginManager
from pprint import pprint
#db설정하기
db = SQLAlchemy()
DB_NAME = "blog_db"
# app을 만들어주는 함수를 지정
def create_app():
app = Flask(__name__) # Flask app 만들기
app.config['SECRET_KEY'] = "IFP"
#db 설정하기
app.config['SQLALCHEMY_DATABASE_URI'] = f'sqlite:///{DB_NAME}'
db.init_app(app)
from .views import views # blueprint 등록, '/blog를 기본으로 함'
from .auth import auth
app.register_blueprint(views, url_prefix ="/blog")
app.register_blueprint(auth, url_prefix="/blog")
return app
db 설정하기 아래의 코드를 추가로 넣어주었습니다.
두번째로 데이터베이스가 존재하지 않는다면 생성하는 코드를 create__app 함수 바로 아래에 추가해 줍니다
이제는 파이썬 클래스로 테이블을 만드는 작업을 해줄겁니다. 먼저 models.py 파일을 만들어 아래 코드를 작성해봅시다.
코드
from . import db
from flask_login import UserMixin
from sqlalchemy.sql import func
class User(db.Model, UserMixin):
id = db.Column(db.Integer, primary_key=True) # id : 유일 키, Integer
email = db.Column(db.String(150), unique=True) # email : 같은 이메일을 가지고 있는 유저가 없도록 함, String
username = db.Column(db.String(150), unique=True) # username : 같은 이름을 가지고 있는 유저가 없도록 함, String
password = db.Column(db.String(150)) # password : 비밀번호, String
created_at = db.Column(db.DateTime(timezone=True), default=func.now()) # 생성일자, 기본적으로 현재가 저장되도록 함
코드를 보자면 id를 기본키로 설정해주고, email,username은 중복이 없도록 unique를 사용해주었습니다.
마지막으로 데이터베이스가 만들어지기 전 models.py에서 작성한 유저 모델을 먼저 등록해야 하므로 __init__.py에 코드를 추가해줍니다
from .models import User
create_database(app)
Flask-LoginManager() 사용해보기
flask_login 라이브러리는 플라스크에서 로그인 기능을 쉽게 구현할 수 있는 라이브러리 입니다.
앞서 작성한 models.py에 UserMixin은 플라스크 로그인에서 수행하는 메소드에 대한 기본구현을 제공해 줍니다.
아래 두 줄의 코드로 로그인이 필요한 곳에 로그인하지 않은 유저가 접근할 경우 로그인 페이지로 리다이렉트 될 수 있도록 해 줍니다.
아래의 코드를 넣어주면 로그인의 준비는 끝이 납니다.
데이터 검증을 위한 라이브러리를 설치해 줍시다.(이메일, 유저네임 중복 여부 확인 등 유효한 데이터인지 검증할 수 있도록)
pip install flask-wtf
pip install email-validator
두개의 명령을 터미널에 입력해주면 설치는 완료 됩니다.
회원가입 처리하기
forms.py를 만들어주고 아래의 코드를 넣어줍니다
from flask_wtf import FlaskForm
from wtforms import StringField, TextAreaField, PasswordField, EmailField
from wtforms.validators import DataRequired, Email, Length, EqualTo
class SignupForm(FlaskForm):
# email : 필수 입력 항목이며, 이메일의 형식을 유지해야 함.
email = EmailField('email', validators=[DataRequired(), Email()])
# username : 필수 입력 항목이며, 최소 5글자부터 최대 30글자까지 허용됨.
username = StringField('username', validators=[DataRequired(), Length(4, 30)])
# password1 : 필수 입력 항목이며, 최소 8글자부터 최대 30글자까지 허용됨, password2와 값이 같아야 함.
password1 = PasswordField('password', validators=[DataRequired(), Length(8, 30), EqualTo("password2", message="Password must match...")])
password2 = PasswordField('password again', validators=[DataRequired()])
auth.py의 코드를 수정해줍니다
from flask import Blueprint, render_template, redirect, request, flash, url_for
from . import db
from blog.forms import SignupForm
from blog.models import User
auth = Blueprint("auth", __name__)
@auth.route("/login",methods=['GET','Post'])
def login():
return render_template("login.html")
@auth.route("/logout")
def logout():
return redirect("views.blog_home")
@auth.route("/sign-up", methods=['GET', 'POST']) # 회원가입에서 POST 요청을 처리해야 함.
def signup():
form = SignupForm()
if request.method == "POST" and form.validate_on_submit():
# 폼으로부터 검증된 데이터 받아오기
signup_user = User(
email=form.email.data,
username=form.username.data,
password=form.password1.data,
)
# 폼에서 받아온 데이터가 데이터베이스에 이미 존재하는지 확인
email_exists = User.query.filter_by(email=form.email.data).first()
username_exists = User.query.filter_by(username=form.username.data).first()
# 이메일 중복 검사
if email_exists:
flash('Email is already in use...', category='error')
# 유저네임 중복 검사
elif username_exists:
flash('Username is already in use...', category='error')
# 위의 모든 과정을 통과한다면, 폼에서 받아온 데이터를 새로운 유저로서 저장
else:
db.session.add(signup_user)
db.session.commit()
flash("User created!!!")
return redirect(url_for("views.home")) # 저장이 완료된 후 home으로 리다이렉트
# GET요청을 보낸다면 회원가입 템플릿을 보여줌
return render_template("signup.html", form=form)
마지막으로 폼 코드 아래에 {{ form.csrf_token }}를 추가해주어야 폼이 작동합니다
views.home에는 각자 views에서 설정해둔 home함수의 이름을 적어주면 됩니다. (필자는 blog_home)
비밀번호 해상하기
지금까지 작업으로 폼에서 받아온 데이터를 DB에 저장이 가능해졌습니다. 하지만 우리는 비밀번호를 hashing해 주어야합니다. 여기서 hashing이란 비밀번호를 알 수 없는 값, 즉 암호화 해주는 것을 말합니다
비밀번호를 저장하기 전 해싱하기 위해 from werkzeug.security import generate_password_hash, check_password_hash를 입력해줍니다
그리고 폼에서 받아온 데이터를 generate_password_hash()메서드를 사용해 해싱해줍니다
이제 실제로 입력해보겠습니다
각 항목별로 입력한 후 sign up을 누른다면
이렇게 초기화가 진행되고
이런 식으로 db에 저장이 된 후 home 화면으로 이동하게 됩니다
로그인 처리하기
forms.py에 로그인 폼 코드를 작성해 줍니다.
다음은 auth.py의 로그인 부분입니다
@auth.route("/login", methods=['GET', 'POST']) # 로그인에서 POST 요청을 처리해야 함.
def login():
form = LoginForm()
if request.method == "POST" and form.validate_on_submit():
# 폼으로부터 검증된 데이터 받아오기
password = form.password.data
# 폼에서 받아온 이메일로 유저 찾기
user = User.query.filter_by(email=form.email.data).first()
# 로그인 폼에서 입력된 이메일이 존재한다면,
if user:
if check_password_hash(user.password, password):
flash("Logged in!", category='success')
login_user(user, remember=True)
return redirect(url_for('views.home'))
else:
flash("Password is incorrect!", category='error')
# 로그인 폼에서 입력된 이메일이 존재하지 않는다면,
else:
flash("Email does not exist...", category='error')
return render_template("login.html", form=form)
앞서 진행했던 대로 login template에도 {{ form.csrf_token }}을 적어줍시다
로그아웃
오류 메시지 나타내기
base.html의 <nav>태그 아래 {%block header%}{%endblock%} 아래 이런 코드를 적어줍니다
{% with messages = get_flashed_messages(with_categories=True) %}
{% if messages %}
{% for category, message in messages %}
{# 카테고리 == error 이라면, 실패 메시지를 출력 #}
{% if category == "error" %}
<div class="alert alert-danger alert-dismissable fade show" role="alert" style="text-align: center">
{{ message }}
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
</div>
{# 그렇지 않다면, 성공 메시지를 출력 #}
{% else %}
<div class="alert alert-success alert-dismissable fade show" role="alert" style="text-align: center">
{{ message }}
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
</div>
{% endif %}
{% endfor %}
{% endif %}
{% endwith %}
위의 코드를 입력하고 메시지를 확인해 봅시다
성공 시 로그인 문구가 나옵니다.
실패한다면 이런 오류 메시지가 출력됩니다.
추가적으로 블루프린트를 수정해 줍니다. __init__.py를 열고
이 부분을 추가해 준 뒤, views.py를 열고
수정해주면 완성된다
동적으로 변화하는 navbar 만들기
로그인을 했을 경우 "Welcome! 닉네임" 처럼 이름이 뜨도록 하거나 로그인 링크가 보이지 않도록 설정을 해 주어야 합니다.
navbar 안에 리스트를 만들어줍니다
현재 로그인한 유저의 정보를 넘겨주기 위해 user=current_user를 추가해줍니다
또한 네비게이션 바는 base.html에 포함되므로 base.html이 있는 모든 템플릿에 user가 변수로서 전달되어야 합니다.
따라서 views와 auth를 수정해 줍니다
views
from flask import Blueprint, render_template
from flask_login import current_user
views = Blueprint("views", __name__)
@views.route("/")
@views.route("/home")
def blog_home():
return render_template("index.html", user=current_user)
@views.route("/about")
def about_me():
return render_template("about.html", user=current_user)
@views.route("/contact")
def contact():
return render_template("contact.html", user=current_user)
@views.route("/categories-list")
def categories_list():
return render_template("categories-list.html", user=current_user)
auth
from flask import Blueprint, render_template, redirect, request, flash, url_for
from flask_login import login_user, login_required, logout_user, current_user
from werkzeug.security import generate_password_hash, check_password_hash
from . import db
from blog.forms import SignupForm, LoginForm
from blog.models import User
auth = Blueprint("auth", __name__)
@auth.route("/login", methods=['GET', 'POST']) # 로그인에서 POST 요청을 처리해야 함.
def login():
form = LoginForm()
if request.method == "POST" and form.validate_on_submit():
# 폼으로부터 검증된 데이터 받아오기
password = form.password.data
# 폼에서 받아온 이메일로 유저 찾기
user = User.query.filter_by(email=form.email.data).first()
# 로그인 폼에서 입력된 이메일이 존재한다면,
if user:
if check_password_hash(user.password, password):
flash("Logged in!", category='success')
login_user(user, remember=True)
return redirect(url_for('views.blog_home'))
else:
flash("Password is incorrect!", category='error')
# 로그인 폼에서 입력된 이메일이 존재하지 않는다면,
else:
flash("Email does not exist...", category='error')
return render_template("login.html", form=form, user=current_user)
@auth.route("/logout")
@login_required
def logout():
logout_user()
return redirect(url_for("views.home")) # 로그아웃하면 views의 blog_home으로 리다이렉트됨
@auth.route("/sign-up", methods=['GET', 'POST']) # 회원가입에서 POST 요청을 처리해야 함.
def signup():
form = SignupForm()
if request.method == "POST" and form.validate_on_submit():
# 폼으로부터 검증된 데이터 받아오기
signup_user = User(
email=form.email.data,
username=form.username.data,
password=generate_password_hash(form.password1.data),
)
# 폼에서 받아온 데이터가 데이터베이스에 이미 존재하는지 확인
email_exists = User.query.filter_by(email=form.email.data).first()
username_exists = User.query.filter_by(username=form.username.data).first()
# 이메일 중복 검사
if email_exists:
flash("이메일이 이미 존재합니다...", category='error')
# 유저네임 중복 검사
elif username_exists:
flash("유저네임이 중복됩니다...", category='error')
# 위의 모든 과정을 통과한다면, 폼에서 받아온 데이터를 새로운 유저로서 저장
else:
db.session.add(signup_user)
db.session.commit()
flash("User created!!!")
return redirect(url_for("views.home")) # 저장이 완료된 후 home으로 리다이렉트
# GET요청을 보낸다면 회원가입 템플릿을 보여줌
return render_template("signup.html", form=form, user=current_user)
마지막으로 base.html 부분에 아까 추가했던 곳을 수정해 줍니다
완성되면
네비게이션 바가 변화하는 것을 볼 수 있습니다.
로그아웃 하게 된다면 다시 이렇게 바뀝니다
'Python > FLASK' 카테고리의 다른 글
직렬화/ 역직렬화 처리하기 (0) | 2022.09.12 |
---|---|
간단한 HTTP API 구축해보기 (0) | 2022.08.24 |
FLASK - 3주차 블로그 만들기 - 기초 (0) | 2022.07.15 |
파이썬 FLASK - Python DB (0) | 2022.07.10 |
파이썬 FLASK - 라우터, 변수 규칙, 후행 슬래시 (0) | 2022.07.10 |