장고 회원가입과 로그인 기능을 함수형 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>
<p 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) == false) return 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 %}
|
'파이썬 > 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 |