클래스 기반 보기 및 바삭한 양식이 포함된 Django 인라인 양식
우선, 인라인 폼셋을 사용하는 이유:
사용자가 한 페이지에서 참조된 객체의 생성/업데이트 보기에서 외래 키 객체를 통해 관련 항목을 생성하고 업데이트할 수 있습니다.
여러 언어로 된 제목을 가질 수 있는 컬렉션이 있지만 사용자가 제공할 제목 번역이 정확히 몇 개인지 모른다고 가정합니다. 컬렉션 생성 양식에 새 행을 추가하는 '추가' 버튼을 클릭하기만 하면 사용자가 필요한 만큼 많은 제목을 추가할 수 있도록 하고 싶습니다.
이것이 우리 모델의 모습입니다.
models.py:
from django.db import models
from django.contrib.auth.models import User
class Collection(models.Model):
subject = models.CharField(max_length=300, blank=True)
owner = models.CharField(max_length=300, blank=True)
note = models.TextField(blank=True)
created_by = models.ForeignKey(User,
related_name="collections", blank=True, null=True,
on_delete=models.SET_NULL)
def __str__(self):
return str(self.id)
class CollectionTitle(models.Model):
"""
A Class for Collection titles.
"""
collection = models.ForeignKey(Collection,
related_name="has_titles", on_delete=models.CASCADE)
name = models.CharField(max_length=500, verbose_name="Title")
language = models.CharField(max_length=3)
이제 CollectionTitle에 대한 양식과 부모 모델 Collection 및 FK 관련 모델 CollectionTitle을 포함하는 formset(inlineformset_factory 사용)를 만들어 보겠습니다.
form.py
from django import forms
from .models import *
from django.forms.models import inlineformset_factory
class CollectionTitleForm(forms.ModelForm):
class Meta:
model = CollectionTitle
exclude = ()
CollectionTitleFormSet = inlineformset_factory(
Collection, CollectionTitle, form=CollectionTitleForm,
fields=['name', 'language'], extra=1, can_delete=True
)
다음으로 이 폼셋을 CollectionCreate 보기에 추가합니다.
views.py:
from .models import *
from .forms import *
from django.views.generic.edit import CreateView, UpdateView
from django.urls import reverse_lazy
from django.db import transaction
class CollectionCreate(CreateView):
model = Collection
template_name = 'mycollections/collection_create.html'
form_class = CollectionForm
success_url = None
def get_context_data(self, **kwargs):
data = super(CollectionCreate, self).get_context_data(**kwargs)
if self.request.POST:
data['titles'] = CollectionTitleFormSet(self.request.POST)
else:
data['titles'] = CollectionTitleFormSet()
return data
def form_valid(self, form):
context = self.get_context_data()
titles = context['titles']
with transaction.atomic():
form.instance.created_by = self.request.user
self.object = form.save()
if titles.is_valid():
titles.instance = self.object
titles.save()
return super(CollectionCreate, self).form_valid(form)
def get_success_url(self):
return reverse_lazy('mycollections:collection_detail', kwargs={'pk': self.object.pk})
CollectionUpdate 보기는 get_context_data()에서 인스턴스 개체를 전달해야 한다는 점을 제외하면 비슷해 보입니다.
views.py:
def get_context_data(self, **kwargs):
data = super(CollectionUpdate, self).get_context_data(**kwargs)
if self.request.POST:
data['titles'] = CollectionTitleFormSet(self.request.POST, instance=self.object)
else:
data['titles'] = CollectionTitleFormSet(instance=self.object)
return data
다음으로 폼셋 내부에 필드로 렌더링될 CollectionForm을 생성해야 합니다. 바삭한 양식에는 Div 또는 HTML에 대해 있는 것과 같은 양식 집합에 대한 레이아웃 개체가 없기 때문에 이것은 간단하지 않습니다.
best solution(대단히 감사합니다!)는 사용자 정의 크리스피 레이아웃 개체를 만드는 것입니다.
custom_layout_object.py:
from crispy_forms.layout import LayoutObject, TEMPLATE_PACK
from django.shortcuts import render
from django.template.loader import render_to_string
class Formset(LayoutObject):
template = "mycollections/formset.html"
def __init__(self, formset_name_in_context, template=None):
self.formset_name_in_context = formset_name_in_context
self.fields = []
if template:
self.template = template
def render(self, form, form_style, context, template_pack=TEMPLATE_PACK):
formset = context[self.formset_name_in_context]
return render_to_string(self.template, {'formset': formset})
다음 단계는 템플릿을 추가하여 formset을 렌더링하는 것입니다.
정확히 내가 렌더링하고 싶은 것은: 각각의 새 제목에 대해 - '이름' 및 '언어' 필드가 있는 행과 행을 제거하기 위한 '제거' 버튼(컬렉션을 업데이트할 때 데이터베이스의 데이터 삭제) 및 하나 행 아래에 있는 추가 버튼 - 새 제목에 대해 다른 행을 추가합니다.
더 많은 행을 동적으로 추가하기 위해 django-dynamic-formset jQuery 플러그인을 사용하고 있습니다.
모든 인라인 formset 사례(예: 하나의 Form에 여러 인라인 formset를 추가하는 경우)에 대해 하나의 formset 템플릿을 갖기 위해 접두사( docs )를 사용하는 것이 좋습니다. Formset 접두사는 참조된 클래스의 related_name입니다. 제 경우에는 'has_titles'입니다.
formset.html:
{% load crispy_forms_tags %}
<table>
{{ formset.management_form|crispy }}
{% for form in formset.forms %}
<tr class="{% cycle 'row1' 'row2' %} formset_row-{{ formset.prefix }}">
{% for field in form.visible_fields %}
<td>
{# Include the hidden fields in the form #}
{% if forloop.first %}
{% for hidden in form.hidden_fields %}
{{ hidden }}
{% endfor %}
{% endif %}
{{ field.errors.as_ul }}
{{ field|as_crispy_field }}
</td>
{% endfor %}
</tr>
{% endfor %}
</table>
<br>
<script src="//ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js">
</script>
<script src="{% static 'mycollections/libraries/django-dynamic-formset/jquery.formset.js' %}">
</script>
<script type="text/javascript">
$('.formset_row-{{ formset.prefix }}').formset({
addText: 'add another',
deleteText: 'remove',
prefix: '{{ formset.prefix }}',
});
</script>
마지막으로 기존 레이아웃 개체와 사용자 정의 Formset 개체를 함께 사용하여 CollectionForm에 대한 자체 양식 레이아웃을 구성할 수 있습니다.
form.py:
from crispy_forms.helper import FormHelper
from crispy_forms.layout import Layout, Field, Fieldset, Div, HTML, ButtonHolder, Submit
from .custom_layout_object import *
class CollectionForm(forms.ModelForm):
class Meta:
model = Collection
exclude = ['created_by', ]
def __init__(self, *args, **kwargs):
super(CollectionForm, self).__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.form_tag = True
self.helper.form_class = 'form-horizontal'
self.helper.label_class = 'col-md-3 create-label'
self.helper.field_class = 'col-md-9'
self.helper.layout = Layout(
Div(
Field('subject'),
Field('owner'),
Fieldset('Add titles',
Formset('titles')),
Field('note'),
HTML("<br>"),
ButtonHolder(Submit('submit', 'save')),
)
)
collection_create.html:
{% extends "mycollections/base.html" %}
{% load crispy_forms_tags %}
{% block content %}
<div class="container">
<div class="card">
<div class="card-header">
Create collection
</div>
<div class="card-body">
{% crispy form %}
</div>
</div>
</div>
{% endblock content %}
이제 모든 것이 제자리에 있으며 저장 버튼을 한 번만 누르면 한 페이지에서 한 형식으로 컬렉션과 제목을 만들 수 있습니다.
이 게시물의 소스 코드는 here 입니다.
인라인 양식 세트 솔루션을 분류하는 데 도움이 된 this awesome blog post에 대한 크레딧입니다.
Reference
이 문제에 관하여(클래스 기반 보기 및 바삭한 양식이 포함된 Django 인라인 양식), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/zxenia/django-inline-formsets-with-class-based-views-and-crispy-forms-14o6텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)