Django

Django form만들기

nyanguk 2021. 1. 21. 23:22

사용자에게 입력을 받거나 입력받은 데이터를 서버로 보내야 하는 것을 구현 할때 장고는 폼을 통해 이 기능을 수행한다.

지금까지는 관리자가 게시글을 작성하도록 했었다면 관리자가 아닌 일반 사용자가 URL에 접속하여 글을 쓰고 수정할 수 있는 기능을 추가한다.


form의 처리 로직

컴퓨터 네트워크에서 배웠듯이 GET요청은 서버에서 부터 데이터를 가져올때 POST요청은 입력된 데이터를 입력하거나 수정할때 사용한다. HTTP 요청 메세지의 body에 데이터를 실어 나르는 것이 바로 POST요청이다.

아래 처리 로직에 의해 Form이 동작하는 두가지 모드를 구현해야함을 알수 있다.

  • GET요청시 입력 양식을 보여주기
  • POST 요청시 입력된 데이터를 검증하고 데이터 저장하기

https://velog.io/@98jihyun/Django-form%EC%97%90-%EB%8C%80%ED%95%98%EC%97%AC


사용자의 글쓰기

먼저 blog 디렉토리 안에 forms.py를 추가해준다. views.py처럼 모든 뷰가 이안에 함수로 작성되었듯이 여러가지 form에 대한 코드는 모두 forms.py에 class로 작성한다.

모델과 모델 폼

만들고자하는 form의 필드를 직접 설정할 것인지 , model 작성시 사용했던 필드들을 이용할지 여부에 따라 class의 매개변수에 전달되는 인자가 달라지게 된다.

  • forms.Form을 전달하면 model을 만들때 처럼 생성한 class안에 필드를 명시해야한다.
  • forms.ModelForm을 전달하면 어떤 model의 필드를 사용할 것인지 Meta class를 통해 명시해야한다.
# blog/forms.py
from django import forms
from .models import Post

# From 사용
class PostForm(forms.ModelForm):
    title = models.CharField(max_length=200)
    text = models.TextField()

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

위의 코드에서 Form은 필드를 직접 설정해야하며 ModelForm은 Meta class에서 사용하고자 하는 필드의 출처를 model변수를 통해 명시 해야한다. 또한 fields 변수로 model에서의 필드중 무엇을 사용할 것인지도 명시해야한다.


폼 페이지 링크

앞으로 만들 폼 페이지에 접속시키기 위해 접속 링크를 추가해야한다. 어느 페이지에서든지 글쓰기가 가능한 페이지로 이동 할 수 있으므로 base.html에 그 링크를 만들어 준다.

class = title-1 인 div안에 아래 내용을 추가한다.

<a href="{% url 'post_new' %}" class="top-menu"><span class="glyphicon glyphicon-plus"></span></a>
<!-- blog/templates/blog/base.html -->
{% load static %}
<html>
    <head>
        <link href="https://fonts.googleapis.com/css2?family=Yeon+Sung&display=swap" rel="stylesheet">
        <link href="https://fonts.googleapis.com/css2?family=Do+Hyeon&family=Roboto&display=swap" rel="stylesheet">
        <link rel="stylesheet" href="{% static 'css/blog.css' %}">
        <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">
        <title>nyanguk의 Django 튜토리얼</title>
    </head>
    <body>
        <div class = title-1>
            <a href="{% url 'post_new' %}" class="top-menu"><span class="glyphicon glyphicon-plus"></span></a>
            <h1> <a href="">nyanguk의 블로그</a></h1>
        </div>
        <div class = post>
            {% block content %}
            {% endblock %}
        </div>
    </body>
</html>

* {% url %} 태그

url태그는 url 사용시 하드코딩을 막기 위해 유용하게 사용될 수 있다. 먼저 나의 어플리케이션 안의 urls.py 파일에서 정의한 url 패턴과 그 url에 할당된 name 을 주목하자!

접속할 url을 지정했고 그 url이 최초 접속에 필요한 것이 아니라 접속한 웹페이지내에서 사용될 경우 템플릿(html) 안에서 사용될 것이다. 그렇다면 일일히 이 urls.py에서 필요 url을 가져올 필요 없이 name을 검색하여 그에 해당하는 url을 가져오게 할수 있다. ''로 검색하고자 하는 name을 적어주고 그 뒤에 바로 전달할 인자를 적는다.

{% url 'post_detail' pk=post.pk %}
post_detail이라는 이름을 가진 url을 사용하고 pk=post.pk를 사용하여 채우도록한다.


url 설정

위에서 urls.py에서 post_new라는 name을 검색하게 만들었으므로 추가해주자

# blog/urls.py
from django.urls import path
from . import views
urlpatterns = [
    path(r'', views.post_list, name='post_list'),
    path(r'post/<int:pk>/', views.post_detail, name='post_detail'),
    path(r'post/new', views.post_new, name='post_new'),
]

 


views.py에 post_new 추가하기

다음과정은 말 안해도 자동으로 생각난다... 접속요청을 보냈을때 post_new함수가 실행되도록 추가해주자

이때 forms.py에서 정의한 Postform의 객체를 생성하여 form에 저장한다. 그리고 post_edit.html 템플릿으로 이동하도록 하였으며 생성된 객체인 form을 전달인자로 넘기도록 하였다. PostForm 객체를 생성하기 위해서는 forms.py 에서 불러와야 하므로 아래 코드를 넣어 줌으로써 불러올 수 있다.

from .forms import PostForm  
# blog/views.py
from django.shortcuts import render,get_object_or_404
from .models import Post
from .forms import PostForm
# Create your views here.
posts=Post.objects.all()
def post_list(request):
    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 만들기

블록 안에 코드를 작성하자! 여기서 form.as_p을 통해 입력을 위한 html을 생성한다. 종류로는 아래 세가지가 있다.

  • as_p
  • as_table
  • as_ul
<!-- 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 %}

views.py 에서 post_new 구체화하기

그런데 뭔가 빼먹은 듯한 느낌이 든다. 분명 Form 처리과정에서는 GET요청과 POST요청에 따라 다른 기능을 할 수있도록 만들어야 함을 알고 있는데 위의 views.py 코드에서는 단지 입력양식만을 보여주고 있다.

다시 한번 views.py를 수정 해보자!! 

 

가장 주된 논점은 GET요청시에는 입력 폼을 보여주고 POST요청시 입력 폼에서 작성한 데이터들이 어디에 담겨 있는지 알고 이를 데이터베이스에 저장해야한다.

post_edit.html에서 작성한 method ='POST'를 주목하자! 입력 폼에서 데이터를 입력하게 되면 request.POST라는 곳에 저장된다. 즉 views.py에서 post_new함수의 전달인자로 들어온 requset의 필드 POST가 정보를 가지고 있는 것이므로 post_new함수내에 데이터를 저장 시키는 코드가 필요하다.

요청메세지의 유형이 POST일 경우 데이터가 담긴 request.POST를 전달인자로 하는 객체를 생성해 form에 저장하고 post라는 새로운 변수에 입력 양식에서 저장된 데이터를 임시저장 한다.(commit =False는 바로 저장시키지 않도록 한다.) 글을 등록하기 위해서는 title과 text 뿐만아니라 author과 같은 추가 데이터가 필요하므로 post의 필드 값을 채운뒤 form.save()를 통해 저장시킨다

 

추가로 글 작성에 성공 했을때 내 글의 상세 페이지로 이동 시키기 위해서 redirect를 사용해 post_detail을 호출한다. 이때 템플릿이 아니라  URL을 호출하는데 글이 생성되면서 pk가 증가되어 post/pk/의 URL이 준비되어있으므로 redirect를 사용한다.

 

*  render와 redirect의 차이

render는 템플릿을 불러오고 redirect는 URL을 불러온다.
자세한 설명은 https://ssungkang.tistory.com/entry/Django-render-%EC%99%80-redirect-%EC%9D%98-%EC%B0%A8%EC%9D%B4 참고

 

render(request, template_name, context=None, content_type=None, status=None, using=None)
redirect(to, permanent=False, *args, **kwargs)

완성된 코드!

# blog/views.py
from django.shortcuts import render,get_object_or_404
from .models import Post
from .forms import PostForm
from django.shortcuts import redirect
from django.utils import timezone
# Create your views here.
posts=Post.objects.all()
def post_list(request):
    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):
    if request.method == "POST":        # 데이터를 저장해야할때
        form = PostForm(request.POST)
        if form.is_valid():
            post = form.save(commit=False)  # 바로저장 X
            post.author = request.user
            post.published_date = timezone.now()
            post.save()                     # 바로저장 O
            return redirect('post_detail', pk=post.pk)
    else:                               # 입력 양식을 보여줘야 할때 
        form = PostForm()
    return render(request, 'blog/post_edit.html', {'form': form})

 

정상적으로 동작하는 것을 확인 할 수 있다!!