20210203_ Django 로그인처리, 세션처리, render, redirect, form 활용
Django
로그인
redirect 처리
확인해 보면 (개발자 툴 - Application - cookies) 브라우저가 다르면는 session을 다르게 인식하기 때문에 시크릿 모드 브라우저와 그냥 브라우저와 session id와 토큰이 다르다 각 세션 아이디 별로 세션 공간이 따로 존재하는데 이것을 쿠키임-> 자동으로 세션 관리해줌으로 그냥 쓰면 됨
세션 값들이 클라이언트를 식별하게 해주는 식별자가 됨
render vs redirect
render
:render(request, template_name, context=None, content_type=None, status=None, using=None)
- context 값을 넘길 수 있음, 템플릿을 불러옴
redirect
:redirect(to, permanent=False, *args, **kwargs)
- 단지, URL로 이동함(이동하여 해당 views가 다시 실행됨) context 값 못 넘김
세션과 로그인 처리
from django.http import HttpResponse
from django.shortcuts import render
from django.contrib.auth.hashers import make_password, check_password # 저장된 password 암호화
from .models import Fcuser
# Create your views here.
def login(request):
if request.method == 'GET':
return render(request, 'login.html')
elif request.method == 'POST':
username = request.POST.get('username', None)
password = request.POST.get('password', None)
res_data = {}
if not (username and password):
res_data['error'] = '모든 값을 입력해 주세요.'
else:
sl_user = Sl_user.objects.get(username=username)
if check_password(password, sl_user.password):
user_id = sl_user.username
# 객체의 username을 user_id 할당
request.session['user'] = user_id
# 세션에서 user를 key로하고 값을 할당 받은 user_id로 설정
return redirect('/')
# '/' 홈으로 가게 만듦음
else:
res_data['error'] = '잘못된 아이디 또는 비밀번호 입니다.'
return render(request, 'login.html', res_data)
home 만들기
- home의 경우 어느 앱에도 속하지 않는 최상단 url로서 일단 프로젝트 폴더 urls를 설정한다. (결국에는 sl_user.views에서 home 함수를 만들어서 프로젝트 url로 연결)
- 프로젝트 urls.py
from django.contrib import admin
from django.urls import path, include
from sl_user.views import home
urlpatterns = [
path('admin/', admin.site.urls),
path('sl_user/', include('sl_user.urls')),
path('', home)
]
- views.py
from django.http import HttpResponse
from django.shortcuts import render, redirect
from django.contrib.auth.hashers import make_password, check_password # 저장된 password 암호화
from .models import Sl_user
# Create your views here.
def home(request):
user_id = request.session.get('user')
# home의 세션을 user_id에 할당 시켜서
if user_id:
sl_user = Sl_user.objects.get(pk=user_id) # pk primary key
# 모델에서 키가 user_id인 경우의 객체를 가져와 sl_user에 할당
return HttpResponse(sl_user.username)
# sl_user 객체의 username을 반환하여 표시
return HttpResponse('Home!') # 맞지 않으면 Home!으로 표시
- 로그아웃
def logout(request):
if request.session.get('user'):
del(request.session['user'])
# 세션이 있으면 세션을 제거하여 로그아웃 됨
return redirect('/')
- 로그인, 로그아웃 url연결
from django.urls import path
from.import views
urlpatterns = [
path('register/', views.register),
path('login/', views.login),
path('logout/', views.logout),
]
template 상속
- html 구성이 대부분이 중복되고 조금 다르기에 상속으로 효율적으로 할 필요가 있음
- 또한 수정시 유지 보수의 편의성을 위해서 필요함
- 기준 template를 만들고 상속받아 원하는 부분만 바꿈
base.html
을 만들어서 무조건 중복되는 부분을 만듦container
클래스 안에 부분이 변경 됨으로 그 부분만 바꾸도록 설정-
블록 구성이나 지정할때 주석 넣으면 인식 안됨 주의!
- base.html
<html>
<head>
<!-- Required meta tags -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<!-- <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-giJF6kkoqNQ00vy+HMDP7azOuL0xtbfIcaT9wjKHr8RbDVddVHyTfAAsrekwKmP1" crossorigin="anonymous"> -->
<!-- bootstrap에서 다운받은 stylesheet 를 연결시켜 적용 -->
<link rel="stylesheet" href="/static/bootstrap.min.css" />
<script src="https://cdn.jsdelivr.net/npm/@popperjs/core@2.5.4/dist/umd/popper.min.js" integrity="sha384-q2kxQ16AaE6UbzuKqyBE9/u/KzioAlnx2maXQHiDX9d4/zp8Ok3f+M7DPm+Ib6IU" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.0-beta1/dist/js/bootstrap.min.js" integrity="sha384-pQQkAEnwaBkjpqZ8RU1fF1AKtTcHJwFl3pblpTlHXybJjHpMYo79HY3hIi4NKxyj" crossorigin="anonymous"></script>
</head>
<body>
<div class="container">
{% block contents %}
{% endblock %}
</div>
</body>
</html>
- login.html
{% extends "base.html" %}
{% block contents %}
<div class="row mt-5">
<div class="col-12 text-center">
<h1>로그인</h1>
</div>
</div>
<div class="row mt-5">
<div class="col-12">
{{error}}
</div>
</div>
<div class="row mt-5">
<div class="col-12">
<form method="POST", action=".">
{% csrf_token %}
<div class="form-group">
<label for="username" class="form-label">사용자 이름</label>
<input type="text" class="form-control" id="username" placeholder="사용자 이름" name="username">
</div>
<div class="form-group">
<label for="password" class="form-label">비밀번호</label>
<input type="password" class="form-control" id="password" placeholder="비밀번호" name="password">
</div>
<button type="submit" class="btn btn-primary">로그인</button>
</form>
</div>
</div>
{% endblock %}
- register.html
{% extends "base.html" %}
{% block contents %}
<div class="row mt-5">
<div class="col-12 text-center">
<h1>회원가입</h1>
</div>
</div>
<div class="row mt-5">
<div class="col-12">
{{error}}
</div>
</div>
<div class="row mt-5">
<div class="col-12">
<form method="POST", action=".">
{% csrf_token %}
<div class="form-group">
<label for="username" class="form-label">사용자 이름</label>
<input type="text" class="form-control" id="username" placeholder="사용자 이름" name="username">
</div>
<div class="form-group">
<label for="useremail" class="form-label">사용자 이메일</label>
<input type="email" class="form-control" id="useremail" placeholder="사용자 이메일" name="useremail">
</div>
<div class="form-group">
<label for="password" class="form-label">비밀번호</label>
<input type="password" class="form-control" id="password" placeholder="비밀번호" name="password">
</div>
<div class="form-group">
<label for="re-password" class="form-label">비밀번호 확인</label>
<input type="password" class="form-control" id="re-password" placeholder="비밀번호 확인" name="re-password">
</div>
<button type="submit" class="btn btn-primary">등록</button>
</form>
</div>
</div>
{% endblock %}
django form을 이용한 구성
-
django에서 제공하는 form 만들기 기능을 통해서 html, view를 좀더 쉽게 구현 가능함
-
form이 제공하는 기본적인 기능들이 있지만 그것에서 조건을 추가하여 전에 기능을 구현 가능함 (복잡해 보여도 조금더 단순하게 view 구성이 가능함)
form 구성
- models 만드는 느낌으로 구성함
- 폼을 구성하는 필드를 만듦
error_messages
속성은 views파일에서 form의 자체 함수is_valid()
error 통해 나타 나는 에러 메세지를 수정할 수 있음(기본 key가 required로 되어 있음)password
의widget
속성은 html에서 input에 대한 속성을 지정 가능label
은 위에 나타나는 label임
- clean vs is_vaild
- 원래
clean
은 기본 내장함수로 views의is_vaild
실행시 값의 존재 유무를 체크하여 타당한 값을 자동으로cleaned_data
라는 dict형태의 변수에 저장함 - 폼 안의
clean
함수를 지정하는 이유는 기본 내장함수를 오버라이딩 하여 viewsis_vaild
실행시 빈값만이 아닌 조건을 커스터 마이징 하기 위함임 - 그래서 기본적으로 빈값체크를 위해서
cleaned_data = super().clean()
인 부모의 clean 함수를 통해 미리 cleaned_data를 체크하고 우리가 필요한 조건을 검사하게 함
- 원래
-
username = cleaned_data.get('username')
,password = cleaned_data.get('password')
를 통해서 해당 필드의 값을 가져와 할당함 - form.py
from django import forms
from .models import Fcuser
from django.contrib.auth.hashers import check_password
class LoginForm(forms.Form):
username = forms.CharField(error_messages={
'required': '아이디를 입력해 주세요.'
}, max_length=32, label="사용자 이름")
password = forms.CharField(error_messages={
'required': '비밀번호를 입력해 주세요.'
}, widget=forms.PasswordInput, label="비밀번호")
def clean(self):
cleaned_data = super().clean()
username = cleaned_data.get('username')
password = cleaned_data.get('password')
if username and password:
sl_user = Sl_user.objects.get(username=username)
#Sl_user모델의 객체에서 가져온다(username 필드와 아까 할당한 username과 같은 객체) -> sl_user 변수에 할당
if not check_password(password, sl_user.password):
# check_password를 통해 입력 받은 password와 sl_user의 password와 비교
self.add_error('password', '비밀번호가 일치하지 않습니다.')
# form 안에 기본적으로 있는 add_error()함수로 특정 필드에 error를 넣는 함수임(password를 키로 하고 값을 지정)
else:
self.user_id = sl_user.id
# 이상 없으면 일치하는 객체 sl_user의 id를 use_id에 할당
views 구성
- POST를 받는 형식은 같지만
def login(request):
if request.method == "POST":
form = LoginForm(request.POST) # POST 데이터를 넣어서 form변수에 할당
if form.is_valid(): # is_valid() 함수를 통해 정상적인지 검증(값이 단지 들어가 있는지 확인하는데 전에 커스터마이징 했음)
# 세션 코드
request.session['user'] = form.user_id
return redirect('/')
else:
form = LoginForm() # 맞지 않으면 post 할당하지 않음
return render(request, 'login.html', {'form': form})
- html 데이터 표현 형식 설정
- `` : 각요소를 P태그를 붙여서 정렬됨
- `` : 각요소가 한줄로
- `` : form을 for 반복문으로 넣을 수 있음
- 그리고 안에 어떤 형식을 반복할 것인지 넣을 수 있음
- django reference-Looping over the form’s fields
{% extends "base.html" %}
{% block contents %}
<div class="row mt-5">
<div class="col-12 text-center">
<h1>로그인</h1>
</div>
</div>
<div class="row mt-5">
<div class="col-12">
{{error}}
</div>
</div>
<div class="row mt-5">
<div class="col-12">
<form method="POST", action=".">
{% csrf_token %}
<!-- for반복문 넣기 -->
{% for field in form %}
<!-- 생성된 form의 fireld 가져와 반복 -->
<div class="form-group">
<label for="{{ field.id_for_label }}">{{ field.label }}</label>
<input type="{{ field.field.widget.input_type }}" class="form-control" id="{{ field.id_for_label }}" placeholder="{{ field.label }}" name="{{ field.name }}">
</div>
{% if field.errors %}
<!-- field 에러시 if 조건문 -->
<span style="color: red">{{ field.errors }}</span>
{% endif %}
{% endfor %}
<button type="submit" class="btn btn-primary">로그인</button>
</form>
</div>
</div>
{% endblock %}