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 또는 로컬 파일로 불러와 사용 가능
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에서 이미지 저장 로직 처리나, 미리보기 기능 등을 확장해볼 수 있습니다.