Django auth 익히기
- accounts/
- articles/
- config/
- templates/
- base.html
- components/
- nav.html
Bootstrap을 적용하기 위해 accounts/forms.py
에서 Auth Form
을 커스텀
로그인, 회원가입, 회원정보 수정, 비밀번호 변경 등에 form이 공통적으로 사용되고 있어, 공용으로 사용할 수 있는 template을 만들었다.
# vscode 자동완성으로 입력되는 `__init__`
class CustomAuthenticationForm(AuthenticationForm):
def __init__(self, request=None, *args, **kwargs):
super().__init__(request=request, *args, **kwargs)
# stackoverflow 참고 코드
class CustomAuthenticationForm(AuthenticationForm):
def __init__(self, *args, **kwargs):
super(CustomAuthenticationForm, self).__init__(*args, **kwargs)
자동완성으로 입력되는 함수로 상속을 진행하면, request관련 에러가 발생한다.
그래서 참고 코드를 살펴보며 조금씩 변화를 주었다.
__init__
에 인자로 전달되는 request=request
부분을 지워보았더니 에러는 발생하지 않았다.
하지만, 로그인이 제대로 진행되지 않았다. (form.is_valid()가 False)
__init__
에 인자로 전달되는 request=None
부분을 지워보았더니 로그인도 제대로 진행되었다.
Stackoverflow 코드는 super에 자기 클래스를 인자로 넣어주는 것이 이해가 되지 않았기 때문에 아래 코드로 작업을 이어서 진행한 뒤 문제가 생기면 다시 정리하려고 한다.
class CustomAuthenticationForm(AuthenticationForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
공통 form.html에 렌더링을 하려고 하였지만, {{ field.help_text }}
에 a tag(HTML)가 들어있어 제대로 렌더링되지 않음
{{ field.help_text|safe }}
{% extends 'base.html' %}
{% block content %}
<h1>{{ title }}</h1>
<hr>
<section>
<form action="" method="POST">
{% csrf_token %}
{% for field in form %}
<div class="mb-4">
<label for="{{ field.id_for_label }}" class="form-label">{{ field.label }}</label>
{{ field }}
<div class="form-text">
{{ field.help_text|safe }}
</div>
</div>
{% endfor %}
<button type="submit" class="btn btn-primary">{{ buttonText }}</button>
</form>
{% if deleteBtn %}
<hr>
<form action="{% url 'accounts:delete' %}" method="POST">
{% csrf_token %}
<button class="btn btn-danger" type="submit">회원탈퇴</button>
</form>
{% endif %}
</section>
{% endblock content %}
accounts/forms.py
from django.contrib.auth.forms import AuthenticationForm
class CustomAuthenticationForm(AuthenticationForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields["username"].widget.attrs["class"] = "form-control"
self.fields["password"].widget.attrs["class"] = "form-control"
accounts/views.py
from django.views.decorators.http import require_http_methods
from .forms import CustomAuthenticationForm
# GET, POST 방식의 요청만 받습니다.
# 다른 요청 방식은 Method not allowed Error(405)
@require_http_methods(["GET", "POST"])
def login(request):
if request.method == "POST":
# 입력받은 정보를 바탕으로 form 인스턴스 생성
form = CustomAuthenticationForm(request, request.POST)
# 생성한 form 인스턴스로 유효성 검사
if form.is_valid():
# 로그인
auth_login(request, form.get_user())
# 메인페이지로 이동
return redirect("accounts:index")
else:
form = CustomAuthenticationForm()
# GET method or form.is_valid() == False
context = {
"form": form,
"title": "로그인",
"buttonText": "로그인",
}
return render(request, "accounts/form.html", context)
accounts/views.py
from django.view.decorators.http import require_POST
@require_POST
def logout(request):
# 로그인이 되어있는 상태라면,
if request.user.is_authenticated:
# 로그아웃!
auth_logout(request)
return redirect("accounts:index")
accounts/forms.py
from django.contrib.auth.forms import UserCreationForm
class CustomUserCreationForm(UserCreationForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields["username"].widget.attrs["class"] = "form-control"
self.fields["password1"].widget.attrs["class"] = "form-control"
self.fields["password2"].widget.attrs["class"] = "form-control"
accounts/views.py
from django.views.decorators.http import require_http_methods
from django.contrib.auth.decorators import login_required
from django.contrib.auth import login as auth_login
from .forms import CustomUserCreationForm
@require_http_methods(["GET", "POST"])
@login_required
def create(request):
if request.method == "POST":
form = CustomUserCreationForm(request.POST)
# 유효성 검사
if form.is_valid():
# 바로 로그인
# save() 메서드는 생성한 user 인스턴스를 리턴합니다.
user = form.save()
auth_login(request, user)
return redirect("accounts:index")
else:
form = CustomUserCreationForm()
context = {
"form": form,
"title": "회원가입",
"buttonText": "가입하기",
}
return render(request, "accounts/form.html", context)
accounts/forms.py
from django.contrib.auth.forms import UserChangeForm
class CustomUserChangeForm(UserChangeForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields["username"].widget.attrs["class"] = "form-control"
self.fields["email"].widget.attrs["class"] = "form-control"
class Meta(UserChangeForm.Meta):
fields = (
"username",
"email",
)
accounts/views.py
from django.view.decorators.http import require_http_methods
from .forms import CustomUserChangeForm
@require_http_methods(["GET", "POST"])
def update(request):
if not request.user.is_authenticated:
return redirect("accounts:login")
if request.method == "POST":
form = CustomUserChangeForm(request.POST, instance=request.user)
if form.is_valid():
form.save()
return redirect("accounts:index")
else:
form = CustomUserChangeForm(instance=request.user)
context = {
"form": form,
"title": "회원정보 수정",
"buttonText": "수정하기",
"deleteBtn": True,
}
return render(request, "accounts/form.html", context)
accounts/views.py
from django.view.decorator.http import require_POST
@require_POST
def delete(request):
if request.user.is_authenticated:
request.user.delete()
return redirect("accounts:index")