Skip to content

장고 폼

한 가지만 더 하면 웹사이트가 완성됩니다.
바로 블로그 글을 추가하거나 수정하는 멋진 기능을 추가하는 것이죠.
폼을 하나 만들어서 Post 모델에 적용해봅시다.
장고의 모든 중요한 부분과 마찬가지로, 폼도 폼만의 forms.py라는 파일을 만들어요.
우리는 이 이름으로 blog 디렉토리 안에 파일을 만들 거에요.
blog
└── forms.py
이제 이 파일을 열고 아래 코드를 작성해보세요!
blog/forms.py
from django import forms

from .models import Post

class PostForm(forms.ModelForm):

class Meta:
model = Post
fields = ('title', 'text',)

위 코드를 보시면, ​Forms를 불러오고, 그 다음 Postmodels.py에서 불러오는 것입니다. ​PostForm 은 우리가 만들 폼의 이름이에요. 그리고 장고에다 이 폼이 ModelForm이라는 것을 알려줘야해요. ​forms.ModelForm로 ModelForm이라는 것을 장고에 알려줍니다!
class Meta가 나오는데요, 이 구문은 이 폼을 만들기 위해서 어떤 model이 쓰여야 하는지 장고에 알려주는 구문입니다. (model = Post)
마지막으로, 이 폼에 필드를 넣으면 완성되겠죠. 이번 폼에서는 titletext만 보여지게 해 봅시다. - author는 현재 로그인 하고 있는 사람이 될 것이고 (바로 당신이요!) 그리고 created_date는 글이 등록되는 시간이 될 것입니다. (예를 들어, 코드 상에서요), 됐죠?
마쳤습니다! 이제 에서 이 폼을 사용해 템플릿에서 보여주기만 하면 되네요.
다음에는 링크, URL, 뷰 그리고 템플릿을 만들 거에요.

폼과 페이지 링크

blog/templates/blog/base.html 파일을 열어봅시다. page-header 라는 div class에 링크를 하나 추가할 거에요.
blog/templates/blog/base.html
<a href="{% url 'post_new' %}" class="top-menu"><span class="glyphicon glyphicon-plus"></span></a>
이렇게 보이실 거에요!
blog/templates/blog/base.html
{% load static %}
<html>
<head>
<title>Django Girls blog</title>
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap.min.css">
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.2.0/css/bootstrap-theme.min.css">
<link href='//fonts.googleapis.com/css?family=Lobster&subset=latin,latin-ext' rel='stylesheet' type='text/css'>
<link rel="stylesheet" href="{% static 'css/blog.css' %}">
</head>
<body>
<div class="page-header">
<a href="{% url 'post_new' %}" class="top-menu"><span class="glyphicon glyphicon-plus"></span></a>
<h1><a href="/">Django Girls Blog</a></h1>
</div>
<div class="content container">
<div class="row">
<div class="col-md-8">
{% block content %}
{% endblock %}
</div>
</div>
</div>
</body>
</html>
앗, NoReverseMatch이라는 에러가 나타나죠? 걱정마세요 :)

URL

이제 blog/urls.py를 열고 아래 구문을 추가할게요!
blog/urls.py
path('post/new', views.post_new, name='post_new'),

urls.py전체 코드는 아래와 같습니다.
blog/urls.py
from django.urls import path
from . import views

urlpatterns = [
path('', views.post_list, name='post_list'),
path('post/<int:pk>/', views.post_detail, name='post_detail'),
path('post/new/', views.post_new, name='post_new'),
]
앗, 브라우저에 사이트를 다시 불러오면 AttributeError가 보이는데요? 왜냐하면, 아직 post_new뷰를 구현하지 않았기 때문이에요! 이제 하나 더 추가해보죠.

post_new view

blog/views.py파일을 열어서 from줄에 아래와 같은 코드를 추가합니다.
blog/views.py
from .forms import PostForm

def post_new(request):
form = PostForm()
return render(request, 'blog/post_edit.html', {'form': form})
Post 폼을 추가하기 위해 PostForm() 함수를 호출하도록 하여 템플릿에 넘깁니다. 곧 view 로 다시 돌아와서 이 작업을 하겠지만, 지금 당장은 폼을 위한 템플릿을 먼저 빨리 만들어보도록 할게요.

템플릿

이번에는 blog/templates/blog 디렉터리 안에 post_edit.html 파일을 생성해 폼이 작동할 수 있게 만들 거에요.
먼저 폼이 보여야 합니다. 그 예로, {{ form.as_p }}로 간단히 만들 수 있어요….
위 코드를 HTML 태그로 폼을 감싸세요. <form method="POST">...</form>
Save 버튼이 필요합니다. 이것은 HTML 버튼으로 만들 수 있어요: <button type="submit">Save</button>
마지막으로 <form ...>을 열어 {% csrf_token %}를 추가하세요. 이 작업은 폼 보안을 위해 중요하답니다! 이 작업을 빼먹고 저장하면 장고는 이렇게 불평할 거에요.
CSFR Forbidden page
네, 이제 post_edit.html 파일의 HTML을 확인해볼까요??
blog/templates/blog/post_edit.html
{% extends 'blog/base.html' %}

{% block content %}
<h1>New post</h1>
<form method="POST" class="post-form">{% csrf_token %}
{{ form.as_p }}
<button type="submit" class="save btn btn-default">Save</button>
</form>
{% endblock %}
서버를 켜보고 로 들어가봅시다.
New form
근데, 타이틀과 텍스트에다 내용을 입력하는건 되지만, 저장을 하게 되면 오류가 나올겁니다.

폼 저장하기

blog/views.py를 다시 여세요. 지금 여러분이 보고 있는 post_new뷰는 아래와 같을 거에요.
blog/views.py
def post_new(request):
form = PostForm()
return render(request, 'blog/post_edit.html', {'form': form})
폼을 제출할 때, 같은 뷰를 불러옵니다. 이때 request에는 우리가 입력했던 데이터들을 가지고 있는데, request.POST가 이 데이터를 가지고 있습니다. (POST는 글 데이터를 "등록하는(posting)"하는 것을 의미합니다. 블로그 "글"을 의미하는 "post"와 관련이 없어요) HTML에서 <form>정의에 method="POST"라는 속성이 있던 것이 기억나나요? 이렇게 POST로 넘겨진 폼 필드의 값들은 이제 request.POST에 저장됩니다. ​POST로 된 값을 다른 거로 바꾸면 안 돼요. ​method 속성의 값으로 넣을 수 있는 유효한 값 중에 GET같은 것도 있지만, post와 어떤 차이점이 있는지 등에 대해서 다루기에는 너무 길어질 것 같아 생략할게요)
이제 view 에서 두 상황으로 나누어 처리해볼게요.
첫 번째: 처음 페이지에 접속했을 때입니다. 당연히 우리가 새 글을 쓸 수 있게 폼이 비어있어야겠죠.
두 번째: 폼에 입력된 데이터를 view 페이지로 가지고 올 때입니다. 여기서 조건문을 추가시켜야 해요. (if를 사용하세요)
blog/views.py
if request.method == "POST":
[...]
else:
form = PostForm()

[...] 이 부분을 이제 추가해볼 것입니다! 만약 methodPOST라면, 폼에서 받은 데이터를 PostForm으로 넘겨줘야겠죠? 이렇게 코드를 작성하면 됩니다.
blog/views.py
form = PostForm(request.POST)
참 쉽죠! 다음에는 폼에 들어있는 값들이 올바른지를 확인해야합니다.(모든 필드에는 값이 있어야하고 잘못된 값이 있다면 저장하면 되지 않아야해요) 이를 위해 form.is_valid()을 사용할거에요.
폼에 입력된 값이 올바른지 확인한 다음, 저장되는거죠!
blog/views.py
if form.is_valid():
post = form.save(commit=False)
post.author = request.user
post.published_date = timezone.now()
post.save()
일반적으로 이 작업을 두 단계로 나눌 수 있어요. ​form.save()로 폼을 저장하는 작업과 작성자를 추가하는 작업입니다. (PostForm에는 작성자(author) 필드가 없지만, 필드 값이 필요하죠!) ​commit=False란 넘겨진 데이터를 바로 Post 모델에 저장하지는 말라는 뜻입니다. 왜냐하면, 작성자를 추가한 다음 저장해야 하니까요. 대부분의 경우에는 commit=False를 쓰지 않고 바로 form.save()를 사용해서 저장해요. 다만 여기서는 작성자 정보를 추가하고 저장해야 하므로 commit=False를 사용하는 거예요. ​post.save()는 변경사항(작성자 정보를 포함)을 유지할 것이고 새 블로그 글이 만들어질 거에요!
끝으로, 새 블로그 글을 작성한 다음에 post_detail페이지로 이동할 수 있으면 좋겠죠? 이 작업을 하려면 한 가지를 더 불러와야 해요. 위에 적어줍시다!
from django.shortcuts import redirect
그리고 새로 작성한 글을 볼 수 있도록 post_detail페이지로 가라고 수정합시다.
blog/views.py
return redirect('post_detail', pk=post.pk)
post_detail은 우리가 이동해야 할 뷰의 이름이에요 post_detail 뷰pk변수가 필요한 거 기억하고 있겠죠? pk=post.pk를 사용해서 뷰에게 값을 넘겨줄 거에요. 여기서 post는 새로 생성한 블로그 글이에요.
blog/views.py
def post_new(request):
if request.method == "POST":
form = PostForm(request.POST)
if form.is_valid():
post = form.save(commit=False)
post.author = request.user
post.published_date = timezone.now()
post.save()
return redirect('post_detail', pk=post.pk)
else:
form = PostForm()
return render(request, 'blog/post_edit.html', {'form': form})
views.pypost_new의 전체 코드입니다! 서버를 실행해보세요! 게시글이 이제 성공적으로 저장될 것입니다 :)
image.png
앗, 이렇게 오류가 뜨나요? 그렇다면 에서 본인의 아이디 비번으로 로그인 후, 다시 글을 추가해보시면 성공적으로 게시글이 추가될거에요!

폼 수정하기

추가를 했으면, 이제 글을 수정도 해야겠죠?
blog/templates/blog/post_detail.html
<a class="btn btn-default" href="{% url 'post_edit' pk=post.pk %}"><span class="glyphicon glyphicon-pencil"></span></a>
전체 코드입니다!
blog/templates/blog/post_detail.html
{% extends 'blog/base.html' %}

{% block content %}
<div class="post">
{% if post.published_date %}
<div class="date">
{{ post.published_date }}
</div>
{% endif %}
<a class="btn btn-default" href="{% url 'post_edit' pk=post.pk %}"><span class="glyphicon glyphicon-pencil"></span></a>
<h1>{{ post.title }}</h1>
<p>{{ post.text|linebreaksbr }}</p>
</div>
{% endblock %}
그 다음, blog/urls.pyblog/template/blog.post_edit.html템플릿을 사용할 코드를 추가합니다.
blog/urls.py
path('post/<int:pk>/edit/', views.post_edit, name='post_edit'),
blog/views.py파일을 열어서 post_edit을 위한 코드를 추가해봅시다.
blog/views.py
def post_edit(request, pk):
post = get_object_or_404(Post, pk=pk)
if request.method == "POST":
form = PostForm(request.POST, instance=post)
if form.is_valid():
post = form.save(commit=False)
post.author = request.user
post.published_date = timezone.now()
post.save()
return redirect('post_detail', pk=post.pk)
else:
form = PostForm(instance=post)
return render(request, 'blog/post_edit.html', {'form': form})
음…. 코드가 post_new와 거의 비슷해 보이지 않나요? 다만, 조금 다른 부분이 있어요!
첫 번째: url로부터 추가로 pk 매개변수를 받아서 처리합니다.
두 번째: get_object_or_404(Post, pk=pk)를 호출하여 수정하고자 하는 글의 Post 모델 인스턴스(instance)로 가져옵니다. (pk로 원하는 글을 찾습니다.) 이렇게 가져온 데이터를 폼을 만들 때와 (글을 수정할 때 폼에 이전에 입력했던 데이터가 있어야 하겠죠?) 폼을 저장할 때 사용하게 됩니다.
blog/views.py
form = PostForm(request.POST, instance=post)
그리고 폼에 아래와 같이 수정하세요.
blog/views.py
form = PostForm(instance=post)
post_detail페이지로 가보세요. 우측 상단에 수정 버튼이 있어야 합니다.
수정 버튼 ‘펜 모양' 을 누르면 우리가 쓴 블로그 글이 들어가 있는 폼을 볼 수 있을 거에요.
이제 수정하고 저장해보세요!

보안

링크를 클릭해 새로운 포스트가 나오게 만드는 것은 멋진 일이에요! 지금은 웹사이트를 방문하는 누구든지 글을 쓸 수 있지만, 그렇게 하고 싶지 않을 수 있어요. 나에게만 보이고 다른 사람에게는 보이지 않는 버튼을 만들어 볼게요.
blog/templates/blog/base.html 파일에서, page-header div를 찾아 아래와 같이 작성된 앵커 태그(Anchor Tag)를 찾습니다.
blog/templates/blog/base.html
<a href="{% url 'post_new' %}" class="top-menu"><span class="glyphicon glyphicon-plus"></span></a>

여기에 {% if %}태그를 추가해 관리자로 로그인한 유저들만 링크가 보일 수 있게 만들 거에요. 그게, 바로 여러분이죠! <a>태그를 아래와 같이 변경하세요.
blog/templates/blog/base.html
{% if user.is_authenticated %}
<a href="{% url 'post_new' %}" class="top-menu"><span class="glyphicon glyphicon-plus"></span></a>
{% endif %}

{% if %}는 브라우저에 페이지를 요청 하는 사용자가 로그인 하는 경우 링크가 발생됩니다. 이는 새 게시글을 완전히 보호해주는 것은 아니지만, 바람직한 방법입니다. 이 부분은 장고걸스 심화 튜토리얼에서 좀더 자세히 다룰거에요.
세부 페이지에 있는 수정 아이콘이 기억나죠? 이번에도 동일하게 다른 사람들이 게시글을 수정하지 못하게 할 거에요.
blog/templates/blog/post_detail.html파일을 열어 아래와 같이 작성된 라인을 찾아주세요.
<a class="btn btn-default" href="{% url 'post_edit' pk=post.pk %}"><span class="glyphicon glyphicon-pencil"></span></a>

이렇게 바꾸세요.
blog/templates/blog/post_detail.html
{% if user.is_authenticated %}
<a class="btn btn-default" href="{% url 'post_edit' pk=post.pk %}"><span class="glyphicon glyphicon-pencil"></span></a>
{% endif %}

로그인했기 때문에, 페이지 새로고침을 해도 아무것도 표시되지 않을 거에요. 새로운 브라우저 또는 시크릿 창을 실행해 보세요. 링크와 아이콘이 보이지 않게 되었어요!
다음 장으로 넘어가보죠

Want to print your doc?
This is not the way.
Try clicking the ··· in the right corner or using a keyboard shortcut (
CtrlP
) instead.