Django에서 로그인 기능을 구현해보자.
해당 실습은 customlogin이라는 새 어플리케이션을 만들어서 진행하며, customlogin에는 따로 urls.py와 forms.py를 만들어준다.
상위 urls.py에서 include 설정을 해준 후
customlogin의 urls.py에 다음과 같이 import를 시켜준다.
from django.urls import path
from .views import *
app_name='login'
urlpatterns = [
]
로그인 기능은 django에서 기본으로 제공되는 model인 User를 사용한다. 따라서 따로 models.py에 정의해줄 필요가 없다. 바로 forms.py로 들어가 form class만 만들어주자.
from django.forms import ModelForm
from django.contrib.auth.models import User
from django import forms
class SignupForm(ModelForm): #회원가입을 제공하는 class이다.
password_check = forms.CharField(max_length=200, widget=forms.PasswordInput())
#아쉽게도 User 모델에서는 password_check 필드를 제공해주지 않는다.
#따라서 따로 password_check 필드를 직접 정의해줄 필요가 있다.
#입력 양식은 type은 기본이 text이다. 따라서 다르게 지정해주고 싶을 경우 widget을 이용한다.
#widget=forms.PasswordInput()은 입력 양식을 password로 지정해주겠다는 뜻이다.
field_order=['username','password','password_check','last_name','first_name','email']
#field_order는 만들어지는 입력양식의 순서를 정해준다.
#여기서 사용한 이유는 password 바로 밑에 password_check 입력양식을 추가시키고 싶어서이다.
#만약 따로 field_order를 지정해주지않았다면, password_check는 맨 밑에 생성된다.
class Meta:
model=User
widgets = {'password':forms.PasswordInput}
fields = ['username','password','last_name','first_name','email']
#User model에 정의된 username, passwordm last_name, first_name, email을 입력양식으로
class SigninForm(ModelForm): #로그인을 제공하는 class이다.
class Meta:
model = User
widgets = {'password':forms.PasswordInput}
field = ['username','password']
회원가입
이제 views.py를 통해 회원가입 기능을 구현할 수 있도록 한다.
from django.shortcuts import render
from .forms import SigninForm, SignupForm #이전에 만든 form 클래스를 선언해주고
from django.http.response import HttpResponseRedirect
from django.urls.base import reverse
from django.contrib.auth.models import User #User 모델을 사용하기위해 선언해준다.
def signup(request):#역시 GET/POST 방식을 사용하여 구현한다.
if request.method == "GET":
return render(request, 'customlogin/signup.html', {'f':SignupForm()} )
elif request.method == "POST":
form = SignupForm(request.POST)
if form.is_valid():
if form.cleaned_data['password'] == form.cleaned_data['password_check']:
#cleaned_data는 사용자가 입력한 데이터를 뜻한다.
#즉 사용자가 입력한 password와 password_check가 맞는지 확인하기위해 작성해주었다.
new_user = User.objects.create_user(form.cleaned_data['username'],form.cleaned_data['email'],form.cleaned_data['password'])
#User.object.create_user는 사용자가 입력한 name, email, password를 가지고 아이디를 만든다.
#바로 .save를 안해주는 이유는 User.object.create를 먼저 해주어야 비밀번호가 암호화되어 저장된다.
new_user.last_name = form.cleaned_data['last_name']
new_user.first_name = form.cleaned_data['first_name']
#나머지 입력하지 못한 last_name과, first_name은 따로 지정해준다.
new_user.save()
return HttpResponseRedirect(reverse('vote:index'))
else:
return render(request, 'customlogin/signup.html',{'f':form, 'error':'비밀번호와 비밀번호 확인이 다릅니다.'})#password와 password_check가 다를 것을 대비하여 error를 지정해준다.
else: #form.is_valid()가 아닐 경우, 즉 유효한 값이 들어오지 않았을 경우는
return render(request, 'customlogin/signup.html',{'f':form})
#원래는 error 메시지를 지정해줘야 하지만 따로 지정해주지 않는다.
#그 이유는 User 모델 클래스에서 자동으로 error 메시지를 넘ㄱ
그리고 url과 template을 만들어준다.
html 문서는 역시 따로 customlogin 폴더 안에 templates를 만들고 그 안에 또 customlogin 폴더를 만들어 저장한다.
회원가입을 수행하는 html의 이름은 signup.html로 정의한다.
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
{% if error %}
<h3 style="color:red">{{error}}</h3>
{% endif %}
<form action="" method="POST">
{% csrf_token %}
<table>
{{ f.as_table }}
</table>
<input type="submit" value="회원가입">
</form>
</body>
</html>
-----------------------------------------------------------------------------------------------------------
URL 부분
path('signup/', signup, name='signup'),
설정한 url로 들어가서 회원가입을 진행하고
관리사이트에 들어가서 User를 누른다.
회원가입이 제대로 진행되었는지 확인이 가능하다.
로그인/로그아웃
이제 가입한 아이디로 로그인을 할 수 있도록 만들어야 한다.
로그인과 로그아웃의 views.py는 다음과 같다.
from django.contrib.auth import login, authenticate
#login과 authenticate 기능을 사용하기위해 선언
#login은 login처리를 해주며, authenticate는 아이디와 비밀번호가 모두 일치하는 User 객체를 추출
def signin(request):#로그인 기능
if request.method == "GET":
return render(request, 'customlogin/signin.html', {'f':SigninForm()} )
elif request.method == "POST":
form = SigninForm(request.POST)
id = request.POST['username']
pw = request.POST['password']
u = authenticate(username=id, password=pw)
#authenticate를 통해 DB의 username과 password를 클라이언트가 요청한 값과 비교한다.
#만약 같으면 해당 객체를 반환하고 아니라면 none을 반환한다.
if u: #u에 특정 값이 있다면
login(request, user=u) #u 객체로 로그인해라
return HttpResponseRedirect(reverse('vote:index'))
else:
return render(request, 'customlogin/signin.html',{'f':form, 'error':'아이디나 비밀번호가 일치하지 않습니다.'})
from django.contrib.auth import logout #logout을 처리하기 위해 선언
def signout(request): #logout 기능
logout(request) #logout을 수행한다.
return HttpResponseRedirect(reverse('vote:index'))
template은 signin.html로 만들어주고 url 또한 만들어준다.
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
{% if error %}
<p>{{ error }}</p>
{% endif %}
<form action="" method="POST">
{% csrf_token %}
<table>
{{ f.as_table }}
</table>
<input type="submit" value="로그인">
</form>
<a href="{% url 'login:signup' %}">회원가입</a>
</body>
</html>
---------------------------------------------------------------------------------------------------------------
#url 부분
path('signin/', signin, name='singin'),
path('signout/', signout, name='signout'),
로그인 기능 활용
다음과 같이 login 화면이 나타나는 것을 볼 수 있다.
그러나 막상 로그인을 진행해도 화면 상에 실제로 바뀐것이 없다. 때문에 로그인 후 화면이 바뀌는 것을 진행해야한다.
우선 앞서 만든 base.html을 signin.html과 signout.html에도 추가해주자.
signin.html
------------------------------------------------------------------------------------------------------------
{% extends 'base.html' %}
{% block content %}
{% if error %}
<p>{{ error }}</p>
{% endif %}
<form action="" method="POST">
{% csrf_token %}
<table>
{{ f.as_table }}
</table>
<input type="submit" value="로그인">
</form>
<a href="{% url 'login:signup' %}">회원가입</a>
{% endblock %}
-------------------------------------------------------------------------------------------------------------
signup.html
------------------------------------------------------------------------------------------------------------
{% extends 'base.html' %}
{% block content %}
{% if error %}
<h3 style="color:red">{{error}}</h3>
{% endif %}
<form action="" method="POST">
{% csrf_token %}
<table>
{{ f.as_table }}
</table>
<input type="submit" value="회원가입">
</form>
{% endblock %}
------------
그리고 base.html을 수정해준다.
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
<h2>netdream 홈페이지</h2>
<a href="{% url 'vote:index' %}">설문조사</a>
<br>
{% if user.is_authenticated %}<!-- 만약 로그인이 되어있는 상태이면 -->
{{user.last_name}} {{user.first_name}}님 환영합니다. <!-- 다음과같이 출력 -->
<a href="{% url 'login:signout' %}">로그아웃</a> <!-- 로그아웃이 뜨도록 한다. -->
{% else %} <!-- 로그인이 되어있지않다면 -->
<a href="{% url 'login:signin' %}">로그인</a> <!-- 로그인과-->
<a href="{% url 'login:signup' %}">회원가입</a> <!-- 회원가입이 뜨도록 한다. -->
{% endif %}
<hr> <!-- 윗부분(고정) -->
{% block content %}
<!-- 변동부분을 block 지정해준다. -->
{% endblock %}
<hr> <!-- 아랫부분(고정) -->
<p>netdream blog</p>
<p><a href=http://netdream.tistory.com/>netdream.com</a>으로 놀러오세요</p>
</body>
</html>
그리고나서 확인을 해보면
아무 곳이나 들어가서 로그인을 눌러주고
username과 password입력 후 로그인을 해주면
다음과 같이 이름이 뜨는 것을 확인가능하다.
이제 로그인까지 완료하였다. 이 로그인 기능으로 홈페이지에 어떤 것을 구현할 수 있을까?
많은 것들이 있겠지만 대표적으로 접근제한이 있다.
앞서 만든 투표기능을 로그인한 사람만 접근이 가능하게 하는 것이 대표적인 예이다.
해당 기능을 구현하기위해서 우선 vote에 있는 views.py에 들어갈 필요가 있다.
from django.contrib.auth.decorators import login_required #login_required를 선언하고
@login_required #@login_required를 함수 앞에 써준다.
#해당 함수는 이제 login 되어야만 사용이 가능하다.
#만일 로그인하지 않은 채로 실행 시 page를 찾을 수 없습니다.가 화면에 뜨게된다.
def qregister(request):
if request.method == "GET":
form = QuestionForm()
return render(request, 'vote/qregister.html', {'f':form})
elif request.method == "POST":
form = QuestionForm(request.POST)
if form.is_valid():
q = form.save(commit=False)
q.date = datetime.now()
q.save()
return HttpResponseRedirect(reverse('vote:index'))
마지막으로 django 서버의 settings.py에 들어가서 맨 앞에 코드를 추가해준다.
import os
from django.urls.base import reverse_lazy #reverse_lazy를 선언
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
LOGIN_URL = reverse_lazy('login:signin')
#해당 의미는 만일 로그인 접근이 제한 상황에서 page not found 대신에 siginin함수가 실행된다.
로그인이 되지않은 상태에서 질문지 등록을 누르면
signin함수가 자동으로 실행된다.