회원 관리
회원 가입, 로그인, 로그아웃 기능을 만든다.
회원 관리를 위한 기능은 Account라는 이름의 앱으로 사진 관리 기능을 하는 Photo 앱과 구분하여 구현한다.
Account 앱 생성
$python manage.py startapp accounts
앱 생성 후 config/settings.py에 accounts 앱도 추가해준다.
로그인, 로그아웃 기능 추가
로그인, 로그아웃을 관리하는 기능이 Django에 이미 구현되어 있다.(그것도 굉장히 잘)
전체 과정은 제네릭뷰를 이용하는 과정과 비슷하다.
URL과 View 추가
먼저 accounts/urls.py에서 뷰를 호출하도록 하고, config/urls.py에서도 연결해준다.
LoginView, LogoutView의 소스를 살펴보면 제네릭 뷰에서 여러 설정(class member)들과 비슷하게 이미 로그인된 유저와 로그아웃 후에 대한 redict 옵션같은게 보인다.
# accounts/urls.py
from django.urls import path
from django.contrib.auth import views as auth_view
urlpatterns = [
path('login/', auth_view.LoginView.as_view(), name='login'),
path('logout/', auth_view.LogoutView.as_view(template_name='registration/logout.html'), name='logout'),
]
# config/urls.py
...
urlpatterns = [
path('', include('photo.urls')),
path('accounts/', include('accounts.urls')),
path('admin/', admin.site.urls),
]
...
템플릿 생성
로그인, 로그아웃 템플릿을 작성한다.
// accounts/templates/registration/login.html
{% extends 'base.html' %}
{% block title %}- Login{% endblock %}
{% block content %}
<div class="row">
<div class="col-md-2"></div>
<div class="col-md-8 panel panel-default">
<div class="alert alert-info">Please enter your login informations.</div>
<form action="" method="post">
{{form.as_p}}
{% csrf_token %}
<input class="btn btn-primary" type="submit" value="Login">
</form>
</div>
<div class="col-md-2"></div>
</div>
{% endblock %}
// accounts/templates/registration/logout.html
{% extends 'base.html' %}
{% block title %}- Logout{% endblock %}
{% block content %}
<div class="row">
<div class="col-md-2"></div>
<div class="col-md-8 panel panel-default">
<div class="alert alert-info">You have been successfully logged out.</div>
<a class="btn btn-primary" href="{% url 'login' %}">Click to Logint</a>
</div>
<div class="col-md-2"></div>
</div>
{% endblock %}
앞서 작성한 base.html에서 상단 네비게이션바의 로그인. 로그아웃의 링크도 연결해준다.
// templates/base.html
...
<li class="nav-item"><a href="{% url 'logout' %}" class="nav-link">Logout</a></li>
{% else %}
<li class="nav-item"><a href="{% url 'login' %}" class="nav-link">Login</a></li>
...
로그인 후 목록페이지로 안내하기 위해 settings.py를 수정해준다.
LOGIN_REDIRECT_URL
의 default 값은 /profile 이다.
# config/settings.py
...
LOGIN_REDIRECT_URL = '/'
회원가입 기능 만들기
회원가입 기능을 위해 뷰를 만들고, 폼(Form)을 구현하는 방법을 이용한다.
# accounts/forms.py
from django.contrib.auth.models import User
from django import forms
class RegisterForm(forms.ModelForm):
password = forms.CharField(label='Password', widget=forms.PasswordInput)
password2 = forms.CharField(label='Repeat Password', widget=forms.PasswordInput)
class Meta:
model = User
fields = ['username', 'first_name', 'last_name', 'email']
def clean_password2(self):
cd = self.cleaned_data
if cd['password'] != cd['password2']:
raise forms.ValidationError('Passwords not matched')
return cd['password2']
forms.ModelForm을 이용해 User 모델의 자료를 입력받도록 하며, password의 경우 widget 옵션으로 사용자가 입력시 *****
형태로 보이도록 한다.
form에 전달된 데이터의 유효성을 검사하는 각 필드의 clean_ 메서드들이 수행되고 나면, 사용자가 정의한 clean_password2(self) 메서드가 호출된다.
cd[]로 전달된 값은 View에서 Form.IsValid() 이후 cleaned_data[‘field’]의 형태로 사용 가능하다.
값을 전달해주는 Form을 생성하였으니 가입 기능을 처리하는 View를 만들어준다.
Django docs 참고: https://docs.djangoproject.com/en/2.2/ref/forms/validation/
초보몽키님 블로그 참고: https://wayhome25.github.io/django/2017/05/06/django-model-form/
# accounts/views.py
from django.shortcuts import render
from .forms import RegisterForm
def register(request):
if request.method == 'POST':
user_form = RegisterForm(request.POST)
if user_form.is_valid():
new_user = user_form.save(commit=False)
new_user.set_password(user_form.cleaned_data['password']) # request.POST['password']를 쓴다면 유효성 검사가 안된, 전달받은 값을 그대로 사용하는 셈이다.
new_user.save()
return render(request, 'registration/register_done.html', {'new_user': new_user})
else:
user_form = RegisterForm()
return render(request, 'registration/register.html', {'form': user_form})
new_user = user_form.save(commit=False) : user모델 객체를 생성하고, commit=False 옵션으로 DB에 저장하지않고 메모리상에만 생성한다.
new_user.set_password 메서드를 통해 암호를 저장해야 암호화된 상태로 저정된다. DB를 열어보면 모두 hash값처럼 생긴 string으로 저정되어 있다. password field가 charField인 이유는 이것때문인 것 같다.
회원가입이 완료되면 register_done페이지로 안내한다.
뷰를 연결하기 위해 url을 수정하고, 템플릿도 작성해준다.
# accounts/urls.py
from django.urls import path
from django.contrib.auth import views as auth_view
from .views import register
urlpatterns = [
path('login/', auth_view.LoginView.as_view(), name='login'),
path('logout/', auth_view.LogoutView.as_view(template_name='registration/logout.html'), name='logout'),
path('register/', register, name='register')
]
// accounts/templates/registration/register.html
{% extends 'base.html' %}
{% block title %}- Registration{% endblock %}
{% block content %}
<div class="row">
<div class="col-md-2"></div>
<div class="col-md-8 panel panel-default">
<div class="alert alert-info">Please enter your account informations.</div>
<form action="" method="post">
{{form.as_p}}
{% csrf_token %}
<input class="btn btn-primary" type="submit" value="Register">
</form>
</div>
<div class="col-md-2"></div>
</div>
{% endblock %}
// accounts/templates/registration/register_done.html
{% extends 'base.html' %}
{% block title %}- Registration Done{% endblock %}
{% block content %}
<div class="row">
<div class="col-md-2"></div>
<div class="col-md-8 panel panel-default">
<div class="alert alert-info">Registration Success. Welcome, {{new_user.username}}</div>
<a class="btn btn-info" href="/">Move to main</a>
</div>
<div class="col-md-2"></div>
</div>
{% endblock %}
마지막으로 base.html에서 네비게이션바의 회원가입 링크도 연결해준다.
// templates/base.html
...
<li class="nav-item"><a href="{% url 'register' %}" class="nav-link">Signup</a></li>
...