반응형

회원가입 기능을 만들어 보았다면 웹 프로그래밍은 거의 마스터 했다고 할 수 있습니다.

 

from pybo import db

class Question(db.Model) :
    id = db.Column(db.Integer, primary_key = True)
    subject = db.Column(db.String(200), nullable = False)
    content = db.Column(db.Text(), nullable = False)
    create_date = db.Column(db.DateTime(), nullable = False)

class Answer(db.Model) :
    id = db.Column(db.Integer, primary_key = True)
    question_id = db.Column(db.Integer, db.ForeignKey('question.id', ondelete = 'cascade'))
    question = db.relationship('Question', backref=db.backref('answer_set', ))
    # question = db.relationship('Question', backref=db.backref('answer_set', casecade='all, delete-orphan'))
    content = db.Column(db.Text(), nullable = False)
    create_date = db.Column(db.DateTime(), nullable = False)

class User(db.Model) :
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(150), unique=True, nullable=False)
    password = db.Column(db.String(200), nullable=False)
    email = db.Column(db.String(120), unique=True, nullable=False)

models.py 입니다.

 

User 모델을 작성해주세요

 

flask db migrate 명령으로 리비전 파일을 생성하겠습니다.

 

이제 리비전 파일로 데이터베이스를 변경하겠습니다.

 

from flask_wtf import FlaskForm
from wtforms import StringField, TextAreaField, PasswordField
from wtforms.fields.html5 import EmailField
from wtforms.validators import DataRequired, Email, EqualTo, Length


class QuestionForm(FlaskForm):
    subject = StringField('제목', validators=[DataRequired('제목은 필수 입력 항목입니다.')])
    content = TextAreaField('내용', validators=[DataRequired('내용은 필수 입력 항목입니다.')])

class AnswerForm(FlaskForm):
    content = TextAreaField('내용', validators=[DataRequired('내용은 필수 입력 항목입니다.')])

class UserCreateForm(FlaskForm):
    username = StringField('사용자이름', validators=[
        DataRequired(), Length(min=3, max=25)])
    passowrd1 = PasswordField('비밀번호', validators=[
        DataRequired(), EqualTo('password2', '비밀번호가 일치하지 않습니다.')])
    password2 = PasswordField('비밀번호확인', validators=[DataRequired()])
    email = EmailField('이메일', [DataRequired(), Email()])

그리고 forms.py 검증하는 부분을 추가시키도록 하겠습니다.

회원가입에 필요한 것인데

userName에는 반드시 데이터를 입력하게 했고(DataRequired()) 길이를 제한했습니다. (Length(min=3, max=25)

password1에는 반드시 데이터를 입력하게 했고 (DataRequired()) 

             비밀번호확인하고 일치하는지 검증을 했습니다. (EqualTo('password2', '비밀번호가 일치하지 않습니다.'))

password2에는 반드시 데이터를 입력하게 했습니다. (DataRequired()) 

email에는 반드시 데이터를 입력하게 했고 (DataRequired())  이메일이 맞는지 확인하게했습니다. (Email())

 

email 검증을 사용하려면 email-validator가 필요하므로 설치해줍시다.

 

회원가입용 화면을 만들어 보겠습니다. 

메인 뷰 (main_views.py)와 답변 뷰 (answer_views.py)에 어디에도 해당하지 않으므로 새로운 파일을 만들겠습니다.

 

from flask import Blueprint, request, render_template, flash, url_for
from werkzeug.security import generate_password_hash
from werkzeug.utils import redirect

from .. import db
from pybo.forms import UserCreateForm
from myproject.pybo.models import User

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

@bp.route('/signup/', methods=('GET','POST'))
def signup():
    form = UserCreateForm()
    if request.method == 'POST' and form.validate_on_submit():
        user = User.query.filter_by(username=form.username.data).first()
        if not user:
            user = User(username=form.username.data,
                        password=generate_password_hash(form.password1.data),
                        email=form.email.data)
            db.session.add(user)
            db.session.commit()
            return redirect(url_for('main.index'))
        else:
            flash('이미 존재하는 사용자입니다.')
    return render_template('auth/signup.html', form=form)

pybo/views/auth_views.py 입니다.

 

/auth/가 URL 접두어이고 그와 연결된 /auth/singup 과 연결된 signup 함수를 만들었습니다.

POST방식 요청에는 계정 등록을 GET방식 요청에는 계정 등록을(??)

 

form = UserCreateForm() 검증 단계를 가진 객체를 가집니다.

 

요청되어진 method 방식이 POST(request.method == 'POST')이고

올바른 방식으로 폼형태가 들어오면 (form.validate_on_submit())

 

username(id) 이미 존재하는지 확인합니다. (User.query.filter_by(username=form.username.data).first())

query.filter_by select문 + where 절이라고 생각하시면 됩니다. 그 안에 적는 내용이 where절입니다.

== 가 아닌 이유는 filter_by의 경우는 ==을 = 취급합니다.

username == form.username.data 폼에 검증을 끝낸 데이터와 DB에 있는 username하고 같은게 있는지

확인하는 과정입니다. 그래서 있으면 무슨 값이 들어가겠죠?

 

if not user 를 통해 똑같은 아이디(username)이 없으면 그 아이디와 비밀번호와 이메일을 저장합니다.

비밀번호를 저장할 때는 hash 함수로 암호화하여 저장합니다.

generate_password_hash 함수는 암호화 데이터는 복호화할 수 없습니다.

 

만약 user에 값이 들어가면 이미 존재하는 사용자입니다.(flash('이미 존재하는 사용자입니다.') 오류를 발생시킵니다.

 

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
  {% extends "base.html" %}
  {% block content %}
  <div class="container my-3">
    <form method="post" class="post-form">
      {{ form.csrf_token }}
      {% include "form_errors.html" %}
      <div class="form-group">
        <label for="username">사용자 이름</label>
        <input type="text" class="form-control" name="username" id="username" value="{{ form.useranme.data or '' }}">
      </div>
      <div class="form-group">
        <label for="password1">비밀번호</label>
        <input type="password" class="form-control" name="password1" id="password1" value="{{ form.password1.data or '' }}">
      </div>
      <div class="form-group">
        <label for="password2">비밀번호 확인</label>
        <input type="password" class="form-control" name="password2" id="password2" value="{{ form.password2.data or '' }}">
      </div>
      <div class="form-group">
        <label for="email">비밀번호</label>
        <input type="text" class="form-control" name="eamil" id="email" value="{{ form.email.data or '' }}">
      </div>
      <button type="submit" class="btn btn-primary">생성하기</button>
    </form>
  </div>
{% endblock %}
</body>

</html>

templates/auth/signup.html 입니다.

 

회원가입 템플릿을 작성해주세요

그리고 회원 가입할 때 발생하는 오류를 표시하도록 했습니다.

 

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
  <!-- 필드 오류 -->
  {% for field, errors in form.errors.items() %}
      <div class="alert alert-danger" role="alert">
          <strong>{{ form[field].label }}</strong>: {{ ', '.join(errors) }}
      </div>
  {% endfor %}
      <!-- flash 오류 -->
      {% for message in get_flashed_messages() %}
      <div class="alert alert-danger" role="alert">
        {{ message }}
      </div>
  {% endfor %}
</body>
</html>

templates/form_errors.html 입니다.

 

필드 오류의 경우는 검증에서 틀린 부분을 이야기합니다. 비밀번호가 다르다, 덜 입력했다. 등...

 

flash('이미 존재하는 사용자입니다.') 이 부분을 처리해주는 부분이  get_flashed_messages() 입니다.

message 내용을 받아서 출력하면 flash 안에 있는 내용이 나오게 되는 것이죠

 

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <nav class="navbar navbar-expand-lg navbar-light bg-light">
      <div class="container-fluid">
        <a class="navbar-brand" href="{{ url_for('main.index') }}">Pybo</a>
        <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
          <span class="navbar-toggler-icon"></span>
        </button>
        <div class="collapse navbar-collapse" id="navbarNav">
          <ul class="navbar-nav">
            <li class="nav-item">
              <a class="nav-link" href="{{ url_for('auth.signup') }}">계정생성</a>
            </li>
            <li class="nav-item">
              <a class="nav-link" href="#">로그인</a>
            </li>
          </ul>
        </div>
      </div>
    </nav>
</body>
</html>

navbar.html 입니다.

 

회원가입 화면으로 이동할 수 있게 링크를 네비게이션 바에 추가했습니다.

 

 

맨 밑은 이메일입니다... 실수했네요

반응형