Django

Django 실습(5) Form과 ModelForm 사용한 블로그

UserDonghu 2023. 10. 12. 20:33

지난번엔 html파일에서 태그를 하나씩 직접 달아서 post로 보내는 방식으로 게시물을 생성했지만, 이번에는 장고에서 제공하는 Form과 ModelForm을 이용해서 좀 더 간단하고 효율적으로 처리할 수 있다.

 

역할 : Form 클래스의 데이터를 읽어서 HTML 입력폼을 알아서 만들어주고 유효성 검증 및 값 반환이 쉽다.

 

Form과 ModelForm의 차이 : Form은 직접 필드를 정의하고 위젯 설정이 필요하지만, ModelForm은 지정된 Model로 부터 필드 정보를 읽고 자동으로 필드를 설정한다.


실습

지난번에 한 실습에서 파일들을 좀 수정하면서 진행

 

blog / models.py 수정

from django.db import models

class Post(models.Model):
    title = models.CharField(max_length=100)
    contents = models.TextField()
    main_image = models.ImageField(upload_to='blog/%Y/%m/%d/', blank=True, null=True) # mysite/blog/연/월/일 폴더에 저장한다
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    def __str__(self):
        return self.title

 

blog 파일 안에 form.py를 생성

 

Form 클래스를 사용할때는 직접 필드를 생성

from django import forms

class PostForm(forms.Form):
    title = forms.CharField(max_length=20)
    contents = forms.CharField()
    main_image = forms.ImageField(required=False)

 

ModelForm을 사용할때는 내부에 Meta 클래스 사용

from django import forms
from .models import Post

class PostForm(forms.ModelForm): # ModelForm 상속
    
    class Meta:
        model = Post # models.py의 Post클래스를 model로 하겠다.
        fields = ("title", "contents", "main_image") # Form에서 받아올 필드 작성. '__all__'를 사용하면 model의 모든 요소 선택

 

blog / views.py 수정

 

Form을 사용할 때

from django.shortcuts import render, get_object_or_404, redirect # 추가
from .models import Post
from django.db.models import Q # 쿼리 객체 Q import
from .forms import PostForm # forms.py 에서 PostForm클래스 import

def index(request):
    if request.GET.get('q'): # GET요청에서 q가 있으면 : 검색 버튼을 눌렀을때
        q = request.GET.get('q')
        db = Post.objects.filter(Q(title__icontains=q) | Q(contents__icontains=q)).distinct() # Q 객체를 이용해서 &(and)와 |(or) 연산을 할 수 있다. distinct() : 중복 제거
    else:
        db = Post.objects.all()
    context = {
        'db': db,
    }
    return render(request, 'blog/index.html', context)

def post(request, pk):
    db = Post.objects.get(pk=pk)
    context = {
        'db': db,
    }
    return render(request, 'blog/post.html', context)

def create(request): # 게시물 생성
    if request.method == "POST": # request가 POST면 : 게시 버튼을 누르면
        form = PostForm(request.POST, request.FILES) # PostForm에 Post요청값과 파일들을 넣어서 form 생성.
        if form.is_valid(): # form 유효성 검사 : DB와 형식이 일치하면
            post = Post( # Post객체 post 생성
                title = form.cleaned_data['title'], # 아까 만든 form의 cleaned_data를 이용해서 dict형태로 접근
                contents = form.cleaned_data['contents'],
                main_image = form.cleaned_data['main_image'],
            )
            post.save() # DB에 저장
            return redirect('blog:post', pk=post.pk) # 생성 후 게시물의 상세보기로 이동.
    else:
        form = PostForm() # POST 요청이 아니면 PostForm형식에 맞게 form 생성
    return render(request, 'blog/create.html', {'form': form})

def update(request, pk): # 게시물 수정 (업데이트)
    post = get_object_or_404(Post, pk=pk) # get_object_or_404 : Post.obejcts.get(pk=pk)가 있으면 post로 지정, 없으면 404를 반환하는 함수
    if request.method == "POST": # request가 POST면 : 수정하기를 누르면
        form = PostForm(request.POST, request.FILES) # request로 form 생성
        if form.is_valid(): # form 유효성 검사 : DB와 형식이 일치하는지
            post.title = form.cleaned_data['title'] # 요소 하나씩 수정
            post.contents = form.cleaned_data['contents']
            if form.cleaned_data['main_image']: # 이미지가 있으면 업데이트
                post.main_image = form.cleaned_data['main_image']
            post.save() # DB에 저장
        return redirect('blog:post', pk=post.pk) # 수정 후 게시물의 상세보기로 이동
    else:
        initial_data = { # 수정하기 전 게시물 데이터 값 받아오기
        'title': post.title,
        'contents': post.contents,
        'main_image': post.main_image,
        }
        form = PostForm(initial=initial_data) # 수정 하기 전 게시물 데이터를 넣은 PostForm으로 생성
    return render(request, 'blog/create.html', {'form': form})

def delete(request, pk): # 게시물 삭제
    post = get_object_or_404(Post, pk=pk) # get_object_or_404 : Post.obejcts.get(pk=pk)가 있으면 post로 지정, 없으면 404를 반환하는 함수
    if request.method == "POST": # POST요청이면 : 삭제 버튼을 눌렀으면
        post.delete() # 지정한 post를 DB에서 삭제
        return redirect('blog:index') # 삭제한 후 메인으로 이동
    return render(request, 'blog/delete.html', {'post': post}) # post 요청이 아닐때는 delete.html render해주기

 

ModelForm을 사용할 때의 create과 update

def create(request): # 게시물 생성
    if request.method == "POST": # request가 POST면 : 게시 버튼을 누르면
        form = PostForm(request.POST, request.FILES) # PostForm에 Post요청값과 파일들을 넣어서 form 생성.
        if form.is_valid(): # form 유효성 검사 : DB와 형식이 일치하면
            post = form.save() # ModelForm일때는 이렇게 DB에 저장가능
            return redirect('blog:post', pk=post.pk) # 생성 후 게시물의 상세보기로 이동.
    else:
        form = PostForm() # POST 요청이 아니면 PostForm형식에 맞게 form 생성
    return render(request, 'blog/create.html', {'form': form})

def update(request, pk): # 게시물 수정 (업데이트)
    post = get_object_or_404(Post, pk=pk) # get_object_or_404 : Post.obejcts.get(pk=pk)가 있으면 post고 없으면 404를 반환하는 함수
    if request.method == "POST": # request가 POST면 : 수정하기를 누르면
        form = PostForm(request.POST, request.FILES, instance=post) # instance=post : ModelForm은 instance 매개변수를 통해 수정할 모델 객체 지정
        if form.is_valid(): # form 유효성 검사 : DB와 형식이 일치하는지
            form.save() # DB에 저장
            return redirect('blog:post', pk=post.pk) # 수정 후 게시물의 상세보기로 이동
    else:
        form = PostForm(instance=post) # ModelForm은 instance매개변수를 이용해서 form의 값을 미리 받아온다.
    return render(request, 'blog/create.html', {'form': form})

 

templates / blog 안에 html 파일 수정

 

menu.html

<h2><a href="{% url 'blog:index' %}">My Blog</a></h2>
{% block content %}
{% endblock %}

 

index.html

{% extends 'blog/menu.html' %}

{% block content %}
<h1>게시판</h1>
<form action="" method="get">
    <input name="q" type="search">
    <button type="submit">검색</button>
</form>
<ul>
    {% for post_detail in db %}
    <li>
        <a href="{% url 'blog:post' post_detail.id %}">{{ post_detail.title }}</a>
        <p>{{post_detail.contents}}</p>
    </li>
    {% endfor %}
</ul>
<button type="button" onclick="location.href='{% url 'blog:create' %}'">글쓰기</button>
{% endblock %}

 

post.html

{% extends 'blog/menu.html' %}

{% block content %}
<h1>게시판</h1>
<p>{{db.title}}</p>
<p>{{db.contents}}</p>
<p>{{db.updated_at}}</p>
{% if db.main_image %}
<img src="{{ db.main_image.url }}" alt="">
{% endif %}
<br>
<button type="button" onclick="location.href='{% url 'blog:update' db.pk %}'">수정하기</button>
<button type="button" onclick="location.href='{% url 'blog:delete' db.pk %}'">삭제하기</button>
{% endblock %}

 

create.html

{% extends 'blog/menu.html' %}

{% block content %}
<form action="" method="post" enctype="multipart/form-data">
    {% csrf_token %}
    {{ form.as_p }}
    <input type="submit">
</form>
{% endblock %}

 

delete.html

{% extends 'blog/menu.html' %}

{% block content %}
<form method="post">
    {% csrf_token %}
    <p>블로그 글 "{{post.title}}" 을 삭제하시겠습니까?</p>
    <input type="submit" value="네!" class="btn btn-primary">
</form>
{% endblock %}

 

결과

blog 메인
게시물 작성하기
게시물 상세보기
게시물 수정하기
수정하면 바뀐 게시물이 나온다.
게시물 삭제하기
삭제하기 누르면 삭제되고 메인화면 보여줌