
class AccountCreateView(CreateView):
model = User
form_class = UserCreationForm
success_url = reverse_lazy('accountapp:hello_world')
template_name = 'accountapp/create.html'
app_name = "accountapp"
urlpatterns = [
path('hello_world/', hello_world, name='hello_world'),
path('create/', AccountCreateView.as_view(), name='create'),
]
{% extends 'base.html' %}
{% block content %}
<div style="text-align: center">
<form action="{% url 'accountapp:create' %}" method="post">
{% csrf_token %}
{{ form }}
<input type="submit" class="btn btn-primary">
</form>
</div>
{% endblock %}
<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' %}">
<span>logout</span>
</a>
{% endif %}
</div>
</div>
urlpatterns = [
path('hello_world/', hello_world, name='hello_world'),
path('create/', AccountCreateView.as_view(), name='create'),
path('login/', LoginView.as_view(template_name='accountapp/login.html'), name='login'),
path('login/', LogoutView.as_view(), name='logout'),
]
django에서 제공하는 User를 사용하면 view를 만들지 않고 간단하게 urls.py에서 작성하여 login, logout 구현 가능
(template는 따로 만들어야하기 때문에 login에서 template_name을 지정)
(view를 작성하지 않았기 때문에 login 성공 후 자동으로 account/profile로 이동)
{% 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 %}

1. post와 get 파라미터 중 next value가 존재한다면 next로 이동
2. next가 없다면 settings.py에 있는 login, logout 경로로 이동
3. setting.py에 경로가 없다면 default경로로 이동(-> accounts/profile)
header.html에 login, logout으로의 이동
<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' %}">
<span>logout</span>
</a>
{% endif %}
</div>
</div>
로그인이 되어있지 않다면 login으로 span 태그를 만들어서 누르면 a태그로 accountapp:login url로 이동
next를 사용하여 전에 있었던 페이지를 기억하고 login하면 next에 저장된 주소로 돌아감-> ?next={{ request.path }}(: get 방식)
(전의 페이지 없이 그냥 login주소에 접속했다면 next에 value가 없으므로 default 주소로 이동)
로그인이 되어있다면 logout으로 span 태그를 만들어 누르면 로그아웃되도록 url지정
settings.py
LOGIN_REDIRECT_URL = reverse_lazy('accountapp:hello_world')
LOGOUT_REDIRECT_URL = reverse_lazy('accountapp:login')
login, logout 성공시 이동할 url 지정
djnago-bootstrap4
페이지 안내에 따라 bootstrap4 설치
(페이지 Quickstrat)
{% 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 %}
{% bootstrap_form form %}
<input type="submit" class="btn btn-dark rounded-pill col-6 mt-3">
</form>
</div>
</div>
{% endblock %}
bootstrap4를 로드하고 "{% bootstrap_form form %}"으로 form을 넣는다.
<input type="submit" class="btn btn-dark rounded-pill col-6 mt-3">
: bootstrap으로 input 버튼 꾸미기

{% load static %}
<head>
<meta charset="UTF-8">
<title>Pragmatic</title>
<!-- BOOTSTRAP LINK -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-gH2yIJqKdNHPEq0n4Mqa/HGKIhSkIHeL5AyhkYV8i59U5AR6csBvApHHNl/vI1Bx" crossorigin="anonymous">
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.2.0/dist/js/bootstrap.bundle.min.js" integrity="sha384-A3rJD856KowSb7dwlZdYEkO39Gagi7vIsF0jrRAoQmDKKtQBHUuLZ9AsSv4jD4Xa" crossorigin="anonymous"></script>
<!-- GOOGLE FONTS LINKS -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Caveat&family=Noto+Sans+KR:wght@100&display=swap" rel="stylesheet">
<!-- DEFAULT CSS LINK -->
<link rel="stylesheet" type="text/css" href="{% static 'base.css' %}">
<style>
@font-face{
font-family: 'NanumSquareRoundR';
src: local('NanumSquareRoundR'),
url("{% static 'fonts/NanumSquareRoundR.ttf' %}") format("opentype");
}
@font-face{
font-family: 'NanumSquareRoundEB';
src: local('NanumSquareRoundEB'),
url("{% static 'fonts/NanumSquareRoundEB.ttf' %}") format("opentype");
}
@font-face{
font-family: 'NanumSquareRoundB';
src: local('NanumSquareRoundB'),
url("{% static 'fonts/NanumSquareRoundB.ttf' %}") format("opentype");
}
@font-face{
font-family: 'NanumSquareRoundL';
src: local('NanumSquareRoundL'),
url("{% static 'fonts/NanumSquareRoundL.ttf' %}") format("opentype");
}
</style>
</head>
<!DOCTYPE html>
<html lang="ko">
{% include 'head.html' %}
<body style="font-family: 'NanumSquareRoundR">
{% include 'header.html' %}
<hr>
{% block content %}
{% endblock %}
<hr>
{% include 'footer.html' %}
</body>
</html>
class AccountDetailView(DetailView):
model = User
template_name = 'accountapp/detail.html'
account의 view에서 DetailView를 만든다.
-> User 모델의 정보를 시각화하는 것만 구현하면 되기 때문에 model과 templat_name(정보를 볼 수 있는 html)만 정해주면 된다.
{% extends 'base.html' %}
{% block content %}
<div>
<div style="text-align: center; max-width: 500px; margin: 4rem auto;">
<p>
{{ user.data_joined }}
</p>
<h2>
{{ user.username }}
</h2>
</div>
</div>
{% endblock %}
path('deatil/<int:pk>', AccountDetailView.as_view(), name='deatil'),
deatil/<int:pk>': int인 pk라는 이름의 정보를 /뒤에 받아옴(몇번 유저인지 확인하기 위해)
<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:detail' pk=user.pk %}">
<span>MyPage</span>
</a>
<a href="{% url 'accountapp:logout' %}">
<span>logout</span>
</a>
{% endif %}
</div>
</div>
login 되어있는 상태에서만 myPage로 넘어갈 수 있는 span태그 생성
-> login된 User의 pk값을 받아서 pk에 저장하여 accountapp:detail에서 사용
(pk는 urls.py에서 path 지정할 때 필요하면 html에서 그 주소로 넘어갈 때 붙여줘야함)
-> 다른 계정의 myPage를 접속하였을 때 로그인한 유저의 Page가 아닌 선택한 계정의 Page가 보일 수 있도록하는 파라미터
view.py
class AccountDetailView(DetailView):
model = User
context_object_name = 'target_user'
template_name = '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: 'NanumSquareRoundB'">
{{ target_user.username }}
</h2>
</div>
</div>
{% endblock %}
class AccountUpdateView(CreateView):
model = User
context_object_name = 'target_user'
form_class = UserCreationForm
success_url = reverse_lazy('accountapp:hello_world')
template_name = 'accountapp/update.html'
CreateView와 유사
{% 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=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 %}
마찬가지로 CreateView와 유사
path('update/<int:pk>', AccountUpdateView.as_view(), name='update'),
deatil/<int:pk>': int인 pk라는 이름의 정보를 /뒤에 받아옴(몇번 유저인지 확인하기 위해)
{% 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: 'NanumSquareRoundB'">
{{ target_user.username }}
</h2>
{% if target_user == user %}
<a href="{% url 'accountapp:update' pk=user.pk %}">
<p>
Change Info
</p>
</a>
{% endif %}
</div>
</div>
{% endblock %}
class AccountUpdateForm(UserCreationForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.fields['username'].disabled = True
class AccountUpdateView(CreateView):
model = User
form_class = AccountUpdateForm
success_url = reverse_lazy('accountapp:hello_world')
template_name = 'accountapp/update.html'
class AccountDeleteView(DeleteView):
model = User
context_object_name = 'target_user'
success_url = reverse_lazy('accountapp:login')
template_name = 'accountapp/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-dark rounded-pill col-6 mt-3">
</form>
</div>
{% endblock %}
form이 필요없다.
path('delete/<int:pk>', AccountDeleteView.as_view(), name='delete'),
{% 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: 'NanumSquareRoundB'">
{{ target_user.username }}
</h2>
{% if target_user == user %}
<a href="{% url 'accountapp:update' pk=user.pk %}">
<p>
Change Info
</p>
</a>
<a href="{% url 'accountapp:delete' pk=user.pk %}">
<p>
Quit
</p>
</a>
{% endif %}
</div>
</div>
{% endblock %}
-> 인증 시스템 사용(ex 로그인했을 때만 글 작성이 가능하도록)
hello_world view에서 로그인 확인
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()
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'))
UpdateView와 DeleteView에서도 인증 시스템을 구현과 주소의 pk접근으로 자신의 계정이 아님에도 계정 수정이나 탈퇴가 가능하므로 이를 막아야한다.

이러한 방법은 적당한 방법이 아니다. 정확한 방법은 추후에 하기로 하자.

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()
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'))
-> -> -> ->
@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()
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})
-> 위의 login되어있는지 확인과 로그아웃이라면 로그인.html로 리턴하는 것을 @login_required를 통해 할 수 있음(코드의 가독성 증가)
@method_decorator(login_required, 'get')
@method_decorator(login_required, 'post')
class AccountUpdateView(CreateView):
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') : 일반 함수에서 사용하는 decorator(login_required)를 class method(method의 방식이 get또는 post)에서 사용할 수 있도록하는 decorator
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
해당 decorator를 사용하여 접속한 pk로 user를 get하여 현재 login된 user와 같은지 확인 후 리턴한다.
has_ownership = [account_ownership_required, login_required]
@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'
has_ownership = [account_ownership_required, login_required] -> 배열로 모든 decorator를 모두 확인하도록
python manage.py createsuperuser
username과 password 입력
path('admin/', admin.site.urls),
주소에 admin/에 접속하여 데이터 수정가능
settings.py에 작성
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, "media")
이미지 사용에 필요한 package: pillow
pip install pillow