본문 바로가기

Pyhon/Django

[Pyhon/Django] 장고 기초#014. 장고 폼(ModelForm)

장고 폼(ModelForm)

지금까지는 Post 모델에 대해서 데이터를 읽고, 뷰를 통해 렌더링하는 작업을 진행했습니다. 장고에서 제공하는 폼 기능을 사용하면 모델의 객체를 생성하고, 수정하는 일을 쉽게 처리 할 수 있습니다.

가장 먼저 블로그 애플리케이션 디렉토리에 forms.py 파일을 생성합니다. 지금까지의 디렉토리 트리는 다음과 같습니다.

  • blog
    • migrations
      • ...
    • static
      • css
        • ...
    • templates 
      • blog
        • ...
    • forms.py
    • models.py
    • ...

forms.py에는 다음 내용을 입력합니다. 우선 장고에서 제공하는 forms 모듈을 import하고, Post 모델에 대해서 폼을 제공하기 위해 Post 모듈 역시 import 합니다.

from django import forms
from .models import Post

class PostForm(forms.ModelForm)
    class Meta:
        model = Post
        fields = ('title', 'text', )

PostForm 클래스는 우리가 작성한 사용자 폼에 해당합니다. 이를 위해 forms.ModelForm 클래스를 상속 받고, Meta 클래스를 정의해야 합니다. Meta는 어떤 모델을 참고할 것인지(model = Post), 폼에서 제공하는 필드(fields = ('title', 'text', )는 무엇인지를 정의합니다.

폼을 정의했으니 이제 폼을 이용하기 위한 기능을 제공해야 합니다. 포스트를 생성하기 위한 버튼을 추가하기 위해서 base.html에 다음 코드를 추가합니다. 이 문장은 '<div class="page-header">' 안쪽에 입력하면 됩니다.

<a href="{% url 'post_new' %}" class="top-menu"><span class="glyphicon glyphicon-plus"></span></a>

base.html의 최종 시안입니다. 장고 템플릿 태그를 이용하여 링크를 클릭 시 post_new 뷰를 호출하고 있습니다. 이번에는 뷰를 호출하면서 별도의 파라미터를 전달하지는 않습니다. 이전 문서처럼 뷰에 대한 정의가 아직 없으므로 페이지를 새로고침 하면 에러(NoReverseMatch)가 발생합니다.

{% load static %}
<html>
    <head>
        <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="/">Blog post list</a></h1>
        </div>
        <div class="content container">
            <div class="row">
                <div class="col-md-8">
                    {% block content %}
                    {% endblock %}
                </div>
            </div>
        </div>
    </body>
</html>

뷰에 연결하기 위해 urls.py에 url를 등록합니다. 위에서 작성한 것처럼 뷰의 이름은 post_new로 일치해야 하며, 실제 연결되는 url은 임의로 작성(post/new/)하면 됩니다. 

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'),
]

다음으로 views.py를 열어 다음과 같이 수정합니다. 폼 객체에 접근하기 위해서 PostForm 모듈을 import 합니다. post_new 뷰는 우리가 작성한 PostForm 객체를 새로 할당하여 템플릿에 제공합니다.

from django.shortcuts import render, get_object_or_404
from django.utils import timezone

from .models import Post
from .forms import PostForm

def post_list(request):
    posts = Post.objects.filter(published_date__lte=timezone.now()).order_by('published_date')
    return render(request, 'blog/post_list.html', { 'posts': posts })

def post_detail(request, pk):
    post = get_object_or_404(Post, pk=pk)
    return render(request, 'blog/post_detail.html', { 'post': post })

def post_new(request):
    form = PostForm()
    return render(request, 'blog/post_edit.html', { 'form': form })

템플릿 코드를 작성하기 위해 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 %}

템플릿 코드 중 csrf_token은 CSRF(Cross-Site Request Forgery) 공격에 대한 방어책으로 장고에서 사용하는 기능 중 하나입니다. 이 내용은 이번 문서에서는 생략하고 별도의 문서에서 다루도록 하겠습니다. 다음으로 등장하는 템플릿 태그 form.as_p는 폼을 문단(p)로 렌더링하라는 지시를 뜻합니다. form.as_table, form.as_ul 등을 사용하면 테이블 또는 리스트로 렌더링 할 것인지를 선택 할 수 있습니다. 이 역시 이번 문서에서는 크게 중요하지 않으므로 넘어가도록 하겠습니다.

여기까지 진행하였다면 http://127.0.0.1:8000/ 페이지에서 상단 + 버튼을 클릭하면, 아래 사진과 같이 포스트 객체를 생성하기 위한 웹 페이지로 이동하는 것을 확인 할 수 있습니다. 해당 폼을 제공하는 페이지는 post_edit.html의 코드 몇 줄로 완성되며, 이는 장고 프레임워크에서 제공하는 편의성 덕분입니다. 이어서 폼을 이용하여 객체를 생성하는 작업을 진행하겠습니다(아직 Submit 버튼을 클릭하여도 아무 일도 일어나지 않습니다!).