Django

Chapter 9-4 Django 포스트 생성, 수정 페이지 만들기

Chansman 2025. 5. 14. 15:15

🧾 Django Formset + jquery.formset 동적 이미지 폼 구현

1️⃣ jquery.formset 소개

jquery.formset은 Django의 폼셋(Formset)을 클라이언트 측에서 동적으로 추가 및 삭제할 수 있도록 도와주는 JavaScript 라이브러리입니다.

  • 🧩 Django의 Formset 기능을 확장하여 사용자 친화적인 UI 제공
  • 📦 HTML 요소를 실시간으로 추가/삭제할 수 있음
  • 📥 CDN 또는 로컬 파일로 불러와 사용 가능

🔗 jquery.formset.js CDN 링크


2️⃣ inlineformset_factory 활용

🧩 개념 정리

Django의 inlineformset_factory는 부모-자식 모델 간 관계를 다룰 수 있는 폼셋을 자동으로 생성해줍니다.

  • 부모: Post
  • 자식: PostImage
  • 관계: PostImage.post → ForeignKey(Post)

post.views.py

class PostCreateView(LoginRequiredMixin, CreateView):
    model = Post
    template_name = 'post/form.html'
    form_class = PostForm

    def get_context_data(self, **kwargs):
        data = super().get_context_data(**kwargs)
        data['formset'] = PostImageFormSet()
        return data

    def form_valid(self, form):
        self.object = form.save(commit=False)
        self.object.user = self.request.user
        self.object.save()

        image_formset = PostImageFormSet(self.request.POST, self.request.FILES, instance=self.object)
        if image_formset.is_valid():
            image_formset.save()

        return HttpResponseRedirect(reverse('main'))

 

urls.py

    path('create/', post_views.PostCreateView.as_view(), name='create'),
    path('<int:pk>/update/', post_views.PostUpdateView.as_view(), name='update'),

✔️ 코드 예시

# post/forms.py

from django.forms import inlineformset_factory
from post.models import Post, PostImage
from utils.forms import BootstrapModelForm

class PostForm(BootstrapModelForm):
    class Meta:
        model = Post
        fields = ('content', )

class PostImageForm(BootstrapModelForm):
    class Meta:
        model = PostImage
        fields = ('image', )

PostImageFormSet = inlineformset_factory(
    Post, PostImage, form=PostImageForm,
    extra=1, can_delete=True, min_num=1, max_num=5
)

🧠 extra, can_delete, min_num, max_num 등의 옵션으로 폼 수 제한 가능

 

utils/forms.py

from django import forms


class BootstrapModelForm(forms.ModelForm):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        for name, field in self.fields.items():
            if 'class' in field.widget.attrs:
                field.widget.attrs['class'] += ' form-control'
            else:
                field.widget.attrs.update({'class': 'form-control'})

            if isinstance(field.widget, forms.Textarea):
                field.widget.attrs.update({'rows': 4})

 

static/style.css 생성 ( 입력폼 변경안되게 만들기 )

textarea.form-control {
    resize: none;
}

 

base.html에 style적용

<link href="{% static 'css/style.css' %}" rel="stylesheet" />

 

statice/js/jquery.formset.js  만들어서 복사해서 붙여넣기 https://cdnjs.com/libraries/jquery.formset

 

post/list.html에 생성버튼 추가하기

{% block content %}
<div class="row">
  <div class="text-end col-10 offset-1 col-lg-6 offset-lg-3">
      <a class="btn btn-sm btn-info" href="{% url 'create' %}">생성</a>
  </div>
  <div class="col-10 offset-1 col-lg-6 offset-lg-3 infinite-container">
    {% include 'post/post_list_content.html' %}
  </div>
</div>
{% endblock %}

 

post_list_content.htm에 수정버튼 추가하기

<div class="mb-2">
      <span class="p-2 border rounded-circle me-2">
        <i class="fa-solid fa-user" style="width: 16px; padding-left: 1px;"></i>
      </span>
      {{ post.user.nickname }} {{ post.id }}
      {% if post.user == request.user %}
          <a href="{% url 'update' post.pk %}" class="btn btn-warning btn-sm float-end">수정</a>
      {% endif %}
    </div>

 


3️⃣ formset.management_form의 역할

폼셋 렌더링 시 반드시 포함해야 하는 관리 폼입니다.

기능 설명

총 폼 수 관리 현재 몇 개의 폼이 존재하는지 서버에 전달
삭제 처리 삭제된 항목을 구분하여 전달함
순서 유지 사용자 입력 순서를 서버가 정확히 인식할 수 있도록 도와줌

이 폼이 없으면 Django는 어느 폼이 유효한지, 삭제되었는지 판단할 수 없습니다.


4️⃣ 템플릿 구현 코드

{# post/form.html #}
{% extends 'base.html' %}
{% load static %}

{% block content %}
  <div class="row">
    <div class="col-10 offset-1 col-lg-6 offset-lg-3">
      <form action="" method="post" enctype="multipart/form-data">
        {% csrf_token %}
        {{ form.as_p }}

        <table class="table">
          {{ formset.management_form }}
          <thead>
            <tr>
              {% for field in formset.forms.0.visible_fields %}
                <th>{{ field.label }}</th>
              {% endfor %}
            </tr>
          </thead>
          <tbody>
            {% for form in formset.forms %}
              <tr class="formset_row">
                {% for field in form.visible_fields %}
                  <td>
                    {% if forloop.first %}
                      {% for hidden in form.hidden_fields %}
                        {{ hidden }}
                      {% endfor %}
                    {% endif %}
                    {{ field.errors.as_ul }}
                    {{ field }}
                    {% if field.errors %}
                      {% for error in field.errors %}
                        <span class="text-danger">{{ error }}</span>
                      {% endfor %}
                    {% endif %}
                  </td>
                {% endfor %}
              </tr>
              {% if form.non_field_errors %}
                {% for error in form.non_field_errors %}
                  <p class="text-danger">{{ error }}</p>
                {% endfor %}
              {% endif %}
            {% endfor %}
          </tbody>
        </table>
        <button class="btn btn-primary">저장</button>
      </form>
    </div>
  </div>
{% endblock %}

{% block js %}
  <script src="{% static 'js/jquery.min.js' %}"></script>
  <script src="{% static 'js/jquery.formset.js' %}"></script>

  <script>
    $('.formset_row').formset({
      addText: '추가하기',
      deleteText: '삭제',
      prefix: 'images'  // formset의 prefix와 일치해야 함
    });
  </script>
{% endblock %}

5️⃣ 주요 포인트 정리 💡

항목 설명

inlineformset_factory Post-PostImage 간의 관계를 폼셋으로 자동 생성
formset.management_form 폼셋 상태 관리 메타 정보 포함 (반드시 포함)
jquery.formset.js JS에서 폼을 동적으로 추가/삭제 가능하게 함
prefix 옵션 서버와 클라이언트 모두 동일한 prefix를 사용해야 데이터가 일치함

✅ 요약

  • Django에서 이미지 여러 장 업로드를 처리할 때, Post와 PostImage 관계를 inlineformset_factory로 폼셋 구성
  • jquery.formset.js로 클라이언트에서 동적으로 폼 추가/삭제
  • 관리 폼 필수 포함 + 폼 오류 처리 및 prefix 맞춤 필요

다음에는 form_valid에서 이미지 저장 로직 처리나, 미리보기 기능 등을 확장해볼 수 있습니다.