1. Django Tutorial(Airbnb) - Login & Logout (View & FormView)

ID짱재·2021년 8월 11일
1

Django

목록 보기
19/43
post-thumbnail

🌈 Login & Logout 기능(View & FormView)

🔥 View를 상속받아 Login & Logout구현

🔥 FormView를 상속받아 Login & Logout구현


1. View를 상속받아 Login & Logout구현

1) is_valid()

  • 경로 config/urls.py에서 include를 통해 users/urls.py로 연결시켜주고, View를 상속받은 LoginView와 매핑시켰어요.
    • 🔎 config/urls.py : path("users/", include("users.urls", namespace="users")),
    • 🔎 users/urls.py : path("login/", views.LoginView.as_view(), name="login"),
  • View를 get방식과 post방식을 사용할 때, 아래처럼 get, post매서드를 통해 분리합니다. 이를통해, GET방식으로 요청된다면 입력할 수 있는 form을 제공하고, POST방식으로 요청된다면 입력된 데이터를 처리해줄 수 있어요.
  • POST 방식으로 전달된 데이터를 확인하기 위해서 2가지(is_valid, cleaned_data) 처리가 필요해요.
    • 1) is_valid() : form에서 실시한 유효성 검사를 Bool 형태로 반환
    • 2) cleaned_data() : 사용자가 입력한 데이터를 Dict형으로 전달
# urser/views.py
from django.shortcuts import render
from django.views import View
from . import forms
class LoginView(View):
    def get(self, request):
        form = forms.LoginForm()
        context = {"form": form}
        return render(request, "users/login.html", context) # 👈 빈 form을 제공해줍니다.
    def post(self, request):
        form = forms.LoginForm(request.POST) # 👈 사용자의 입력값을 유지
        # print(form.is_valid()) # True
        if form.is_valid():  # 👈 유효성 검사 결과가 True라면,,
            print(form.cleaned_data) # {'email': 'jewon119@kakao.com', 'password': '123456789'}
        context = {"form": form}
        return render(request, "users/login.html", context)
  • Django Form을 간단히 만들어볼께요. 현재 비밀번호가 노출되고 이썽요. 비밀번호를 form에 노출되지 않도록 하기 위해서는 form에서 "widget=forms.PasswordInput"을 사용합니다. widget 기능은 form 다양한 style format을 제공해줍니다.
# users/forms.py
from django import forms
from . import models
class LoginForm(forms.Form): # 👈 가장 기본적인 form인 Form을 상속받아옵니다.
    """Login Form Definition"""
    email = forms.EmailField()
    password = forms.CharField(widget=forms.PasswordInput) # 👈 widget을 이용하면 form의 style을 변경할 수 있어요:)
  • email로 로그인하는 form을 템플릿에 적용시켜보죠. 템플릿에서 {{form.as_p}}을 위치시키면 입력 form이 나타납니다!
  • 서버의 보안을 위해 POST로 데이터를 전송할 때는 {% csrf_token %}을 반드시 넣어줘야해요. 넣지 않으면 에러가 발생합니다.
{% extends "base.html" %}
    {% block page_title %}
        Login
    {% endblock page_title %}
    {% block search-bar %} # 👈 search_bar를 덮어씌어 나타나지 않게 해줘요:)
    {% endblock search-bar %}
    {% block content %}
        <form method="post" action="{% url "users:login" %}">
            {% csrf_token %} # 👈 POST로 데이터를 전송할 때 보안을 위해 반드시 필요해요!
            {{form.as_p}}
            <button>Login</button>
        </form>    
    {% endblock content %}

2) clean() & checked_password()

  • 사용자가 입력한 데이터를 forms.py으로 가져와 DB에 값과 일치하는지 검증하기위해, "clean" 매서드를 사용합니다. "clean_필드명"통해 필드마다 검증하기 위한 매서드를 만들 수도 있고, 필요에 따라 "clean" 매서드 안에서 모두 처리할 수 있도록 할 수 있습니다.
  • check_password()를 사용하면, 입력된 사용자의 비밀번호와 DB의 비밀번호가 일치한지 검증시켜줍니다. 일치하면 View로 값을 return해주고, 일치하지않으면 에러를 발생시켜 줄 수 있도록 처리하였어요.
  • clean 매서드는 유효성 검증뿐 아니라 검증 결과에 문제가 있다면, 오류 메시지를 템플릿에 쉽게 전달할 수 있습니다.
  • ValidationError()만 사용하면, 오류메시지를 해당 필드에 위치시킬 수 없기 때문에 add_error()를 함께 사용해볼께요.
    • 🔎 self.add_error("필드명", forms.ValidationError("error 메시지"))
  • clean 매서드로 처리한 뒤 값을 return해주지 않으면, View에서 "cleaned_data"의 값이 None을 가지기 때문에 form에서 처리 후 "self.cleaned_data"를 return해주어야 해요. self.cleaned_data을 return하면, 검증을 마친 정제된 email과 password 값이 View로 전달됩니다!
    • 🔎 return self.cleaned_data
from django import forms
from . import models
class LoginForm(forms.Form):
    """Login Form Definition"""
    email = forms.EmailField()
    password = forms.CharField(widget=forms.PasswordInput)
    def clean(self):
        email = self.cleaned_data.get("email") # 👈 유효성 검사를 진행한 email field에 값을 추출합니다.
        password = self.cleaned_data.get("password") # 👈 유효성 검사를 진행한 password field에 값을 추출합니다.
        try:
            user = models.User.objects.get(email=email) # 👈 emil을 기준으로 해당 Object를 가져와요!
            if user.check_password(password):  # 👈 비밀번호가 서로 일치하다면 True, 아니면 False 반환
                return self.cleaned_data
            else:
                self.add_error("password", forms.ValidationError("Password is wrong"))
        except models.User.DoesNotExist:
            self.add_error("email", forms.ValidationError("User does not exist"))

3) authenticate()

  • 사용자가 입력한 email과 password가 DB의 데이터와 일치하는지 form에서 검증했기 떄문에, View에서 검증을 마친 정제된 값을 다룰 수 있어요. 이 때, Django의 "authenticate"을 사용하여 인증 기능을 진행합니다.
  • authenticate는 아래 위치에 있습니다. login, logout 기능도 사용하기 위해 함께 import해줍니다.
    • 🔎 from django.contrib.auth import authenticate, login, logout
  • form에서 검증한 email과 password 값을 authenticate의 username과 password 값으로 전달해주면, Django에서 알아서 쿠키를 생성하고 인증을 진행합니다.
    • 🔎 user = authenticate(request, username=email, password=password)
  • 인증이 되었다면, login 매서드를 사용하여 request와 인증완료된 사용자의 Object를 전달하고, redirect로 hom으로 진입하게 합니다!
    • 🔎 login(request, user)
  • 유효하지 않았을 때, form을 context로 템플릿에 전달하는 이유는 form.py에서 탐지한 에러 메시지를 템플릿으로 전달하기 위함입니다:)
  • Logout은 사용자가 Logout 버튼을 클릭할 때 호출되도록 FBC로 만들고, logout함수에 사용자의 요청(request)만 전달하면 쉽게 처리할 수 있어요:)
from django.views import View
from django.shortcuts import render, redirect, reverse
from django.contrib.auth import authenticate, login, logout # 👈 authenticate import 
from . import forms
class LoginView(View):
    def get(self, request):
        form = forms.LoginForm()
        context = {"form": form}
        return render(request, "users/login.html", context)
    def post(self, request):
        form = forms.LoginForm(request.POST)
        if form.is_valid():
            email = form.cleaned_data.get("email")
            password = form.cleaned_data.get("password")
            user = authenticate(request, username=email, password=password)
            # print(user) # jewon119@kakao.com
            if user is not None:
                login(request, user)  # 👈 login함수에는 request와 인증한 객체를 전달해요:)
                return redirect(reverse("core:home"))
        context = {"form": form} # 👈 로그인이 안되었다면, form에 담긴 오류메시지가 템플릿으로 전달되요:)
        return render(request, "users/login.html", context)
def log_out(request):
    logout(request) # 👈 request만 보내면 logout을 해줘요:)
    return redirect(reverse("core:home"))
  • "partials/nav.html"에서 로그인이 되었다면 logout 버튼을,,, 로그아웃이된 상태라면 login 버튼이 나타날 수 있게 해야합니다. 템플릿에서 인증결과에 대해서 어떻게 알 수 있을까요? is_authenticated를 사용하면 됩니다.
  • 로그인 여부는 "user.is_authenticated"로 확인할 수 있는데, user을 템플릿으로 render하지 않았는데도 불구하고 사용할 수 있습니다. 이는 Django의 "context processor"가 자동으로 render해줬기 때문입니다:)
<a href="{% url "core:home" %}">Nbnb</a>
    <ul>
    {% if user.is_authenticated %}
        <li><a href="{% url "users:logout" %}">Log out</a></li>
    {% else %}
        <li><a href="{% url "users:login" %}">Log in</a></li>
    {% endif %}    
    </ul>
  • Login & Logout 기능 구현 후 최종적인 urls.py의 상태는 아래와 같습니다.
#users/urls.py
from django.urls import path
from . import views
app_name = "users"
urlpatterns = [
    path("login/", views.LoginView.as_view(), name="login"), # 👈 login
    path("logout/", views.log_out, name="logout"), # 👈 logout
]

2. FormView를 상속받아 Login & Logout구현

1) form_valid()

  • email 주소를 ID로하여 로그인을 구현하고자한다면 "FormView"를 상속받아 사용하는 것이 View를 사용하는것보다 편리합니다.
  • FormView의 위치는 아래와 같아요
    • 🔎 from django.views.generic import FormView
  • template_name은 render되는 html이에요. GET방식일 때, return되는 "users/login.html" 템플릿과 같죠.
  • form_class에믐 사용할 form을 지정해주면 됩니다.
  • success_url은 인증을 완료했을 때 이동할 경로를 지정해주면 되요. reverse_laze()는 요청이 들어올 때만 호출되는 게으른 reverse 버전입니다.
  • "FormView"를 상속받았다면, 내장된 매서드인 form_valid()를 통해 값의 유효성 검사를 진행합니다.
    • View 상속받은 경우 : 🔎 is_valid()
    • FormView 상속 시 : 🔎 form_valid()
  • "FormView"를 상속받았다면, form_valid 결과에 form을 담아 반환해주면 유효성 검사에 문제가 존재할 경우 에러를 표시해줍니다.
    • View 상속받은 경우 : 🔎 return render(request, "users/login.html", context)
    • FormView 상속 시 : 🔎 return super().form_valid(form)
from django.views.generic import FormView
from django.urls import reverse_lazy
from django.shortcuts import redirect, reverse
from django.contrib.auth import authenticate, login, logout
from . import forms
class LoginView(FormView):
    template_name = "users/login.html"
    form_class = forms.LoginForm
    success_url = reverse_lazy("core:home")
    def form_valid(self, form):
        email = form.cleaned_data.get("email")
        password = form.cleaned_data.get("password")
        user = authenticate(self.request, username=email, password=password)
        if user is not None:
            login(self.request, user)
        return super().form_valid(form) # 👈 super().form_valid(form)을 반환
def log_out(request):
    logout(request)
    return redirect(reverse("core:home"))
  • forms.py는 View를 사용할 때와 완전히 동일해요!
from django import forms
from . import models
class LoginForm(forms.Form):
    """Login Form Definition"""
    email = forms.EmailField()
    password = forms.CharField(widget=forms.PasswordInput)
    def clean(self):
        email = self.cleaned_data.get("email")
        password = self.cleaned_data.get("password")
        try:
            user = models.User.objects.get(email=email)
            if user.check_password(password):
                return self.cleaned_data
            else:
                self.add_error("password", forms.ValidationError("Password is wrong"))
        except models.User.DoesNotExist:
            self.add_error("email", forms.ValidationError("User does not exist"))
profile
Keep Going, Keep Coding!

0개의 댓글