📍 최종 목적 : 실제 가상 서버를 빌려서 그 위에다가 우리가 만든 docker 시스템을 얹는거고 docker 시스템을 만들기 위해서는 4가지 컨테이너를 구축해야함 (지금까지는 그 중에서도 장고 부분 구축하고 있었음, 장고 컨테이너 내부의 여러가지 앱들 중에서도 accountapp 만들고 있음) 


view.py -> url.py ->create.html 작성
실습결과)
: 바로 urls.py에서 작성 가능
path('login/', LoginView.as_view(template_name='accountapp/login.html'), name='login'),
path('logout/', LogoutView.as_view(), name='logout'),
login, logout 구현 완료
장고에서 기본 제공해주는 것 사용
<div style="text-align: center">
<div>
<h4>Login</h4>
</div>
<div>
<form action="" method="post">
{% csrf_token %}
{{ form }}
<input type ="submit" class="btn btn-primary">
</form>
</div>
</div>
{% endblock %}
http://127.0.0.1:8000/accounts/create/
에서 이름이랑 비번 정한 후
http://127.0.0.1:8000/accounts/login/
을 하면
http://127.0.0.1:8000/accounts/profile/
로 감
=> login을 하고 어디로 가라는 지칭을 안했기 때문에 기본적으로 /profile/로 가게 됨

get/post 파라미터 중에 next value가 있다면 next로 이동
next가 없으면 setting.py 안에 있는 LOGIN_REDIRECT_URL, LOGOUT 경로가 있다면 이동
1번, 2번 둘 다 없다면 default 경로로 이동
<div>
<span>nav1</span>
<span>nav2</span>
<span>nav3</span>
{% if not user.is_authenticated %}
<!--이 유저가 로그인이 돼 있지 않다면 로그인을 보여주고 나머지 로그인 돼 있다면 로그아웃을 보여줄 것-->
<a href="{% url 'accountapp:login' %}?next={{ request.path }}">
<!--next=~ 써서 get 방식으로 값을 넘겨줌으로써 지금 있는 페이지 어디서든 그 url을 next 인자로 넘겨줌으로써 로그인을 하고 나서 원래 자리로 돌아올 수 있게됨 -->
<span>login</span>
</a>
{% else %}
<a href="{% url 'accountapp:logout' %}?next={{ request.path }}">
<span>logout</span>
</a>
{% endif %}
</div>
login으로 next 인자 없이 직접 로그인 경로로 들어가면 여전히 default 값으로 감
방지하기 위해 login_redirect_url 사용
setting.py에서
LOGIN_REDIRECT_URL=reverse_lazy('accountapp:hello_world')
LOGOUT_REDIRECT_URL = reverse_lazy('accountapp:login')
pip install django-bootstrap4{% load bootstrap4 %} 추가{% bootstrap_form form%} 추가하면 bootstrap이 적용된 폼을 사용 가능 @font-face {
font-family: 'NanumSquareRoundR';
src: local('NanumSquareRoundR'),
url("{% static 'fonts/NanumRoundR.otf' %}") format("opentype");
}
=> 프로젝트 내부의 어떤 경로에서든 폰트들 사용 가능
5. 적용하기 (html 전체에서 base 폰트로 지정)
base.html에서 코드 작성
<body style="font-family: 'NanumSquareR';">
장고에서 제공해주는 이름: Detail View (게인페이지 생성)
class AccountDetailView(DetailView):
model = User
template_name = 'accountapp/detail.html'
{% extends 'base.html' %}
{% block content %}}
<div>
<div style="text-align: center; max-width: 500px; margin: 4rem auto;">
<p>
{{ user.date_joined }}
</P>
<h2>
{{ user.username }}
</h2>
</div>
</div>
{% endblock %}
path('detail/<int:pk>', AccountDetailView.as_view(), name='detail'),
<int:pk> 특정 유저 객체에 부여된 고유한 키, pk라는 이름의 integer 정보를 /뒤에 받겠다. (몇 번 유저에 접근할 것인지)
context_object_name = 'target_user'
target_user.date_joined
=> 4번, 5번의 경우 다른 계정의 myPage를 들어갔을때 로그인한 유저의 Page가 아니라 선택한 그 계정의 Page가 보일 수 있도록 (다른 사람이 내 페이지에 오더라고 내 페이지의 정보 보여줌)
class AccountUpdateView(UpdateView):
model = User
form_class = UserCreationForm
success_url = reverse_lazy('accountapp:hello_world')
template_name = 'accountapp/update.html'
path('update/<int:pk>', AccountUpdateView.as_view(), name='update'),
{% if target_user == user %}
<a href="{% url 'accountapp:update' pk=user.pk %}">
<p>
Change Info
</p>
</a>
{% endif %}
target_user가 지금 접속한 유저와 같다면 링크 보여주게 함
위의 단계까지 했다면 아이디까지 바꿀 수 있게 됨 => change info를 들어갔을 때 user name 칸을 비활성화 시켜주는 작업 필요
5. accountapp에 파이썬 파일 새로 만듦 (forms.py)
class AccountUpdateForm(UserCreationForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['username'].disabled = True
# username이 아이디값 속성 중에서 disabled 값 True로 바꿈
# usercreationformdmf 상속받아 사용하는데, 마지막 줄이 없다면 usercreationform이랑 accountupdateform이랑 전혀 다르지 않은 폼
# 한 줄이 추가됨으로써 초기화 이후에 값을 하나 바꿔줌 username칸을 비활성화 시켜줌
브라우저 창에서(개발자 도구)에서 아이디 바꿔서 보낼 수 있음 (비활성화 돼있다하더라도)
=> 그래도 바뀌지 않음!
class AccountDeleteView(DeleteView):
model = User
success_url = reverse_lazy('accountapp:login')
template_name = 'accountapp/delete.html'
path('delete/<int:pk>', AccountDeleteView.as_view(), name='delete'),
<a href="{% url 'accountapp:delete' pk=user.pk %}">
<p>
Quit
</p>
</a>
<a href="{% url 'accountapp:create' %}">
<span>SignUp</span>
</a>
+) bug fix
views.py에서 update랑 delete view에 context_object_name = 'target_user' 추가
delete랑 update html 파일 pk=target_user.pk로 수정(account로 돼있음)
Account App 만든 이유 ) 처음에 만든 Hello World에 아무나 접근해서 글을 쓸 수 있다는 것이 문제 -> 중간에 '인증' 과정을 넣기 위해 account app 만듦
if request.user.is_authenticated:
# request안에 user객체 있고 그 안에 is_authenticated라는 메소드 존재
if request.method == "POST":
temp = request.POST.get('hello_world_input')
new_hello_world = HelloWorld()
new_hello_world.text = temp
new_hello_world.save()
# DB에 helloworld 객체 저장하게 됨
return HttpResponseRedirect(reverse('accountapp:hello_world'))
else:
hello_world_list = HelloWorld.objects.all()
# HelloWorld에 모든 데이터를 긁어올 수 있음
return render(request, 'accountapp/hello_world.html', context={'hello_world_list': hello_world_list})
else:
return HttpResponseRedirect(reverse('accountapp:login'))
# 요청을 받은 객체 안에서 method를 찾고 post일 경우 기존
=> if와 else문 추가
+) 이는 accountapp에도 적용해야함 (로그인하지도 않았는데 정보 수정 페이지 들어가면 안 됨)
해결해주기 위해 views.py에서 코드 추가 (update view와 delete view 둘 다 추가)
def get(self, *args, **kwargs):
if self.request.user.is_authenticated:
return super().get(*args, **kwargs)
else:
return HttpResponseRedirect(reverse('accountapp:login'))
# 로그인 돼있으면 기존의 방식대로 하되, 아니라면 login 창으로 되돌려 보내주기
def post(self, *args, **kwargs):
if self.request.user.is_authenticated:
return super().get(*args, **kwargs)
else:
return HttpResponseRedirect(reverse('accountapp:login'))
+) 이 상태에서 문제는 다른 사람의 정보 수정 페이지에 접근이 가능하고, 그 사람의 탈퇴 페이지까지 들어갈 수 있음 (탈퇴까지 가능)
해결해주기 위해 추가적으로 코드 작성
self.get_object() => self는 자체 뷰를 가리킴, 그 안에서 현재 사용되고 있는 object 중에서도 update view 같은 경우는 pk를 받는데, pk를 가지고 옴 (유저 객체)
그것이 지금 현재 request를 보내고 있는 유저와 같은지 확인 필요
다(update, delete view에) if self.request.user.is_authenticated and self.get_object() == self.request.user: 이 코드로 수정
그리고 else HttpResponseREdirect~가 아니라 HttpResponseForbidden()로 수정 (금지돼 있는 곳에 접근했음을 보여줌
: 함수 앞/뒤를 꾸며줌, 함수의 내부를 고치진 않지만 앞/뒤에 붙어서 꾸며줌!(코드 가독성 상승)업로드중..
@method_decorator(login_required, 'get')
@method_decorator(login_required, 'post')
class AccountUpdateView(UpdateView):
model = User
context_object_name = 'target_user'
form_class = AccountUpdateForm
success_url = reverse_lazy('accountapp:hello_world')
template_name = 'accountapp/update.html'
@method_decorator(login_required, 'get')
@method_decorator(login_required, 'post')
class AccountDeleteView(DeleteView):
model = User
context_object_name = 'target_user'
success_url = reverse_lazy('accountapp:login')
template_name = 'accountapp/delete.html'
이러면 본인인지 확인하는 과정은 삭제됨
from django.contrib.auth.models import User
from django.http import HttpResponseForbidden
def account_ownership_required(func):
def decorated(request, *args, **kwargs):
user = User.objects.get(pk=kwargs['pk'])
if not user == request.user:
return HttpResponseForbidden()
return func(request, *args, **kwargs)
return decorated
= 본인 확인 코드
요청을 받으면서 프라이머리 키로 받은 값을 가지고 있는 user object가 유저가 됨
pk를 확인해서 user object가 실제로 request 보낸 유저랑 같은지 아닌지 확인하고 아니라면 forbidden response 보냄
그리고 나서 decorator 적용 (다시 views.py)
@method_decorator(login_required, 'get')
@method_decorator(login_required, 'post')
@method_decorator(account_ownership_required, 'get')
@method_decorator(account_ownership_required, 'post')
class AccountUpdateView(UpdateView):
model = User
context_object_name = 'target_user'
form_class = AccountUpdateForm
success_url = reverse_lazy('accountapp:hello_world')
template_name = 'accountapp/update.html'
@method_decorator(login_required, 'get')
@method_decorator(login_required, 'post')
@method_decorator(account_ownership_required, 'get')
@method_decorator(account_ownership_required, 'post')
class AccountDeleteView(DeleteView):
model = User
context_object_name = 'target_user'
success_url = reverse_lazy('accountapp:login')
template_name = 'accountapp/delete.html'
has_ownership = [account_ownership_required, login_required] 이런 식으로 배열을 만들고 메서드 decorator안에 하나만 넣어주면 배열 내의 있는 decorator들을 모두 확인하고 체크해줌@method_decorator(has_ownership, 'get')
@method_decorator(has_ownership, 'post')
=> 원래 했던 인증시스템과 똑같이 동작, 훨씬 간결하게 코드 작성 가능