본문 바로가기

Django

[Django] 회원가입, 로그인 구현

웹 개발시 필수 기능인 인증 기능을 만들겠다.

인증 기능은 일반적으로 로그인시 인증을 하는 것 외에도 로그인한 사용자에 대한 권한부여까지 포함한다.

장고 엔진 내부에서는 웹 요청에 따른 사용자 식별, 사용자별 세션 할당 및 관리 기능도 수행한다.

이런 세션 처리 기능도 인증 기능에 포함된다.

로그인, 로그아웃, 회원가입, 비밀번호 변경 등의 기본적인 인증 기능을 개발한다.

 

장고에 포함된 django.contrib.auth 앱이 인증 기능을 담당한다.

장고에서는 User 테이블을 기본으로 제공한다.

모든 웹 애플리케이션에서 공통으로 사용하는 테이블이기 때문이다.

장고의 인증 기능을 담당하는 auth 앱은 User 테이블 이외에도

Group, Permission 등의 테이블을 정의하고 있다.

 

장고의 인증 기능을 보면 URL과 뷰는 이미 개발되어 있고, 템플릿은 템플릿 파일명만 정해져있으므로

그 템플릿 내용은 개발자가 코딩해야한다.

# setting.py
LOGIN_REDIRECT_URL = '/'

폼을 장식하는데 유용한 django-widget-tweaks앱을 등록해야 한다.

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',

    # shkim
    'taggit.apps.TaggitAppConfig',
    'taggit_templatetags2',
    'widget_tweaks',

    'bookmark.apps.BookmarkConfig',
    'blog.apps.BlogConfig',
    'photo.apps.PhotoConfig',
]

 

urls.py 수정

# urls.py

from mysite.views import HomeView, UserCreateView, UserCreateDoneTV
urlpatterns = [
    path('admin/', admin.site.urls),
    # shkim
    path('accounts/', include('django.contrib.auth.urls')), 
    path('accounts/register/', UserCreateView.as_view(), name='register'), 
    path('accounts/register/done/', UserCreateDoneTV.as_view(), name='register_done'),

    path('', HomeView.as_view(), name='home'),
    path('bookmark/', include('bookmark.urls')),
    path('blog/', include('blog.urls')),
    path('photo/', include('photo.urls')),

] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

계정등록, 가입 처리를 수행하는 뷰를 임포트한다. UserCreateView는 계정을 추가하는 뷰이고, UserCreateDoneTV는 계정 생성이 완료된 후에 보여줄 화면을 처리하는 뷰이다.

장고의 인증은 URLConf를 가져와서 사용한다.

 

views.py 수정

from django.views.generic import TemplateView
from django.views.generic import CreateView 
from django.contrib.auth.forms import UserCreationForm 
from django.urls import reverse_lazy


#--- Homepage
class HomeView(TemplateView):
    template_name = 'home.html'


#--- User Creation
class UserCreateView(CreateView): 
    template_name = 'registration/register.html' 
    form_class = UserCreationForm 
    success_url = reverse_lazy('register_done') 


class UserCreateDoneTV(TemplateView): 
    template_name = 'registration/register_done.html'

UserCreationForm : User 모델의 객체를 생성하기 위해 보여주는 폼이다. 장고에서 기본으로 제공하는 폼이다

revers_lazy : 인자로 URL 패턴명을 받는다. URL 패턴명을 인식하기 위해서는 urls.py 모듈이 메모리에 로딩되어야한다. 지금 작성하고 있는 views.py 모듈이 로딩되고 처리되는 시점에 urls.py 모듈이 로딩되지 않을 수도 있으므로 reverse_lazy 함수를 임포트하였다.

UserCreateView : /accouts/register/ URL을 처리하는 뷰이다.

 

Templates 코딩하기

# base.html

<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <title>{% block title %}Django Web Programming{% endblock %}</title>

    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css">

    {% block extra-style %}{% endblock %}
</head>

<body style="padding-top:90px;">

<nav class="navbar navbar-expand-lg navbar-dark bg-primary fixed-top">
    <span class="navbar-brand mx-5 mb-0 font-weight-bold font-italic">Django - Python Web Programming</span>
    <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent">
        <span class="navbar-toggler-icon"></span>
    </button>

    <div class="collapse navbar-collapse" id="navbarSupportedContent">
        <ul class="navbar-nav mr-auto">
            <li class="nav-item mx-1 btn btn-primary">
                <a class="nav-link text-white" href="{% url 'home' %}">Home</a></li>
            <li class="nav-item mx-1 btn btn-primary">
                <a class="nav-link text-white" href="{% url 'bookmark:index' %}">Bookmark</a></li>
            <li class="nav-item mx-1 btn btn-primary">
                <a class="nav-link text-white" href="{% url 'blog:index' %}">Blog</a></li>
            <li class="nav-item mx-1 btn btn-primary">
                <a class="nav-link text-white" href="{% url 'photo:index' %}">Photo</a></li>

            <li class="nav-item dropdown mx-1 btn btn-primary">
                <a class="nav-link dropdown-toggle text-white" href="#" data-toggle="dropdown">Util</a>
                <div class="dropdown-menu">
                    <a class="dropdown-item" href="{% url 'admin:index' %}">Admin</a>
                    <div class="dropdown-divider"></div>
                    <a class="dropdown-item" href="{% url 'blog:post_archive' %}">Archive</a>
                    <a class="dropdown-item" href="{% url 'blog:search' %}">Search</a>
                </div>
            </li>
        </ul>

        <form class="form-inline my-2" action="" method="post"> {% csrf_token %}
            <input class="form-control mr-sm-2" type="search" placeholder="global search" name="search_word">
        </form>

        <ul class="navbar-nav ml-5 mr-5">
            <li class="nav-item dropdown mx-1 btn btn-primary">
                {% if user.is_active %}
                <a class="nav-link dropdown-toggle text-white" href="#" data-toggle="dropdown">
            	    <i class="fas fa-user"></i>&ensp;{% firstof user.get_short_name user.get_username %}&ensp;</a>
                <div class="dropdown-menu">
            	    <a class="dropdown-item" href="{% url 'logout' %}">Logout</a>
            	    <a class="dropdown-item" href="{% url 'password_change' %}">Change Password</a>
                </div>
                {% else %}
                <a class="nav-link dropdown-toggle text-white" href="#" data-toggle="dropdown">
            	    <i class="fas fa-user"></i>&ensp;Anonymous&ensp;</a>
                <div class="dropdown-menu">
            	    <a class="dropdown-item" href="{% url 'login' %}">Login</a>
            	    <a class="dropdown-item" href="{% url 'register' %}">Register</a>
                </div>
                {% endif %}
            </li>
        </ul>

    </div>
</nav>


<div class="container">
    {% block content %}{% endblock %}
</div>


{% block footer %}{% endblock %}


<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js"></script>
<script src="https://kit.fontawesome.com/c998a172fe.js"></script>

{% block extra-script %}{% endblock %}

</body>
</html>
# logged_out.html
{% extends "base.html" %}

{% block title %}logged_out.html{% endblock %}

{% block content %}

    <h1>Logged out</h1>
    <br>

    <div>
        <i class="fas fa-quote-left"></i>
        <span class="h6">&ensp;Thanks for spending your quality time with this web site today.&ensp;</span>
        <i class="fas fa-quote-right"></i>
    </div>

    <p class="font-italic"><a href="{% url 'login' %}">Log in again</a></p>

{% endblock %}
#login.html
{% extends "base.html" %}
{% load widget_tweaks %}

{% block title %}login.html{% endblock %}

{% block content %}

    <h1>Please Login</h1>
    <p class="font-italic">Please enter your id and password.</p>

    {% if form.errors %}
    <div class="alert alert-danger">
        <div class="font-weight-bold">Wrong! Please correct the error(s) below.</div>
        {{ form.errors }}
    </div>
    {% endif %}

    <form action="." method="post" class="card pt-3">{% csrf_token %}

        <div class="form-group row">
            {{ form.username|add_label_class:"col-form-label col-sm-2 ml-3 font-weight-bold" }}
            <div class="col-sm-5">
                {{ form.username|add_class:"form-control"|attr:"autofocus" }}
            </div>
        </div>
        <div class="form-group row">
            {{ form.password|add_label_class:"col-form-label col-sm-2 ml-3 font-weight-bold" }}
            <div class="col-sm-5">
                {{ form.password|add_class:"form-control" }}
            </div>
        </div>

        <div class="form-group">
            <div class="offset-sm-2 col-sm-5">
                <input type="submit" value="Log in" class="btn btn-info"/>
                <input type="hidden" name="next" value="{{ next }}" />
            </div>
        </div>

    </form>

{% endblock %}
#password_change_done.html
{% extends "base.html" %}

{% block title %}password_change_done.html{% endblock %}

{% block content %}

    <h1>{{ title }}</h1> 
    <br>

    <p>Your password was changed!</p> 

{% endblock %}
#password_change_form.html
{% extends "base.html" %}
{% load widget_tweaks %}

{% block title %}password_change_form.html{% endblock %}

{% block content %}

    <h1>{{ title }}</h1>
    <p class="font-italic">Please enter your old password for security's sake,
        and then enter your new password twice.</p>

    {% if form.errors %}
    <div class="alert alert-danger">
        <div class="font-weight-bold">Wrong! Please correct the error(s) below.</div>
        {{ form.errors }}
    </div>
    {% endif %}

    <form action="." method="post" class="card pt-3">{% csrf_token %}

        <div class="form-group row">
            {{ form.old_password|add_label_class:"col-form-label col-sm-3 ml-3 font-weight-bold" }}
            <div class="col-sm-5">
                {{ form.old_password|add_class:"form-control"|attr:"autofocus" }}
            </div>
        </div>
        <div class="form-group row">
            {{ form.new_password1|add_label_class:"col-form-label col-sm-3 ml-3 font-weight-bold" }}
            <div class="col-sm-5">
                {{ form.new_password1|add_class:"form-control" }}
            </div>
        </div>
        <div class="form-group row">
            {{ form.new_password2|add_label_class:"col-form-label col-sm-3 ml-3 font-weight-bold" }}
            <div class="col-sm-5">
                {{ form.new_password2|add_class:"form-control" }}
            </div>
        </div>

        <div class="form-group">
            <div class="offset-sm-3 col-sm-5">
                <input type="submit" value="Password change" class="btn btn-info"/>
            </div>
        </div>

    </form>

{% endblock %}
#register_done.html
{% extends "base.html" %}

{% block title %}register_done.html{% endblock %}

{% block content %}

    <h1>Registration Completed Successfully</h1> 
    <br>

    <p>Thank you for registering.</p> 

    <p class="font-italic"><a href="{% url 'login' %}">Log in</a> </p>  

{% endblock %}
#register.html
{% extends "base.html" %}
{% load widget_tweaks %}

{% block title %}register.html{% endblock %}

{% block content %}

    <h1>New User Registration</h1>
    <p class="font-italic">Please enter your username and password twice.</p>

    {% if form.errors %}
    <div class="alert alert-danger">
        <div class="font-weight-bold">Wrong! Please correct the error(s) below.</div>
        {{ form.errors }}
    </div>
    {% endif %}

    <form action="." method="post" class="card pt-3">{% csrf_token %}

        <div class="form-group row">
            {{ form.username|add_label_class:"col-form-label col-sm-3 ml-3 font-weight-bold" }}
            <div class="col-sm-5">
                {{ form.username|add_class:"form-control"|attr:"autofocus" }}
            </div>
        </div>
        <div class="form-group row">
            {{ form.password1|add_label_class:"col-form-label col-sm-3 ml-3 font-weight-bold" }}
            <div class="col-sm-5">
                {{ form.password1|add_class:"form-control" }}
            </div>
        </div>
        <div class="form-group row">
            {{ form.password2|add_label_class:"col-form-label col-sm-3 ml-3 font-weight-bold" }}
            <div class="col-sm-5">
                {{ form.password2|add_class:"form-control" }}
            </div>
        </div>

        <div class="form-group">
            <div class="offset-sm-3 col-sm-5">
                <input type="submit" value="Register" class="btn btn-info"/>
            </div>
        </div>

    </form>

{% endblock %}

'Django' 카테고리의 다른 글

[Django] 콘텐츠 편집 기능 개발  (0) 2022.08.01
[Django] Photo 앱 개발  (0) 2022.08.01
[Django] Blog 앱 확장 - 검색 기능  (0) 2022.07.31
[Django] Blog 앱 확장 - 댓글 달기  (0) 2022.07.30
[Django] Blog 앱 확장 - Tag 달기  (0) 2022.07.30