웹프로그래밍/Django

[Djnago] ModelForm 알아보기

ssung.k 2019. 8. 12. 07:17

ModelForm

model 과 form 은 연관이 되어있습니다. 대체적으로 model의 field 에 해당하는 form filed 를 만들기 때문에 models.pyforms.py 를 작성하면 이는 대체로 비슷한 양상을 보입니다. 하지만 따로 작성하게 되면 각자를 따로 관리해줘야 하기 때문에 번거롭습니다. 다행히도 둘을 연결 시킨 ModelForm 이 존재하고 이는 form 을 상속받아 form 처럼 동작할 수 있으면서 model 과 쉽게 연결지을 수 있습니다. 지정한 model로 부터 필드를 읽어들어서 Form Fields 를 세팅 함으로서 field 를 하나하나 설정해줄 필요가 없습니다. 간단한 예시가 아래 나와있습니다.

# forms.py

class PostForm(forms.ModelForm):
  class Meta:
    model = Post
    fields = '__all__'

 

ModelForm 을 통한 create 와 edit

기존에는 핵심이 되는 views 코드만 있었지만 전체적인 코드를 공개해달라는 요청이 많아서 전체적인 코드를 올리겠습니다.

기본적인 Post 모델과 이와 연결되는 ModelForm 을 만들었습니다.

# models.py

from django.db import models

class Post(models.Model):
    title = models.CharField(max_length=10)
    content = models.TextField()
# forms.py

from django import forms
from . models import Post

class PostForm(forms.ModelForm):
    class Meta:
        model = Post
        fields = '__all__'

전체적인 post 들을 보여주는 home.html 과 글을 수정하거나 생성하는 post_form.html 입니다.


<!-- core/home.html -->

<h1>메인 화면</h1>

<a href="{% url 'post_create' %}">글쓰기</a>
<hr>
{% for post in post_list %}
    <h3>{{ post.title }}</h3>
    <div>{{ post.content }}</div>
    <a href="{% url 'post_edit' post.pk %}">수정</a>
    <hr>
{% endfor %}
<!-- core/post_form.html -->

<form action="" method="POST">
    {% csrf_token %}
    <table>
        {{form.as_table}}
    </table> 
    <input type="submit" value="제출">
</form>

home, 글 생성, 글 수정에 대한 url 입니다.

# urls.py

from django.urls import path
from . import views

urlpatterns = [
    path('',views.home, name="home"),
    path('post_create/', views.post_create, name="post_create"),
    path('post_edit/<int:pk>', views.post_edit, name="post_edit"),
]

글을 생성하고 수정하는 내용입니다.

수정시에는 instance=post 를 지정해줌으로서 기존에 있던 데이터를 불러올 수 있습니다.

# views.py

from django.shortcuts import render,redirect, get_object_or_404
from .forms import PostForm
from .models import Post

def home(request):
    post_list = Post.objects.all()
    context = {
        'post_list': post_list,
    }
    return render(request, "core/home.html", context)

def post_create(request):
    if request.method == "POST":
        form = PostForm(request.POST, request.FILES)
        if form.is_valid():
            post = form.save()
            return redirect('home')
    else:
        form = PostForm()
    context = {
        'form' : form
    }
    return render(request, 'core/post_form.html', context)


def post_edit(request, pk):
    post = get_object_or_404(Post,pk=pk)
    if request.method == "POST":
        form = PostForm(request.POST, request.FILES, instance=post)
        if form.is_valid():
            post = form.save()
            return redirect(home)
    else:
        form = PostForm(instance=post)
    context = {
        'form': form,
    }
    return render(request, 'core/post_form.html', context)

 

저장을 잠시 미루기 위한 commit=False

현재까지의 코드는 사용자에게 form을 통해 입력받은 내용을 저장해서 새로운 인스턴스를 만듭니다. 지금의 문제는 사용자에게 입력 받는 것이 아닌 views 에서 로직을 통해 새로운 값을 넣을 수 없습니다. 이를 해결해보도록 하겠습니다.

기존의 Post model 에 대해서 ip 라는 필드를 추가하였습니다. 해당 필드는 사용자로 부터 값을 받는 것이 아니라 views 에서 로직을 통해 추가합니다.

# models.py

from django.db import models

class Post(models.Model):
    title = models.CharField(max_length=10)
    content = models.TextField()
    ip = models.CharField(max_length=20)

    def __str__(self):
        return self.title

이에 따라 forms 도 변경했습니다. all 로 지정시에는 ip도 받는 form 이 생기기 때문에 앞선 두 개의 필드만 지정해주었습니다.

# forms.py

from django import forms
from . models import Post

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

이제 값을 저장하지 않은 체로 post 를 받아와서 ip 필드를 채워준 후 다시 저장해주도록 합니다. commit=False 를 통해 post 에 해당하는 instance 들은 저장을 잠시 미룰 수 있습니다.

# views.py

def post_create(request):
    if request.method == "POST":
        form = PostForm(request.POST, request.FILES)
        if form.is_valid():
            post = form.save(commit=False)
            post.ip = request.META['REMOTE_ADDR']
            post.save()
            return redirect('home')
    else:
        form = PostForm()
    context = {
        'form' : form
    }
    return render(request, 'core/post_form.html', context)

 

유효성을 통과한 값, cleand_data

form에는 이미 request 로 부터 넘어온 값이 유효성 검사를 마치고 저장이 되어있습니다. 따라서 form을 만들었다면 request.POST 로 기존의 값에 접근하기 보다는 cleand_data 를 통해서 접근하시는게더 좋습니다.

if request.method == "POST":
        form = PostForm(request.POST, request.FILES)
        if form.is_valid():
            title = form.cleand_data['title'] # (O)
         		title = request.POST['title'] # (X)
            return redirect('home')