본문 바로가기

Python/Django

[Django Authentication] 회원가입, 로그인, 로그아웃 기능 구현 / 일일이 다 구현해보자

https://github.com/JisunParkRea/djangotube_tutorial

 

JisunParkRea/djangotube_tutorial

Simple video service which can upload youtube videos using django - JisunParkRea/djangotube_tutorial

github.com

참고

초보몽키의 개발공부로그

Using the Django authentication system

Django Girls Tutorial: Extensions

구현하고자 하는 기능

해당 포스트에서는 장고의 내장된 인증 기능 없이 view, urls 등을 일일이 다 구현해보았다.

  • 회원가입
    • username, email, password로 가입
    • 회원가입 시 가입 계정으로 로그인 된 후 video list 페이지로 넘어가기
  • 로그인
    • 로그인 후 video list 페이지로 넘어가기
    • 로그인 실패 시 login failed를 알리는 HttpResponse 보내기
  • 로그아웃
    • 로그아웃 후 video list 페이지로 넘어가기
  • Template
    • 로그인/회원가입 전에 video list 페이지 방문 시 로그인/회원가입 버튼 보여주기
    • 로그인 후 video list 우측 상단에 username을 표시하고, logout 링크, new video 버튼도 같이 보여주기
  • 추가적으로...
    • 새로운 video는 로그인된 사용자만 추가할 수 있도록 하자(화면에 new video 버튼이 안보이더라도, url을 알고 있다면 접근이 가능하기 때문에)

회원가입

forms.py

회원가입 시 사용자에게 입력 받을 ModelForm 작성

from django import forms
from django.contrib.auth.models import User

class UserForm(forms.ModelForm):

    class Meta:
        model = User
        fields = ['username', 'email', 'password']

views.py

회원가입 로직을 구현하자

  1. POST request인 경우
    • UserForm대로 사용자가 입력한 form을 받아서 그것이 유효하면 새로운 user을 만들고 login한 후 video_list 페이지로 redirect한다.
  2. POST request가 아닌 경우
    • 비어있는 UserForm을 반환한다
from django.shortcuts import render, redirect
from django.contrib.auth.models import User
from django.contrib.auth import login
from .forms import UserForm

def signup(request):
    if request.method == 'POST':
        form = UserForm(request.POST)
        if form.is_valid():
            new_user = User.objects.create_user(**form.cleaned_data)
            login(request, new_user)
            return redirect('video_list')
    else:
        form = UserForm()
        return render(request, 'video/user_new.html')

urls.py

urlpatterns = [
    path('signup/', views.signup, name='user_signup'),
]

templates/video/user_new.html

<html>

<head>
    <title>Sign up</title>
    <link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css">
    <link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap-theme.min.css">
</head>

<body>
    <div class="content container">
        <header class="page-header">
            <h1>Sign up</h1>
        </header>
        <div class="row">
            <div class="col-md-16">
                <form method="POST">
                    {% csrf_token %}
                    <div class="form-group">
                        <label for="username">Username</label>
                        <input type="text" name="username" class="form-control" id="username" placeholder="Username">
                    </div>
                    <div class="form-group">
                        <label for="email">Email</label>
                        <input type="text" name="email" class="form-control" id="email" placeholder="Email">
                    </div>
                    <div class="form-group">
                        <label for="password">Password</label>
                        <input type="text" name="password" class="form-control" id="password" placeholder="Password">
                    </div>
                    <button type="submit" class="btn btn-default">Submit</button>
                </form>
            </div>
        </div>
    </div>
</body>

</html>

로그인

forms.py

로그인 시 사용자에게 입력 받을 ModelForm 작성

class LoginForm(forms.ModelForm):

    class Meta:
        model = User
        fields = ['username', 'password']

views.py

로그인 로직을 구현하자

  1. POST request인 경우
    • LoginForm대로 사용자가 입력한 form을 받아서 username과 password를 authenticate한다
    • user이 유효하면, login하고 video_list 페이지로 redirect한다
  2. POST request가 아닌 경우
    • 비어있는 LoginForm을 반환한다
from django.http import HttpResponse
from django.contrib.auth import authenticate
from .forms import LoginForm

def signin(request):
    if request.method == 'POST':
        form = LoginForm(request.POST)
        username = request.POST['username']
        password = request.POST['password']
        user = authenticate(username = username, password = password)
        if user is not None:
            login(request, user)
            return redirect('video_list')
        else:
            return HttpResponse('Login failed. Try again.')
    else:
        form = LoginForm()
        return render(request, 'video/user_login.html')

urls.py

urlpatterns = [
    path('login/', views.signin, name='user_login'),
]

templates/video/user_login.html

<html>

<head>
    <title>Login</title>
    <link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css">
    <link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap-theme.min.css">
</head>

<body>
    <div class="content container">
        <header class="page-header">
            <h1>Login</h1>
        </header>
        <div class="row">
            <div class="col-md-16">
                <form method="POST">
                    {% csrf_token %}
                    <div class="form-group">
                        <label for="username">Username</label>
                        <input type="text" name="username" class="form-control" id="username" placeholder="Username">
                    </div>
                    <div class="form-group">
                        <label for="password">Password</label>
                        <input type="text" name="password" class="form-control" id="password" placeholder="Password">
                    </div>
                    <button type="submit" class="btn btn-default">Submit</button>
                </form>
            </div>
        </div>
    </div>
</body>

</html>

로그아웃

views.py

from django.contrib.auth import logout

def signout(request):
    logout(request)
    return redirect('video_list')

urls.py

urlpatterns = [
    path('logout/', views.signout, name='user_logout'),
]

템플릿 수정

templates/video/video_list.html

<html>

<head>
    <title>Video List</title>
    <link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css">
    <link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap-theme.min.css">
</head>

<body>
    <div class="content container">
        <header class="page-header" sytle="overflow: auto;">
            <h1 style="display: inline;">Video List</h1>
            {% if user.is_authenticated %}
                <a class="btn btn-default" href="/video/new" style="float: right;">New Video</a>
                <p style="float: right;"><span style = " font-size:1.5em; color: green;">Welcome, {{ user.username }}<small>(<a href="{% url 'user_logout' %}">Logout</a>)</small>&nbsp;&nbsp;</span></p>
            {% else %}
                <div style="float: right;">
                    <a class="btn btn-default" href="/video/signup">Sign up</a>
                    <a class="btn btn-default" href="/video/login">Login</a>
                </div>
            {% endif %}
        </header>
        <div class="row">
            <div class="col-md-12">
                {% for video in video_list %}
                <a href="/video/{{ video.id }}">
                    <h4>{{ video.title }}</h4>
                </a>
                {% endfor %}
            </div>
        </div>
    </div>
</body>

</html>

추가적으로...

로그인된 사용자만 새로운 비디오를 추가할 수 있게 하기

login_required decorator을 해당 view에 사용하면 된다.

from django.contrib.auth.decorators import login_required

@login_required
def video_new(request):
    if request.method == 'POST': # 새로운 비디오 데이터를 업로드할 때
        form = VideoForm(request.POST)
        form.save()
        return redirect('video_list')
    elif request.method == 'GET': # 새로운 비디오를 추가할 템플릿을 가져와야할 때
        return render(request, 'video/video_new.html')