728x90

장고 회원가입과 로그인 기능을 함수형 view 에서 class 형 view 로 변경하는 연습을 하고 코드를 적어둔다.

 

base.html

<!DOCTYPE html>
<html lang="en">
<head>
    <title>{% block title %}{% endblock %}</title>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/css/bootstrap.min.css">
    <script src="https://cdn.jsdelivr.net/npm/jquery@3.5.1/dist/jquery.slim.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.1/dist/umd/popper.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/js/bootstrap.bundle.min.js"></script>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
    {% block head %}
    {% endblock %}
</head>
<body>
<div class="container">
    {% block contents %}
    {% endblock %}
</div>
</body>
</html>

 

 

index.html

{% extends "base.html" %}
{% block title %}Home{% endblock %}
{% block contents %}
    <div>Hello world!</div>
    <div>{{ user.username }}</div>
    <div>{{ user }}</div>
{% endblock %}
 

 

urls.py

from django.contrib import admin
from django.urls import path,include
from . import views
 
urlpatterns = [
    path('admin/', admin.site.urls),
 
    path('', views.HomeView.as_view(), name='index'),
    path('user/', include('accounts.urls')),
]
 

 

HomeView

세션처리가 일반적인 케이스가 아니라서 여기서 개 삽질을 좀 했다.

로그인 세션 정보가 없으면 로그인 화면으로 자동 이동된다.

데이터베이스의 값을 템플릿에서 사용하려면 뷰에서 값을 전달해야 한다.

그때 context라는 형태로 템플릿에 값을 전달한다.

from django.views.generic import TemplateView
from django.utils.decorators import method_decorator
from accounts.decorators import login_required
import logging
from accounts.models import User
 
@method_decorator(login_required, name='dispatch')
class HomeView(TemplateView):
    template_name = 'index.html'
 
    def get_context_data(self**kwargs):
        user_id = self.request.session.get('user')
        if user_id:
            user = User.objects.get(pk=user_id)
            logging.warning(user.username)
            context = super(HomeView, self).get_context_data(**kwargs)
            context['user'= user
            return context
 

 

decorators.py

from django.shortcuts import redirect
from .models import User
 
def login_required(function):
    def wrap(request, *args, **kwargs):
        user = request.session.get('user')
        if user is None or not user:
            return redirect('/user/login')
        return function(request, *args, **kwargs)
 
    return wrap
 
 
def admin_required(function):
    def wrap(request, *args, **kwargs):
        user = request.session.get('user')
        if user is None or not user:
            return redirect('/login')
        
        user = User.objects.get(useremail=user)
        if user.level != 'admin':
            return redirect('/')
 
        return function(request, *args, **kwargs)
 
    return wrap
 

 

models.py and app urls.py, admin.py

함수형 뷰를 사용해도 되고, 클래스형 뷰를 사용해도 된다.

클래스형 뷰를 사용하기 위해서는 가장 먼저 urls.py 에서 클래스형 뷰를 사용한다는 걸 표시해줘야 한다.

# models.py
from django.db import models
 
class User(models.Model):
    username = models.CharField(max_length=32, verbose_name='사용자명')
    useremail = models.EmailField(max_length=128, verbose_name='emailID', unique=True)
    password = models.CharField(max_length=128,verbose_name='비밀번호')
    created_at = models.DateTimeField(auto_now_add=True, verbose_name='등록시간')
    updated_at = models.DateTimeField(auto_now=True)
 
    def __str__(self):
        return self.useremail
 
    class Meta:
        db_table = 'accounts'
        verbose_name = '사용자'
        verbose_name_plural = '사용자'
 
 
# app urls.py
from django.urls import path
from . import views
 
app_name = 'accounts'
urlpatterns = [
    # path('register/', views.register, name='ajax_register'), # ajax_register
    path('register/', views.UserRegisterView.as_view(), name='ajax_register'), # ajax_register
 
    # path('login/', views.login, name='ajax_login'),
    path('login/', views.UserLoginView.as_view(), name='ajax_login'),
 
    # path('logout/',views.logout),
    path('logout/',views.UserLogoutView.as_view()),
]
 
 
# admin.py
from django.contrib import admin
from .models import User
 
# Register your models here.
class UserAdmin(admin.ModelAdmin):
    list_display = ('username','useremail','password','created_at','updated_at')
 
admin.site.register(User,UserAdmin)
 

 

 

app views.py

from django.core.exceptions import ValidationError
from django.core.validators import validate_email
from django.http import HttpResponse, JsonResponse
from django.shortcuts import render, redirect
from django.contrib.auth.hashers import make_password, check_password
from django.utils.decorators import method_decorator
from django.views.generic import TemplateView
from django.views.decorators.csrf import csrf_exempt
 
from .models import User
 
# Create your views here.
def index(reqest):
    user_id = reqest.session.get('user')
 
    if user_id:
        user = User.objects.get(pk=user_id)
        return HttpResponse(user.useremail)
 
    return redirect('/')
 
 
def login(request):
    if request.method == 'GET':
        return render(request, 'login.html')
    elif request.method == 'POST':
        useremail = request.POST.get('useremail'None)
        password = request.POST.get('password'None)
 
        res_data = {}
        try:
            user = User.objects.get(useremail=useremail)
            if check_password(password, user.password):
                # 비밀번호가 일치하면 세션 생성
                request.session['user'= user.id
                res_data['data'= '1'
                res_data['message'= '로그인 되었습니다.'
            else:
                res_data['data'= '0'
                res_data['message'= '입력 정보를 확인하세요.'
            return JsonResponse(res_data)
        except User.DoesNotExist:
            # 대문자 User 임에 주의
            res_data['status'= '0'
            res_data['message'= '입력 정보를 확인하세요.'
            return JsonResponse(res_data)
 
        return render(request, 'login.html', res_data)
 
 
 
def register(request):
    if request.method == 'GET':
        return render(request, 'register.html')
    elif request.method == 'POST':
        username = request.POST.get('username',None)
        useremail = request.POST.get('useremail',None)
        password = request.POST.get('password',None)
 
        res_data ={}
        try:
            user = User.objects.get(useremail=useremail)
            if user:
                res_data['status'= '0' # 기존 가입된 회원
                return JsonResponse(res_data)
        except User.DoesNotExist:
            # 대문자 User 임에 주의
            user = User(
                username = username,
                useremail = useremail,
                password = make_password(password)
            )
            user.save()
            # session 생성
            user = User.objects.get(useremail=useremail)
            request.session['user'= user.id
            res_data['data'= '1' # 회원 가입 완료
            res_data['message'= '회원 가입 완료'
            return JsonResponse(res_data)
 
        return render(request,'register.html',res_data)
 
 
def logout(request):
    if request.session.get('user'):
        del(request.session['user'])
 
    return redirect('/user/login')
 
@method_decorator(csrf_exempt, name='dispatch')
class BaseView(TemplateView):
    @staticmethod
    def response(data={}, message='', status=200):
        result = {
            'data': data,
            'message': message,
            'status': status,
        }
        return JsonResponse(result)
 
class UserLogoutView(BaseView):
    def get(self, request):
        if self.request.session.get('user'):
            del (self.request.session['user'])
        return redirect('/')
 
class UserLoginView(BaseView):
    template_name = 'login.html'
    def post(self,request):
        useremail = request.POST.get('useremail'None)
        password = request.POST.get('password'None)
 
        try:
            user = User.objects.get(useremail=useremail)
            if check_password(password, user.password):  # 비밀번호가 일치하면 세션 생성
                self.request.session['user'= user.id
                data = '1'
                message = '로그인 되었습니다.'
            else:
                data = '0'
                message = '입력 정보를 확인해주세요.'
            return self.response(data, message, status='')
        except User.DoesNotExist:
            # 대문자 User 임에 주의
            data = '0'
            # message = '입력 정보를 확인해주세요.'
            message = '등록된 회원 정보가 없습니다.'
            return self.response(data, message, status='')
        return self.response()
 
 
class UserRegisterView(BaseView):
    template_name = 'register.html'
    def post(self,request):
        username = request.POST.get('username'None)
        useremail = request.POST.get('useremail'None)
        password = request.POST.get('password'None)
 
        if not username:
            return self.response(data='0', message='성명을 입력해주세요.', status=400)
        if not password:
            return self.response(data='0', message='패스워드를 입력해주세요.', status=400)
        try:
            validate_email(useremail)
        except ValidationError:
            return self.response(data='0', message='올바른 이메일을 입력해주세요.', status=400)
 
        try:
            user = User.objects.get(useremail=useremail)
            if user:
                return self.response(data='0', message='이미 가입된 아이디입니다.', status=400)
        except User.DoesNotExist:
            # 대문자 User 임에 주의
            user = User(
                username=username,
                useremail=useremail,
                password=make_password(password)
            )
            user.save()
            # session 생성
            user = User.objects.get(useremail=useremail)
            self.request.session['user'= user.id
            return self.response(data='1', message='회원 가입 완료.', status=200)
 
        return self.response({'user.id': user.id})
 
 

 

User.objects.get(useremail=useremail) SELECT * FROM accounts WHERE useremail=useremail;

User.objects.all()  → SELECT * FROM accounts;

User.objects.all().order_by('-id')[:20] → SELECT * FROM accounts ORDER BY id DESC LIMIT 20;

 

 

login.html

POST 방식의 <form> 을 사용하는 탬플릿에서는 CSRF(Cross Site Request Forgery)  공격을 방어하기 위해 {% csrf_token %} 태그를 사용해야 한다.

 
{% extends "base.html" %}
{% block title %}로그인{% endblock %}
{% block contents %}
<div class="login-form pt-5">
    <form method="post" id="post-form">
        {% csrf_token %}
        <h2 class="text-center">로그인</h2>
        <div class="form-group">
            <input type="text" id="useremail" class="form-control" placeholder="user email 입력" required="required">
        </div>
        <div class="form-group">
            <input type="password" id="password" class="form-control" placeholder="비밀번호 입력" required="required">
        </div>
        <div class="form-group">
            <button type="submit" class="btn btn-primary btn-block">로그인</button>
        </div>
    </form>
    <class="text-center"><a href="{% url 'accounts:ajax_register' %}" id="register">회원 가입</a></p>
</div>
<script>
$(document).on('submit''#post-form', function (e) {
    e.preventDefault();
 
    $.ajax({
        type: 'POST',
        url: '{% url "accounts:ajax_login" %}',
        data: {
            useremail: $('#useremail').val(),
            password: $('#password').val(),
            csrfmiddlewaretoken: $('input[name=csrfmiddlewaretoken]').val(),
            action: 'post'
        },
        success: function (json) {
            if(json.data == 1){
                document.getElementById("post-form").reset();
                alert(json.message);
                window.location.href = '/';
                {#window.location.href = {% url 'index' %};#}
            } else if(json.data == 0){
                alert(json.message);
            }
        },
        error: function (xhr, errmsg, err) {
            alert('에러가 발생했습니다.');
            console.log(xhr.status + ": " + xhr.responseText); 
        }
    });
});
</script>
{% endblock %}
 

 

register.html

{% extends "base.html" %}
{% block title %}회원 가입{% endblock %}
{% block contents %}
    <div class="signup-form pt-5">
        <form method="post" id="post-form" action=".">
            {% csrf_token %}
            <h2 class="text-center">회원가입</h2>
            <div class="form-group">
                <div class="row">
                    <div class="col">
                        <input type="text" class="form-control" id="username" placeholder="사용자 이름" required="required" />
                    </div>
                </div>
            </div>
            <div class="form-group">
                <input type="email" class="form-control" id="useremail" placeholder="Email" required="required"/>
            </div>
            <div class="form-group">
                <input type="password" class="form-control" id="password" placeholder="비밀번호" required="required">
            </div>
            <div class="form-group">
                <input type="password" class="form-control" id="repasswd" placeholder="비밀번호 확인"
                       required="required">
            </div>
            <div class="form-group">
                <button type="submit" class="btn btn-success btn-lg btn-block">회원 등록</button>
            </div>
        </form>
        <div class="text-center"><a href="{% url 'accounts:ajax_login' %}">로그인</a></div>
    </div>
    <script>
        $(document).on('submit''#post-form', function (e) {
            e.preventDefault();
            var pwd = $("#password");
            var repwd = $("#repasswd");
            if (PasswordChk(pwd, repwd) == falsereturn false;
 
            $.ajax({
                type: 'POST',
                url: '{% url "accounts:ajax_register" %}',
                data: {
                    username: $('#username').val(),
                    useremail: $('#useremail').val(),
                    password: $('#password').val(),
                    csrfmiddlewaretoken: $('input[name=csrfmiddlewaretoken]').val(),
                    action: 'post'
                },
                success: function (json) {
                    if (json.data == 1) {
                        document.getElementById("post-form").reset();
                        window.location.href = '/';
                        alert(json.message);
                    } else if (json.data == 0) {
                        alert(json.message);
                    }
                },
                error: function (xhr, errmsg, err) {
                    alert('에러가 발생했네요.');
                    console.log(xhr.status + ": " + xhr.responseText);
                }
            });
        });
 
        function PasswordChk(pwd, repwd) {
            if (pwd.val() == '') {
                alert('비밀번호를 입력하세요');
                pwd.focus();
                return false;
            }
            if (pwd.val().indexOf(' '> -1) {
                alert("공백은 입력할 수 없습니다.");
                return false;
            }
 
            var check1 = /^(?=.*[a-zA-Z])(?=.*[0-9]).{10,15}$/.test(pwd.val()); //영문,숫자
            var check2 = /^(?=.*[a-zA-Z])(?=.*[0-9])(?=.*[^a-zA-Z0-9]).{8,15}$/.test(pwd.val()); //영문,숫자,특수문자
            var check3 = /^(?=.*[a-zA-Z])(?=.*[^a-zA-Z0-9]).{10,15}$/.test(pwd.val()); //영문,특수문자
            var check4 = /^(?=.*[^a-zA-Z0-9])(?=.*[0-9]).{10,15}$/.test(pwd.val()); //특수문자, 숫자
            if (!(check1 || check2)) {
                alert("영문/숫자 혼용시에는 10~15자리를 사용해야 합니다.\n영문/숫자/특수문자 혼용시에는 8~15자리를 사용해야 합니다.");
                return false;
            }
 
            // 동일한 문자/숫자 4자 이상
            if (/(\w)\1\1\1/.test(pwd.val())) { // /(\w)\1\1/.test(pwd)
                alert("같은 문자를 4번 이상 사용할 수 없습니다.");
                pwd.focus();
                return false;
            }
            if (isContinuedValue(pwd.val())) {
                alert("비밀번호에 4자 이상의 연속 문자 또는 숫자를 사용하실 수 없습니다.");
                pwd.focus();
                return false;
            }
 
            if (repwd.val() == '') {
                alert('비밀번호를 다시 한번 더 입력하세요');
                repwd.focus();
                return false;
            }
            if (pwd.val() !== repwd.val()) {
                alert('비밀번호를 둘다 동일하게 입력하세요');
                return false;
            }
            return true;
        }
 
        function isContinuedValue(value) {
            console.log("value = " + value);
            var intCnt1 = 0;
            var intCnt2 = 0;
            var temp0 = "";
            var temp1 = "";
            var temp2 = "";
            var temp3 = "";
 
            for (var i = 0; i < value.length - 3; i++) {
                temp0 = value.charAt(i);
                temp1 = value.charAt(i + 1);
                temp2 = value.charAt(i + 2);
                temp3 = value.charAt(i + 3);
 
                if (temp0.charCodeAt(0- temp1.charCodeAt(0== 1
                    && temp1.charCodeAt(0- temp2.charCodeAt(0== 1
                    && temp2.charCodeAt(0- temp3.charCodeAt(0== 1) {
                    intCnt1 = intCnt1 + 1;
                }
 
                if (temp0.charCodeAt(0- temp1.charCodeAt(0== -1
                    && temp1.charCodeAt(0- temp2.charCodeAt(0== -1
                    && temp2.charCodeAt(0- temp3.charCodeAt(0== -1) {
                    intCnt2 = intCnt2 + 1;
                }
            }
            return (intCnt1 > 0 || intCnt2 > 0);
        }
    </script>
{% endblock %}
 
728x90

'파이썬 > Django' 카테고리의 다른 글

Django Custom User Model - 회원가입, 로그인  (2) 2022.01.29
Django, squash migrations  (0) 2022.01.26
Python Django 회원가입 with jQuery(ajax)  (0) 2022.01.22
Python Django Login with jQuery(ajax)  (0) 2022.01.17
Django Login 예제  (0) 2022.01.16
블로그 이미지

Link2Me

,