Accountapp implementation
21강. CreateView를 통한 회원가입 구현
학습 목표 : 장고에서 제공하는 CreateView를 통해 Account의 Create,  즉 회원 가입을 구현해 본다.
1) pragmatic/accountApp/views.py
from django.contrib.auth.forms import UserCreationForm
from django.contrib.auth.models import User
from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import render
from django.urls import reverse, reverse_lazy
from accountApp.models import HelloWorld
from django.views.generic import CreateView
def hello_world(request):
# class-Based View로 AccountCreateView를 추가한다.
class AccountCreateView(CreateView):
    model = User  
    form_class = UserCreationForm
    success_url = reverse_lazy('accountApp:hello_world')  # 계정 만들기 성공 했으면, 어느 경로로 연결할 것인가?
    template_name = 'accountApp/create.html'  # 어느 html 파일을 통해서 볼지?
Python
복사
•
model = User  :  장고 기본 제공 model
•
form_class = UserCreationForm  :  장고 기본 제공 form
•
success_url = reverse_lazy(' ')  :  계정 생성 성공 시, 어느 경로로 연결 될 건지? (※  def는 reverse 사용, class는 reverse_lazy 사용)
•
template_name = ' '  :  어느 html 파일을 통해서 볼 건지?
2) pragmatic/accountApp/urls.py
from django.urls import path
from accountApp.views import hello_world, AccountCreateView
app_name = "accountApp"
urlpatterns = [
    path('hello_world/', hello_world, name='hello_world'),
		
		# create 경로를 추가한다.
    path('create/', AccountCreateView.as_view(), name='create'),
]
Python
복사
3) pragmatic/accountApp/templates/accountApp/create.html (create.html 생성)
{% extends 'base.html' %}
{% block content %}
    <div style="text-align: center">
        <form action="{% url 'accountApp:create' %}" method="post">
            <!-- post 사용 시, 잊지 말 것! -->
						{% csrf_token %} 
						<!-- views.py에서 지정한 class form -->
            {{ form }}  
            <input type="submit" class="btn btn-primary">
        </form>
    </div>
{% endblock %}
HTML
복사
22강. Login / Logout 구현
학습 목표 : 장고에서 제공되는 LoginView, LogoutView를 기반으로 로그인 및 로그아웃 기능을 구현한다.
1) pragmatic/accountApp/urls.py
from django.urls import path
from accountApp.views import hello_world, AccountCreateView
from django.contrib.auth.views import LoginView, LogoutView
app_name = "accountApp"
urlpatterns = [
    path('hello_world/', hello_world, name='hello_world'),
		
		# login과 logout 경로를 추가한다.
    path('login/', LoginView.as_view(template_name='accountApp/login.html'), name='login'),
    path('logout/', LogoutView.as_view(), name='logout'),
    path('create/', AccountCreateView.as_view(), name='create'),
]
Python
복사
2) pragmatic/accountApp/templates/accountApp/login.html (login.html 생성)
{% extends 'base.html' %}
{% block content %}
    <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 %}
HTML
복사
3) pragmatic/pragmatic/templates/header.html 
<div class="pragmatic_header">
         <div>
             <h1 class="pragmatic_logo">Pragmatic</h1>
         </div>
         <div>
             <span>nav1</span>
             <span>nav2</span>
             <span>nav3</span>
             {% if not user.is_authenticated %}
						 <!--유저가 로그인 되어 있지 않을 경우-->
             <a href="{% url 'accountApp:login' %}?next={{ request.path }}">
             <span>login</span>
             </a>
             {% else %} 
						 <!--유저가 로그인 되어 있을 경우-->
             <a href="{% url 'accountApp:logout' %}?next={{ request.path }}">
                 <span>logout</span>
             </a>
             {%endif %}
         </div>
    </div>
HTML
복사
4) pragmatic/pragmatic/settings.py
from pathlib import Path
import environ
import os
from django.urls import reverse_lazy
# 맨 하단에 Redirect 경로를 추가한다.
LOGIN_REDIRECT_URL = reverse_lazy('accountApp:hello_world')
LOGOUT_REDIRECT_URL = reverse_lazy('accountApp:login')
Python
복사
23강. Bootstrap을 이용한 Form 디자인 정리
학습 목표 : django-bootstrap4 라이브러리를 이용하여 form을 부트스트랩 스타일로 꾸며보고, 지금까지 만든 사이트의 디자인을 정리한다.
1) pragmatic/pragmatic/settings.py
우선, 터미널에서 pip install django-bootstrap4 입력하여 설치한다.
그 다음 settings.py에서 내용을 추가한다.
from pathlib import Path
import environ
import os
from django.urls import reverse_lazy
# Application definition
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'accountApp',
		
		# 추가한다.
    'bootstrap4', 
]
Python
복사
2) pragmatic/accountApp/templates/accountApp/login.html 
{% extends 'base.html' %}
{% load bootstrap4 %}
{% block content %}
		<!-- (문단의 스타일을 추가적으로 변경함) -->
    <div style="text-align: center; max-width: 500px; margin: 4rem auto">
        <div>
            <h4>Login</h4>
        </div>
        <div>
            <form action="" method="post">
                {% csrf_token %}
								<!-- 기존 {{ form }} 대신 bootstrap_form으로 변경한다.-->
                {% bootstrap_form form %}
                <input type="submit" class="btn btn-dark rounded-pill col-6 mt-3">
            </form>
        </div>
    </div>
{% endblock %}
HTML
복사
3) pragmatic/accountApp/templates/accountApp/create.html
{% extends 'base.html' %}
<!-- 추가한다. -->
{% load bootstrap4 %}
{% block content %}
		
		<!-- 변경한다. -->
    <div style="text-align: center; max-width: 500px; margin: 4rem auto">
        <div class="mb-4">
            <h4>SignUp</h4>
        </div>
        <form action="{% url 'accountApp:create' %}" method="post">
            {% csrf_token %}
            {% bootstrap_form form %}
            <input type="submit" class="btn btn-dark rounded-pill col-6 mt-3">
        </form>
    </div>
{% endblock %}
HTML
복사
4) pragmatic/templates/head.html (폰트를 전체적으로 변경하고 싶을 때)
우선, pragmatic/static 하단에 ‘fonts’라는 새 디렉토리를 생성한 후, 다운 받은 글꼴 파일을 삽입한다.
그 다음 head.html 파일에서 <style> 태그를 통해 내용을 추가한다.
{% load static %}
<head>
    <meta charset="UTF-8">
    <title>Pragmatic</title>
		<!-- 폰트 정보를 추가한다. -->
    <style>
        @font-face {
            font-family: 'NanumPen';
            src: local('NanumPen'),
            url("{% static 'fonts/NanumPen.ttf' %}") format("opentype");
        }
    </style>
</head>
HTML
복사
24강. DetailView를 이용한 개인 페이지 구현
학습 목표 : DetailView를 이용하여 개인 페이지를 만들어 본다.
1) pragmatic/pragmatic/views.py
from django.contrib.auth.forms import UserCreationForm
from django.contrib.auth.models import User
from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import render
from django.urls import reverse, reverse_lazy
# Create your views here.
from accountApp.models import HelloWorld
from django.views.generic import CreateView, DetailView
def hello_world(request):
class AccountCreateView(CreateView):
# AccountDetailView 클래스를 추가한다.
class AccountDetailView(DetailView):
    model = User
    context_object_name = 'target_user'
    template_name = 'accountApp/detail.html'
Python
복사
2) pragmatic/accountApp/templates/accountApp/detail.html (detail.html 생성)
{% extends 'base.html' %}
{% block content %}
  <div>
    <div style="text-align: center; max-width: 500px; margin: 4rem auto;">
      <p>
        {{ target_user.date_joined }}
      </p>
      <h2 style="font-family: 'NanumPen'">
        {{ target_user.username }}
      </h2>
    </div>
  </div>
{% endblock %}
HTML
복사
3) pragmatic/accountApp/urls.py
from django.contrib.auth.views import LoginView, LogoutView
from django.urls import path
from accountApp.views import hello_world, AccountCreateView, AccountDetailView
app_name = "accountApp"
urlpatterns = [
    path('hello_world/', hello_world, name='hello_world'),
    path('login/', LoginView.as_view(template_name='accountApp/login.html'), name='login'),
    path('logout/', LogoutView.as_view(), name='logout'),
    path('create/', AccountCreateView.as_view(), name='create'),
		# detail 경로를 추가한다.
    path('detail/<int:pk>', AccountDetailView.as_view(), name='detail'),
]
Python
복사
4) pragmatic/templates/header.html
<div class="pragmatic_header">
         <div>
             <h1 class="pragmatic_logo">Pragmatic</h1>
         </div>
         <div>
             <span>nav1</span>
             <span>nav2</span>
             <span>nav3</span>
             {% if not user.is_authenticated %}
						<!-- 유저가 로그인 되어 있지 않을 경우 -->
           
             {% else %}
						<!-- 유저가 로그인 되어 있을 경우 -->
						<!-- detail.html로 연결될 MyPage 바를 추가한다. -->
             <a href="{% url 'accountApp:detail' pk=user.pk %}">
                 <span>MyPage</span>
             </a>
             <a href="{% url 'accountApp:logout' %}?next={{ request.path }}">
                 <span>logout</span>
             </a>
             {%endif %}
         </div>
    </div>
HTML
복사
25강. UpdateView를 이용한 비밀번호 변경 구현
학습 목표 : UpdateView를 이용한 비밀번호 변경 구현을 진행한다.
1) pragmatic/accountApp/views.py
from django.contrib.auth.forms import UserCreationForm
from django.contrib.auth.models import User
from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import render
from django.urls import reverse, reverse_lazy
from accountApp.models import HelloWorld
from django.views.generic import CreateView, DetailView, UpdateView
from accountApp.forms import AccountUpdateForm
def hello_world(request):
class AccountCreateView(CreateView):
class AccountDetailView(DetailView):
# AccountCreatView 클래스를 토대로 AccountUpdateView를 추가한다.
class AccountUpdateView(UpdateView):
    model = User
		context_object_name = 'target_user'
    form_class = AccountUpdateForm # Form을 바꿔줌
    success_url = reverse_lazy('accountApp:hello_world')
    template_name = 'accountApp/update.html'
Python
복사
2) pragmatic/accountApp/urls.py
from django.contrib.auth.views import LoginView, LogoutView
from django.urls import path
from accountApp.views import hello_world, AccountCreateView, AccountDetailView, AccountUpdateView
app_name = "accountApp"
urlpatterns = [
    path('hello_world/', hello_world, name='hello_world'),
    path('login/', LoginView.as_view(template_name='accountApp/login.html'), name='login'),
    path('logout/', LogoutView.as_view(), name='logout'),
    path('create/', AccountCreateView.as_view(), name='create'),
    path('detail/<int:pk>', AccountDetailView.as_view(), name='detail'),
		# update 경로를 추가한다.
    path('update/<int:pk>', AccountUpdateView.as_view(), name='update'),
]
Python
복사
3) pragmatic/accountApp/templates/accountApp/update.html (update.html 생성)
<!-- create.html 내용을 토대로 작성함 -->
{% extends 'base.html' %}
{% load bootstrap4 %}
{% block content %}
    <div style="text-align: center; max-width: 500px; margin: 4rem auto">
        <div class="mb-4">
            <h4>Change Info</h4>
        </div>
        <form action="{% url 'accountApp:update' pk=target_user.pk %}" method="post">
            {% csrf_token %}
            {% bootstrap_form form %}
            <input type="submit" class="btn btn-dark rounded-pill col-6 mt-3">
        </form>
    </div>
{% endblock %}
HTML
복사
4) pragmatic/accountApp/templates/accountApp/detail.html 
{% extends 'base.html' %}
{% block content %}
  <div>
    <div style="text-align: center; max-width: 500px; margin: 4rem auto;">
      <p>
        {{ target_user.date_joined }}
      </p>
      <h2 style="font-family: 'NanumPen'">
        {{ target_user.username }}
      </h2>
			
			<!-- 로그인 했을 경우, 변경하고자 하는 유저가 본인일 경우 update 가능 (추가) -->
      {% if target_user == user %}
      <a href="{% url 'accountApp:update' pk=user.pk %}">
        <p>
          Change Info
        </p>
      </a>
      {% endif %}
    </div>
  </div>
{% endblock %}
HTML
복사
5) pragmatic/accountApp/forms.py (forms.py 생성)
# update 시, id 변경을 비활성화 하기 위한 용도
from django.contrib.auth.forms import UserCreationForm
class AccountUpdateForm(UserCreationForm):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.fields['username'].disabled = True
Python
복사
26강. DeleteView 기반 회원 탈퇴 구현
학습 목표 : DeleteView 기반 회원 탈퇴 구현을 진행한다.
1) pragmatic/accountApp/views.py
from django.contrib.auth.forms import UserCreationForm
from django.contrib.auth.models import User
from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import render
from django.urls import reverse, reverse_lazy
from accountApp.models import HelloWorld
from django.views.generic import CreateView, DetailView, UpdateView, DeleteView
from accountApp.forms import AccountUpdateForm
def hello_world(request):
class AccountCreateView(CreateView):
class AccountDetailView(DetailView):
class AccountUpdateView(UpdateView):
# AccountDeleteView를 추가한다.
class AccountDeleteView(DeleteView):
    model = User
		context_object_name = 'target_user'
    success_url = reverse_lazy('accountApp:login')
    template_name = 'accountApp/delete.html'
Python
복사
2) pragmatic/accountApp/urls.py
from django.contrib.auth.views import LoginView, LogoutView
from django.urls import path
from accountApp.views import hello_world, AccountCreateView, AccountDetailView
from accountApp.views import AccountUpdateView, AccountDeleteView
app_name = "accountApp"
urlpatterns = [
    path('hello_world/', hello_world, name='hello_world'),
    path('login/', LoginView.as_view(template_name='accountApp/login.html'), name='login'),
    path('logout/', LogoutView.as_view(), name='logout'),
    path('create/', AccountCreateView.as_view(), name='create'),
    path('detail/<int:pk>', AccountDetailView.as_view(), name='detail'),
    path('update/<int:pk>', AccountUpdateView.as_view(), name='update'),
		
		# delete 경로를 추가한다.
		path('delete/<int:pk>', AccountDeleteView.as_view(), name='delete'),
]
Python
복사
3) pragmatic/accountApp/templates/accountApp/delete.html (delete.html 생성)
{% extends 'base.html' %}
{% block content %}
    <div style="text-align: center; max-width: 500px; margin: 4rem auto">
        <div class="mb-4">
            <h4>Quit</h4>
        </div>
        <form action="{% url 'accountApp:delete' pk=target_user.pk %}" method="post">
            {% csrf_token %}
            <input type="submit" class="btn btn-danger rounded-pill col-6 mt-3">
        </form>
    </div>
{% endblock %}
HTML
복사
4) pragmatic/accountApp/templates/accountApp/detail.html 
{% extends 'base.html' %}
{% block content %}
  <div>
    <div style="text-align: center; max-width: 500px; margin: 4rem auto;">
      <p>
        {{ target_user.date_joined }}
      </p>
      <h2 style="font-family: 'NanumPen'">
        {{ target_user.username }}
      </h2>
			
      {% if target_user == user %}
      <a href="{% url 'accountApp:update' pk=user.pk %}">
        <p>
          Change Info
        </p>
      </a>
			<!-- Quit이 가능한 경우를 추가해 준다. -->
			<a href="{% url 'accountApp:delete' pk=user.pk %}">
        <p>
          Quit
        </p>
      </a>
      {% endif %}
    </div>
  </div>
{% endblock %}
HTML
복사
5) pragmatic/templates/header.html
<div class="pragmatic_header">
         <div>
             <h1 class="pragmatic_logo">Pragmatic</h1>
         </div>
         <div>
             <span>nav1</span>
             <span>nav2</span>
             <span>nav3</span>
             {% if not user.is_authenticated %}
						 <!-- 유저가 로그인 되어 있지 않을 경우 -->
	           <a href="{% url 'accountApp:login' %}?next={{ request.path }}">
                <span>login</span>
             </a>
             <a href="{% url 'accountApp:create' %}">
                <span>SignUp</span>
             </a>
             {% else %}
						 <!-- 유저가 로그인 되어 있을 경우 -->
             <a href="{% url 'accountApp:detail' pk=user.pk %}">
                 <span>MyPage</span>
             </a>
             <a href="{% url 'accountApp:logout' %}?next={{ request.path }}">
                 <span>logout</span>
             </a>
             {%endif %}
         </div>
    </div>
HTML
복사
Authentication
27강. Authentication 인증 시스템 구축
학습 목표 : 요청을 보내는 유저가 로그인을 했는지, 또 접근하는 페이지의 성격에 따라 해당 페이지의 유저 객체와 요청 유저 객체를 비교하고, 필요할 경우 요청을 차단하는 코드를 구축한다. 
1) pragmatic/accountApp/views.py
from django.contrib.auth.forms import UserCreationForm
from django.contrib.auth.models import User
from django.http import HttpResponse, HttpResponseRedirect
from django.shortcuts import render
from django.urls import reverse, reverse_lazy
from accountApp.models import HelloWorld
from django.views.generic import CreateView, DetailView, UpdateView, DeleteView
from accountApp.forms import AccountUpdateForm
from django.http import HttpResponseForbidden
def hello_world(request):
class AccountCreateView(CreateView): 
class AccountDetailView(DetailView):
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'
    # 로그인 되어 있을 때만 Update 가능 + 타 계정으로는 접속 불가능
    def get(self, *args, **kwargs):
        if self.request.user.is_authenticated and self.get_object() == self.request.user:
            return super().get(*args, **kwargs)
        else:
            return HttpResponseForbidden()
    def post(self, *args, **kwargs):
        if self.request.user.is_authenticated and self.get_object() == self.request.user:
            return super().post(*args, **kwargs)
        else:
            return HttpResponseForbidden()
class AccountDeleteView(DeleteView):
    model = User
    context_object_name = 'target_user'
    success_url = reverse_lazy('accountApp:login')
    template_name = 'accountApp/delete.html'
    # 로그인 되어 있을 때만 Delete 가능 + 타 계정으로는 접속 불가능
    def get(self, *args, **kwargs):
        if self.request.user.is_authenticated and self.get_object() == self.request.user:
            return super().get(*args, **kwargs)
        else:
            return HttpResponseForbidden()
    def post(self, *args, **kwargs):
        if self.request.user.is_authenticated and self.get_object() == self.request.user:
            return super().post(*args, **kwargs)
        else:
            return HttpResponseForbidden()
Python
복사
28강. Decorator를 이용한 코드 간소화
학습 목표 : 파이썬의 Decorator 패턴을 이용해 자주 쓰이는 코드들을 줄여본다. 장고에서 기본 제공하는 Decorator부터 커스터마이징 한 Decorator로 27강에서 만든 인증시스템 코드 볼륨을 줄인다.
함수의 내부를 변경시키지는 않지만, 함수를 앞 뒤로 꾸며주는 역할을 한다.
Function
1) Decorator 적용 전
...
def hello_world(request):
    if 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()
            hello_world_list = HelloWorld.objects.all()
            return HttpResponseRedirect(reverse('accountApp:hello_world'))
        else:
            hello_world_list = HelloWorld.objects.all()
            return render(request, 'accountApp/hello_world.html', context={'hello_world_list': hello_world_list})
    else:
        return HttpResponseRedirect(reverse('accountApp:login'))
...
Python
복사
2) Decorator 적용 후 (장고 기본 제공)
from django.contrib.auth.decorators import login_required
...
@login_required
def hello_world(request):
    if request.method == "POST":
        temp = request.POST.get('hello_world_input')
        new_hello_world = HelloWorld()
        new_hello_world.text = temp
        new_hello_world.save()
        hello_world_list = HelloWorld.objects.all()
        return HttpResponseRedirect(reverse('accountApp:hello_world'))
    else:
        hello_world_list = HelloWorld.objects.all()
        return render(request, 'accountApp/hello_world.html', context={'hello_world_list': hello_world_list})
...
Python
복사
Method (class 내부)
1) Decorator 적용 전
...
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'
    def get(self, *args, **kwargs):
        if self.request.user.is_authenticated and self.get_object() == self.request.user:
            return super().get(*args, **kwargs)
        else:
            return HttpResponseForbidden()
    def post(self, *args, **kwargs):
        if self.request.user.is_authenticated and self.get_object() == self.request.user:
            return super().post(*args, **kwargs)
        else:
            return HttpResponseForbidden()
class AccountDeleteView(DeleteView):
    model = User
    context_object_name = 'target_user'
    success_url = reverse_lazy('accountApp:login')
    template_name = 'accountApp/delete.html'
    def get(self, *args, **kwargs):
        if self.request.user.is_authenticated and self.get_object() == self.request.user:
            return super().get(*args, **kwargs)
        else:
            return HttpResponseForbidden()
    def post(self, *args, **kwargs):
        if self.request.user.is_authenticated and self.get_object() == self.request.user:
            return super().post(*args, **kwargs)
        else:
            return HttpResponseForbidden()
Python
복사
2) Decorator 적용 후 (장고 기본 제공)
※ @method_decorator(function, ‘적용할 method’) 
  : 일반 function에 사용하는 decorator를 method에 사용할 수 있도록 변환해주는 decorator
from django.contrib.auth.decorators import login_required
from django.utils.decorators import method_decorator
...
@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'
Python
복사
직접 만든 Decorator 적용하기
1) pragmatic/accountApp/decorators.py (decorators.py 생성)
from django.contrib.auth.models import User
from django.http import HttpResponseForbidden
# (get, post 모두) pk를 확인해서, User.objects가 실제 request를 보낸 유저와 같은지 판별
def account_ownership_required(func):
    def decorated(request, *args, **kwargs):
        # 요청을 받으면서 pk(primary key)로 'pk' 값을 가지고 있는 유저가 user가 된다.
        user = User.objects.get(pk=kwargs['pk'])
        # user를 확인해서, 다른 경우 Forbidden 된다.
        if not user == request.user:
            return HttpResponseForbidden()
        return func(request, *args, **kwargs)
    return decorated
Python
복사
2) Decorator 적용 후 1 (직접 만든 Decorator 적용)
from django.contrib.auth.decorators import login_required
from django.utils.decorators import method_decorator
from accountApp.decorators import account_ownership_required
...
@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'
Python
복사
3) Decorator 적용 후 2 (직접 만든 Decorator 적용, 코드 간소화)
from django.contrib.auth.decorators import login_required
from django.utils.decorators import method_decorator
from accountApp.decorators import account_ownership_required
# 배열로 만든다. (method_decorator에 넣으면, 배열 내에 있는 decorator들을 다 확인함)
has_ownership = [account_ownership_required, login_required()]
...
@method_decorator(has_ownership, 'get')
@method_decorator(has_ownership, '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(has_ownership, 'get')
@method_decorator(has_ownership, 'post')
class AccountDeleteView(DeleteView):
    model = User
    context_object_name = 'target_user'
    success_url = reverse_lazy('accountApp:login')
    template_name = 'accountApp/delete.html'
Python
복사
29강. superuser, media 관련 설정
학습 목표 : createsuperuser 명령어를 통한 관리자 계정 생성, 그리고 Media 파일을 다루기 위해 필요한 설정들을 진행한다.
1) superuser(관리자) 계정 생성
jangs@DESKTOP-70KJSMB MINGW64 ~/PycharmProjects/pragmatic_2/pragmatic (master)
$ python manage.py createsuperuser
Username (leave blank to use 'jangs'): username입력
Email address: (생략 가능)
Password: password 입력
Password (again): password 입력
Superuser created successfully.
jangs@DESKTOP-70KJSMB MINGW64 ~/PycharmProjects/pragmatic_2/pragmatic (master)
$ python manage.py createsuperuser
Username (leave blank to use 'jangs'): jang
Email address:
Password:
Password (again):
Error: Blank passwords aren't allowed.
Password:
Password (again):
Superuser created successfully.
2) pragmatic/pragmatic/settings.py
step 1. 하단에 MEDIA_URL과 MEDIA_ROOT를 추가한다.
...
STATIC_URL = 'static/'
STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')
STATICFILES_DIRS = [
    BASE_DIR / "static",
]
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
LOGIN_REDIRECT_URL = reverse_lazy('accountApp:hello_world')
LOGOUT_REDIRECT_URL = reverse_lazy('accountApp:login')
# 실제 미디어 파일에 접근하기 위해, 주소창에서 media ~(이하) 필요하다.
MEDIA_URL = '/media/'
# 미디어 파일을 서버에 올렸을 때, 어느 경로에 지정될 것인지에 대한 정보
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
Python
복사
step 2. 터미널 창에서 $ pip install pillow 를 입력하여 설치한다.
Profileapp Implementation
30강. Profileapp 시작 그리고 ModelForm
학습 목표 : 새로운 앱 Profileapp을 시작한다. 그리고 모델 기반으로 간편하게 Form을 만들어 주는 ModelForm을 짚고 넘어간다.
Profileapp 시작
•
account 앱과 profile 객체를 1:1로 매칭 시킨다.  (즉, account 하나에 하나의 profile)
•
Profile Image / Profile Nickname / Profile Message 세 가지로 구성된다.
•
Delete View와 Detail View는 구현하지 않는다.
1) profileApp 생성
터미널 창에 $ python manage.py startapp profileApp 를 입력한다.
2) pragmatic/pragmatic/settings.py
from pathlib import Path
import environ
import os
from django.urls import reverse_lazy
# Application definition
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'accountApp',
    'bootstrap4', 
		
		# 추가한다.
    'profileApp', 
]
Python
복사
3) pragmatic/pragmatic/urls.py
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
    path('admin/', admin.site.urls),
    path('accounts/', include('accountApp.urls')),
		
		# profiles 경로를 추가한다.
    path('profiles/', include('profileApp.urls')),
]
Python
복사
4) pragmatic/profileAPP/urls.py (urls.py 생성)
app_name = 'profileApp'
urlpatterns = [
	# 추가로 작성할 것
]
Python
복사
5) pragmatic/profileAPP/models.py
from django.contrib.auth.models import User
from django.db import models
class Profile(models.Model):
    user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='profile')
    image = models.ImageField(upload_to='profile/', null=True)
    nickname = models.CharField(max_length=20, unique=True, null=True)
    message = models.CharField(max_length=100, null=True)
Python
복사
6) pragmatic/profileAPP/forms.py (forms.py 생성)
from django.forms import ModelForm
from profileApp.models import Profile
class ProfileCreationForm(ModelForm):
    class Meta:
        model = Profile
        fields = ['image', 'nickname', 'message']
Python
복사

.png&blockId=7f8101a5-baff-44aa-b922-e1595acf0b2b)
.png&blockId=ec1b978b-88a6-47a7-aea4-5dc900078e1d)