Django Auth
사용자 신원 확인 > 권한을 부여하는 과정
웹에서는?
클라이언트 > URL로 신원을 요청하고
Server > 신원에 맞는 권한에 따라 html을 응답받는다.
서버는 나를 기억하지 못한다.
WHY? = HTTP(S) 특징
- connectionless - 요청에 대한 응답이 이뤄지면, 서버와 클라이언트는 남이 된다.
- sataeless - 서버는 클라이언트에 대해 기억하려고 하지 않는다.
session & cookie
세션 - 서버가 기억하고 있는 사용자에 대한 정보 또는 서버가 사용자를 기억하고있는 상태
쿠키 - 서버가 사용자를 기억하기 위해 사용자에게 간직하라고 건네주는 정보
실습
1. 가상환경 만들기
python -r venv venv
source venv/Scripts/activate
pip install -r requirements.txt
runserver
base.html
{% load bootstrap5 %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
{% bootstrap_css %}
<title>Document</title>
</head>
<body>
<div class="container">
<a href="#">Login</a>
{% block content %}
{% endblock %}
</div>
{% bootstrap_javascript %}
</body>
</html>
버튼 만들기
accounts 템플릿만들기(로그인만들기)
python manage.py startapp accounts
INSTALLED_APPS = [
# custom apps
'articles',
'bootstrap5',
#thrid_party libraris
'bootstrap5',
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
]
settings.py
accounts > urls.py
from django.urls import path
from . import views
app_name = 'accounts'
urlpatterns = [
#Login
#http://localhost:8000/accounts/login
path('login/', views.login, name='login'),
#Logout
]
accounts views.py
from django.shortcuts import render
from django.contrib.auth.forms import AuthenticationForm
# Create your views here.
def login(request):
#GET 요청일 때
#로그인 폼이 담긴 페이지를 보여줍니다.
form = AuthenticationForm()
context = {
'form':form,
}
return render(request, 'accounts/login.html', context)
accounts login.html
{% extends 'base.html' %}
{% block content %}
<form action="" method="POST">
{% csrf_token %}
{{ form }}
<button>로그인하기</button>
</form>
{% endblock %}
accounts urls.py
from django.urls import path
from . import views
app_name = 'accounts'
urlpatterns = [
#Login
#http://localhost:8000/accounts/login
path('login/', views.login, name='login'),
#Logout
]
accounts views.py
from django.shortcuts import render, redirect
from django.contrib.auth.forms import AuthenticationForm
from django.contrib.auth import login as auth_login
# Create your views here.
def login(request):
if request.method == 'POST':
form = AuthenticationForm(request, request.POST)
if form.is_valid():
#이 시점에서 로그인..
# 첫번쨰 인자에 요청 정보(request)
# 두번째 인자에 유저 객체(...?)
auth_login(request, form.get_user())# 반드시 호출
return redirect('articles:index')
else:
#GET 요청일 때
#로그인 폼이 담긴 페이지를 보여줍니다.
form = AuthenticationForm()
context = {
'form':form,
}
return render(request, 'accounts/login.html', context)
여기서 웹을 확인하여 로그인한다.
로그인 후에 데이터를 확인해보자
django_session(SQLITE)
그러면 데이터가 생성돼있을것이다.
CRUD
templates base.html
{% load bootstrap5 %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
{% bootstrap_css %}
<title>Document</title>
</head>
<body>
<div class="container">
<a href="#">Login</a>
<p>{{ request.user }}님, 안녕하세요.</p>
<p>{{ user }}님, 안녕하세요.</p>
{% block content %}
{% endblock %}
</div>
{% bootstrap_javascript %}
</body>
</html>
웹상에서는 똑같이 @@님 , 안녕하세요 라고 뜹니다.
settigs.py
TEMPLATES 를 보자
옵션에 context_prcessour 이 있다. 4개정도?
이친구들은 장고가 템플릿츠를 만들떄
request 는 리퀘스트 객체를 자동으로 담아줍니다.
auth 는 user를 담아줍니다.
MIDDLEWARE 을 보자.
request.user를 남는 녀석은 MIDDLEWAY에서 5번째되는녀석이 담아주는 것
결론 : request.user 와 user 은 같은 것이다.
user == request.user.
앞으로 적을때는 그냥 user라 적자.
로그아웃기능 만들기
base.html
{% load bootstrap5 %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
{% bootstrap_css %}
<title>Document</title>
</head>
<body>
<div class="container">
<a href="#">Login</a>
{% comment %} <p>{{ request.user }}님, 안녕하세요.</p> {% endcomment %}
<p>{{ user }}님, 안녕하세요.</p>
<a href="{% url 'account:logout' %}">Logout</a>
{% block content %}
{% endblock %}
</div>
{% bootstrap_javascript %}
</body>
</html>
urls.py
from django.urls import path
from . import views
app_name = 'accounts'
urlpatterns = [
#Login
#http://localhost:8000/accounts/login
path('login/', views.login, name='login'),
#Logout
path('logout/', views.logout, name='logout'),
]
views.py
from django.shortcuts import render, redirect
from django.contrib.auth.forms import AuthenticationForm
from django.contrib.auth import login as auth_login
from django.contrib.auth import logout as auth_logout
# Create your views here.
def login(request):
if request.method == 'POST':
form = AuthenticationForm(request, request.POST)
if form.is_valid():
#이 시점에서 로그인..
# 첫번쨰 인자에 요청 정보(request)
# 두번째 인자에 유저 객체(...?)
auth_login(request, form.get_user())# 반드시 호출
return redirect('articles:index')
else:
#GET 요청일 때
#로그인 폼이 담긴 페이지를 보여줍니다.
form = AuthenticationForm()
context = {
'form':form,
}
return render(request, 'accounts/login.html', context)
def logout(request):
# 로그아웃 == 세션을 삭제합니다.
auth_logout(request)
return redirect('articles:index')
base.html
{% load bootstrap5 %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
{% bootstrap_css %}
<title>Document</title>
</head>
<body>
<div class="container">
{% if user.is_authenticated %}
<p>{{ user }}님, 안녕하세요.</p>
<form action="{% url 'accounts:logout' %}" method="POST">
{% csrf_token %}
<button>Logout</button>
</form>
{% else %}
<a href="{% url 'accounts:login' %}">Login</a>
{% endif %}
{% block content %}
{% endblock %}
</div>
{% bootstrap_javascript %}
</body>
</html>
방어해야함 다시 view
from django.shortcuts import render, redirect
from django.contrib.auth.forms import AuthenticationForm
from django.contrib.auth import login as auth_login
from django.contrib.auth import logout as auth_logout
from django.views.decorators.http import require_POST
# Create your views here.
def login(request):
if request.method == 'POST':
form = AuthenticationForm(request, request.POST)
if form.is_valid():
#이 시점에서 로그인..
# 첫번쨰 인자에 요청 정보(request)
# 두번째 인자에 유저 객체(...?)
auth_login(request, form.get_user())# 반드시 호출
return redirect('articles:index')
else:
#GET 요청일 때
#로그인 폼이 담긴 페이지를 보여줍니다.
form = AuthenticationForm()
context = {
'form':form,
}
return render(request, 'accounts/login.html', context)
@require_POST
def logout(request):
# 로그아웃 == 세션을 삭제합니다.
# request에는 현재" 로그인"된 유저의 요청정보만 담겨저있다.
auth_logout(request)
return redirect('articles:index')
로그인이 돼있는 상태에서 로그인 주소를 가지고 로그인을 하면안된다 views.py 에서 방어해주자
from django.shortcuts import render, redirect
from django.contrib.auth.forms import AuthenticationForm
from django.contrib.auth import login as auth_login
from django.contrib.auth import logout as auth_logout
from django.views.decorators.http import require_POST
# Create your views here.
def login(request):
if request.user.is_authenticated:
# 로그인이 되어있다면... 메인으로 리다이렉트
return redirect('articles:index')
if request.method == 'POST':
form = AuthenticationForm(request, request.POST)
if form.is_valid():
#이 시점에서 로그인..
# 첫번쨰 인자에 요청 정보(request)
# 두번째 인자에 유저 객체(...?)
auth_login(request, form.get_user())# 반드시 호출
return redirect('articles:index')
else:
#GET 요청일 때
#로그인 폼이 담긴 페이지를 보여줍니다.
form = AuthenticationForm()
context = {
'form':form,
}
return render(request, 'accounts/login.html', context)
@require_POST
def logout(request):
# 로그아웃 == 세션을 삭제합니다.
# request에는 현재" 로그인"된 유저의 요청정보만 담겨저있다.
auth_logout(request)
return redirect('articles:index')
회원가입
base.html
{% load bootstrap5 %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
{% bootstrap_css %}
<title>Document</title>
</head>
<body>
<div class="container">
{% if user.is_authenticated %}
<p>{{ user }}님, 안녕하세요.</p>
<form action="{% url 'accounts:logout' %}" method="POST">
{% csrf_token %}
<button>Logout</button>
</form>
{% else %}
<a href="{% url 'accounts:login' %}">Login</a>
<a href="{% url 'accounts:signup'%}">signup</a>
{% endif %}
{% block content %}
{% endblock %}
</div>
{% bootstrap_javascript %}
</body>
</html>
urls.py
from django.urls import path
from . import views
app_name = 'accounts'
urlpatterns = [
#Login
#http://localhost:8000/accounts/login
path('login/', views.login, name='login'),
#Logout
path('logout/', views.logout, name='logout'),
path('signup/', views.signup, name='signup'),
]
views.py
from django.shortcuts import render, redirect
from django.contrib.auth.forms import AuthenticationForm
from django.contrib.auth import login as auth_login
from django.contrib.auth import logout as auth_logout
from django.views.decorators.http import require_POST
# Create your views here.
def login(request):
if request.user.is_authenticated:
# 로그인이 되어있다면... 메인으로 리다이렉트
return redirect('articles:index')
if request.method == 'POST':
form = AuthenticationForm(request, request.POST)
if form.is_valid():
#이 시점에서 로그인..
# 첫번쨰 인자에 요청 정보(request)
# 두번째 인자에 유저 객체(...?)
auth_login(request, form.get_user())# 반드시 호출
return redirect('articles:index')
else:
#GET 요청일 때
#로그인 폼이 담긴 페이지를 보여줍니다.
form = AuthenticationForm()
context = {
'form':form,
}
return render(request, 'accounts/login.html', context)
@require_POST
def logout(request):
# 로그아웃 == 세션을 삭제합니다.
# request에는 현재" 로그인"된 유저의 요청정보만 담겨저있다.
auth_logout(request)
return redirect('articles:index')
def signup(request):
form=
context = {
'form':form,
}
return render(request, 'accounts/signup.html', context)
signup.html(템플릿 만들어주기)
{% extends 'base.html' %}
{% block content %}
<form action="" method="POST">
{% csrf_token %}
{{ form }}
<button>회원가입하기</button>
</form>
{% endblock %}
views.py
추가 import
from django.contrib.auth.forms import UserCreationForm
def signup(request):
if request.method = 'POST':
form = UserCreationForm(request.POST)
if form.is_valid():
form.save() # 데이터베이스에 회원 정보 생성
return redirect('artivles:index')
else:
# GET
form = UserCreationForm()
context = {
'form':form,
}
return render(request, 'accounts/signup.html', context)
from django.shortcuts import render, redirect
from django.contrib.auth import login as auth_login
from django.contrib.auth import logout as auth_logout
from django.contrib.auth.forms import AuthenticationForm
from django.contrib.auth.forms import UserCreationForm
from django.views.decorators.http import require_POST
# Create your views here.
def login(request):
if request.user.is_authenticated:
# 로그인이 되어있다면... 메인으로 리다이렉트
return redirect('articles:index')
if request.method == 'POST':
form = AuthenticationForm(request, request.POST)
if form.is_valid():
#이 시점에서 로그인..
# 첫번쨰 인자에 요청 정보(request)
# 두번째 인자에 유저 객체(...?)
auth_login(request, form.get_user())# 반드시 호출
return redirect('articles:index')
else:
#GET 요청일 때
#로그인 폼이 담긴 페이지를 보여줍니다.
form = AuthenticationForm()
context = {
'form':form,
}
return render(request, 'accounts/login.html', context)
@require_POST
def logout(request):
# 로그아웃 == 세션을 삭제합니다.
# request에는 현재" 로그인"된 유저의 요청정보만 담겨저있다.
auth_logout(request)
return redirect('articles:index')
def signup(request):
if request.method = 'POST':
form = UserCreationForm(request.POST)
if form.is_valid():
form.save() # 데이터베이스에 회원 정보 생성
return redirect('artivles:index')
else:
# GET
form = UserCreationForm()
context = {
'form':form,
}
return render(request, 'accounts/signup.html', context)
##
회원 삭제
base.html
{% load bootstrap5 %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
{% bootstrap_css %}
<title>Document</title>
</head>
<body>
<div class="container">
{% if user.is_authenticated %}
<p>{{ user }}님, 안녕하세요.</p>
<form action="{% url 'accounts:logout' %}" method="POST">
{% csrf_token %}
<button>Logout</button>
</form>
<form action="{% url 'accounts:delete' %}" method ="POST">
{% csrf_token %}
<button>회원탈퇴</button>
</form>
{% else %}
<a href="{% url 'accounts:login' %}">Login</a>
<a href="{% url 'accounts:signup'%}">signup</a>
{% endif %}
{% block content %}
{% endblock %}
</div>
{% bootstrap_javascript %}
</body>
</html>
urls.py
from django.urls import path
from . import views
app_name = 'accounts'
urlpatterns = [
#Login
#http://localhost:8000/accounts/login
path('login/', views.login, name='login'),
#Logout
path('logout/', views.logout, name='logout'),
path('signup/', views.signup, name='signup'),
path('delete/', views.delete, name='delete'),
]
views.py
from django.shortcuts import render, redirect
from django.contrib.auth import login as auth_login
from django.contrib.auth import logout as auth_logout
from django.contrib.auth.forms import AuthenticationForm
from django.contrib.auth.forms import UserCreationForm
from django.views.decorators.http import require_POST
# Create your views here.
def login(request):
if request.user.is_authenticated:
# 로그인이 되어있다면... 메인으로 리다이렉트
return redirect('articles:index')
if request.method == 'POST':
form = AuthenticationForm(request, request.POST)
if form.is_valid():
#이 시점에서 로그인..
# 첫번쨰 인자에 요청 정보(request)
# 두번째 인자에 유저 객체(...?)
auth_login(request, form.get_user())# 반드시 호출
return redirect('articles:index')
else:
#GET 요청일 때
#로그인 폼이 담긴 페이지를 보여줍니다.
form = AuthenticationForm()
context = {
'form':form,
}
return render(request, 'accounts/login.html', context)
@require_POST
def logout(request):
# 로그아웃 == 세션을 삭제합니다.
# request에는 현재" 로그인"된 유저의 요청정보만 담겨저있다.
auth_logout(request)
return redirect('articles:index')
def signup(request):
if request.method == 'POST':
form = UserCreationForm(request.POST)
if form.is_valid():
user = form.save() # 데이터베이스에 회원 정보 생성
auth_login(request, user)
return redirect('articles:index')
else:
# GET
form = UserCreationForm()
context = {
'form':form,
}
return render(request, 'accounts/signup.html', context)
@require_POST #<<방어하기
def delete(request):
#request.user == 여기까지가 현재 로그인 된 유저 객체
#is_staff, is_active, is_authenticated, last_login ...
request.user.delete() #진짜로 지워버리기
#실제 계정 삭제 대신 비활성화하기
# request.user.is_active = False
# request.user.save()
return redirect('articles:index')
서버종료
git status
git add .
git status
git commit -m "Add signup & delete features"
원래 create 짜고 찍고
crud 짜고 찍고
즉, commit 은 기능별로 찍어서 보내라.
회원 정보 수정
urls.py
from django.urls import path
from . import views
app_name = 'accounts'
urlpatterns = [
#Login
#http://localhost:8000/accounts/login
path('login/', views.login, name='login'),
#Logout
path('logout/', views.logout, name='logout'),
path('signup/', views.signup, name='signup'),
path('delete/', views.delete, name='delete'),
path('update/', views.update, name='update'),
]
views.py
from django.shortcuts import render, redirect
from django.contrib.auth import login as auth_login
from django.contrib.auth import logout as auth_logout
from django.contrib.auth.forms import AuthenticationForm
from django.contrib.auth.forms import UserCreationForm
from django.contrib.auth.forms import UserChangeForm
from django.views.decorators.http import require_POST
# Create your views here.
def login(request):
if request.user.is_authenticated:
# 로그인이 되어있다면... 메인으로 리다이렉트
return redirect('articles:index')
if request.method == 'POST':
form = AuthenticationForm(request, request.POST)
if form.is_valid():
#이 시점에서 로그인..
# 첫번쨰 인자에 요청 정보(request)
# 두번째 인자에 유저 객체(...?)
auth_login(request, form.get_user())# 반드시 호출
return redirect('articles:index')
else:
#GET 요청일 때
#로그인 폼이 담긴 페이지를 보여줍니다.
form = AuthenticationForm()
context = {
'form':form,
}
return render(request, 'accounts/login.html', context)
@require_POST
def logout(request):
# 로그아웃 == 세션을 삭제합니다.
# request에는 현재" 로그인"된 유저의 요청정보만 담겨저있다.
auth_logout(request)
return redirect('articles:index')
def signup(request):
if request.method == 'POST':
form = UserCreationForm(request.POST)
if form.is_valid():
user = form.save() # 데이터베이스에 회원 정보 생성
auth_login(request, user)
return redirect('articles:index')
else:
# GET
form = UserCreationForm()
context = {
'form':form,
}
return render(request, 'accounts/signup.html', context)
@require_POST
def delete(request):
#request.user == 여기까지가 현재 로그인 된 유저 객체
#is_staff, is_active, is_authenticated, last_login ...
request.user.delete() #진짜로 지워버리기
#실제 계정 삭제 대신 비활성화하기
# request.user.is_active = False
# request.user.save()
return redirect('articles:index')
def update(request):
form = UserCreationForm(request)
context = {
'form'=form,
}
return render(request, 'accounts/update.html', context)html
forms.py(회원가입 폼 바꾸기)
from django.contrib.auth.forms import UserChangeForm
from django.contrib.auth.models import User
class CustomUserChangeForm(UserChangeForm):
class Meta:
model = User
fields = ('email', 'first_name', 'last_name')
forms.py (보충)
from django.contrib.auth.forms import UserChangeForm
from django.contrib.auth import get_user_model
class CustomUserChangeForm(UserChangeForm):
class Meta:
model = get_user_model
fields = ('email', 'first_name', 'last_name')
views.py (회원가입 폼 바꾸기)
from django.shortcuts import render, redirect
from django.contrib.auth import login as auth_login
from django.contrib.auth import logout as auth_logout
from django.contrib.auth.forms import AuthenticationForm
from django.contrib.auth.forms import UserCreationForm
from django.views.decorators.http import require_POST
from .forms import CustomUserChangeForm
# Create your views here.
def login(request):
if request.user.is_authenticated:
# 로그인이 되어있다면... 메인으로 리다이렉트
return redirect('articles:index')
if request.method == 'POST':
form = AuthenticationForm(request, request.POST)
if form.is_valid():
#이 시점에서 로그인..
# 첫번쨰 인자에 요청 정보(request)
# 두번째 인자에 유저 객체(...?)
auth_login(request, form.get_user())# 반드시 호출
return redirect('articles:index')
else:
#GET 요청일 때
#로그인 폼이 담긴 페이지를 보여줍니다.
form = AuthenticationForm()
context = {
'form':form,
}
return render(request, 'accounts/login.html', context)
@require_POST
def logout(request):
# 로그아웃 == 세션을 삭제합니다.
# request에는 현재" 로그인"된 유저의 요청정보만 담겨저있다.
auth_logout(request)
return redirect('articles:index')
def signup(request):
if request.method == 'POST':
form = UserCreationForm(request.POST)
if form.is_valid():
user = form.save() # 데이터베이스에 회원 정보 생성
auth_login(request, user)
return redirect('articles:index')
else:
# GET
form = UserCreationForm()
context = {
'form':form,
}
return render(request, 'accounts/signup.html', context)
@require_POST
def delete(request):
#request.user == 여기까지가 현재 로그인 된 유저 객체
#is_staff, is_active, is_authenticated, last_login ...
request.user.delete() #진짜로 지워버리기
#실제 계정 삭제 대신 비활성화하기
# request.user.is_active = False
# request.user.save()
return redirect('articles:index')
def update(request):
form = CustomUserChangeForm()
context = {
'form':form,
}
return render(request, 'accounts/update.html', context)
User = get_user_model
base.html
{% load bootstrap5 %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
{% bootstrap_css %}
<title>Document</title>
</head>
<body>
<div class="container">
{% if user.is_authenticated %}
<p>{{ user }}님, 안녕하세요.</p>
<form action="{% url 'accounts:logout' %}" method="POST">
{% csrf_token %}
<button>Logout</button>
</form>
<form action="{% url 'accounts:delete' %}" method ="POST">
{% csrf_token %}
<button>회원탈퇴</button>
</form>
<a href="{% url 'accounts:update' %}">회원정보수정</a>
{% else %}
<a href="{% url 'accounts:login' %}">Login</a>
<a href="{% url 'accounts:signup' %}">signup</a>
{% endif %}
{% block content %}
{% endblock %}
</div>
{% bootstrap_javascript %}
</body>
</html>
다끝나고
git commit -m "Add user update feature"
보충 버전 회원정보수정
forms.py
from django.contrib.auth.forms import UserChangeForm
from django.contrib.auth import get_user_model
class CustomUserChangeForm(UserChangeForm):
class Meta:
model = get_user_model
fields = ('email', 'first_name', 'last_name')
views.py
from django.shortcuts import render, redirect
from django.contrib.auth.forms import UserCreationForm, AuthenticationForm
from django.contrib.auth import login as auth_login
from django.contrib.auth import logout as auth_logout
from .forms import CustomUserChangeForm
# Create your views here.
def signup(request):
if request.method == 'POST':
form = UserCreationForm(request.POST)
if form.is_valid():
user = form.save()
return redirect('articles:index')
else:
form = UserCreationForm()
context = {
'form':form,
}
return render(request, 'accounts/signup.html', context)
def login(request):
if request.method == 'POST':
form = AuthenticationForm(request, request.POST)
if form.is_valid():
#로그인 => 세션을 생성. auth_login()
auth_login(request, form.get_user())
return redirect('articles:index')
else:
form = AuthenticationForm()
context ={
'form':form,
}
return render(request, 'accounts/login.html', context)
def logout(request):
if request.method == 'POST':
# 로그아웃 => 세션 삭제 : auth_logout
auth_logout(request)
return redirect('articles:index')
def update(request):
if request.method == 'POST':
form = CustomUserChangeForm(request.POST, instance=request.user)
if form.is_valid():
form.save
return redirect('articles:index')
else:
form = CustomUserChangeForm(instance=request.user)
context = {
'form':form,
}
return render(request, 'accounts/update.html', context)
'SSAFY > Django' 카테고리의 다른 글
[Django] SQL 과 장고 ORM 사용 방법 (0) | 2021.03.26 |
---|---|
[DJANGO] 댓글 만들기 (0) | 2021.03.25 |
[Django]Form (0) | 2021.03.21 |
[DJANGO] STATIC FILES (0) | 2021.03.19 |
Static 정적파일 & Media 파일 추가하기 (0) | 2021.03.18 |