Got a perfect _server side_ solution for accepting forms with nested
formsets, automatically generated from a reusable app foreign key relations to their section model. Georgeous! However, the client side javascript is, even after a complete refactoring, still an utter mess. After the template generates the HTML tree it's a chore to use javascript to show, hide and duplicate nodes based on user interaction. Maybe I should look into client-side templating?main
53
cms/forms.py
|
@ -47,6 +47,9 @@ class SectionForm(forms.ModelForm):
|
|||
self.fields['type'].choices = self._meta.model.TYPES
|
||||
self.fields['type'].initial = self._meta.model.TYPES[0][0]
|
||||
|
||||
self.fields['type'].widget.attrs['class'] = 'type'
|
||||
self.fields['type'].widget.attrs['data-form'] = self.prefix
|
||||
|
||||
def delete(self):
|
||||
instance = super().save()
|
||||
instance.delete()
|
||||
|
@ -83,18 +86,13 @@ class SectionForm(forms.ModelForm):
|
|||
|
||||
#SectionFormSet = inlineformset_factory(Page, Section, form=SectionForm, extra=1)
|
||||
|
||||
def get_view(section):
|
||||
if section:
|
||||
return section.__class__.view_class()
|
||||
|
||||
class BaseSectionFormSet(forms.BaseInlineFormSet):
|
||||
'''Potentially nested formset based on
|
||||
https://www.yergler.net/2013/09/03/nested-formsets-redux/
|
||||
'''If a swappable Section model defines one-to-many fields, (i.e. has
|
||||
foreign keys pointing to it) formsets will be generated for the
|
||||
related models and stored in the form.formsets array.
|
||||
|
||||
If a Section subclass provides a 'formset_class' attribute, the
|
||||
section form generated for the edit page will be given a 'formset'
|
||||
attribute. This way, sections can customize their edit form to
|
||||
request additional information.
|
||||
Based on this logic for nested formsets:
|
||||
https://www.yergler.net/2013/09/03/nested-formsets-redux/
|
||||
|
||||
Typical usecases could be:
|
||||
- an images section that displays multiple images
|
||||
|
@ -106,22 +104,23 @@ class BaseSectionFormSet(forms.BaseInlineFormSet):
|
|||
def add_fields(self, form, index):
|
||||
super().add_fields(form, index)
|
||||
section = form.instance
|
||||
view = get_view(section)
|
||||
if hasattr(view, 'formset_class'):
|
||||
form.formset = view.formset_class(
|
||||
instance=section,
|
||||
data=form.data if self.is_bound else None,
|
||||
files=form.files if self.is_bound else None,
|
||||
prefix=f'{form.prefix}-{view.formset_class.get_default_prefix()}')
|
||||
#raise ValueError(form.formset)
|
||||
|
||||
form.formsets = []
|
||||
for field in section._meta.get_fields():
|
||||
if field.one_to_many:
|
||||
formset = forms.inlineformset_factory(Section, field.related_model, fields='__all__', extra=1)(
|
||||
instance=section,
|
||||
data=form.data if self.is_bound else None,
|
||||
files=form.files if self.is_bound else None,
|
||||
prefix=f'{form.prefix}-{field.name}')
|
||||
formset.name = field.name
|
||||
form.formsets.append(formset)
|
||||
|
||||
def is_valid(self):
|
||||
result = super().is_valid()
|
||||
if self.is_bound:
|
||||
for form in self.forms:
|
||||
if hasattr(form, 'formset'):
|
||||
result = result and form.formset.is_valid()
|
||||
for formset in form.formsets:
|
||||
result = result and formset.is_valid()
|
||||
return result
|
||||
|
||||
def save(self, commit=True):
|
||||
|
@ -131,6 +130,12 @@ class BaseSectionFormSet(forms.BaseInlineFormSet):
|
|||
form.formset.save(commit=commit)
|
||||
return result
|
||||
|
||||
def get_form_kwargs(self, index):
|
||||
kwargs = super().get_form_kwargs(index)
|
||||
kwargs.update({'label_suffix': ''})
|
||||
return kwargs
|
||||
|
||||
|
||||
SectionFormSet = forms.inlineformset_factory(
|
||||
parent_model = Page,
|
||||
model = Section,
|
||||
|
@ -138,3 +143,9 @@ SectionFormSet = forms.inlineformset_factory(
|
|||
formset = BaseSectionFormSet,
|
||||
extra=1,
|
||||
)
|
||||
|
||||
# class ImagesForm(forms.ModelForm):
|
||||
# class Meta:
|
||||
# model = SectionImage
|
||||
# exclude = ['section']
|
||||
# ImagesFormSet = forms.inlineformset_factory(Section, SectionImage, form=ImagesForm, extra=3)
|
||||
|
|
Po Szerokość: | Wysokość: | Rozmiar: 183 KiB |
Po Szerokość: | Wysokość: | Rozmiar: 85 KiB |
Przed Szerokość: | Wysokość: | Rozmiar: 216 KiB Po Szerokość: | Wysokość: | Rozmiar: 263 KiB |
|
@ -1,75 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
version="1"
|
||||
viewBox="0 0 48 48"
|
||||
enable-background="new 0 0 48 48"
|
||||
id="svg6"
|
||||
sodipodi:docname="edit.svg"
|
||||
inkscape:version="0.92.4 (5da689c313, 2019-01-14)">
|
||||
<metadata
|
||||
id="metadata12">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<defs
|
||||
id="defs10" />
|
||||
<sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1075"
|
||||
id="namedview8"
|
||||
showgrid="true"
|
||||
inkscape:zoom="13.906433"
|
||||
inkscape:cx="58.141489"
|
||||
inkscape:cy="18.442256"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="0"
|
||||
inkscape:current-layer="svg6">
|
||||
<inkscape:grid
|
||||
type="xygrid"
|
||||
id="grid887" />
|
||||
</sodipodi:namedview>
|
||||
<circle
|
||||
fill="#4CAF50"
|
||||
cx="24"
|
||||
cy="24"
|
||||
r="21"
|
||||
id="circle2" />
|
||||
<g
|
||||
id="g895"
|
||||
transform="matrix(0.49467333,-0.05078122,-0.05078122,0.49467333,-9.7836852,14.587579)"
|
||||
style="fill:#ffffff;fill-opacity:1">
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path889"
|
||||
d="M 50,48 H 60 L 85,23 75,13 50,38 Z"
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path891"
|
||||
d="M 78,10 88,20 94,14 84,4 Z"
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
||||
</g>
|
||||
</svg>
|
Przed Szerokość: | Wysokość: | Rozmiar: 2.3 KiB |
|
@ -1,60 +0,0 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
version="1"
|
||||
viewBox="0 0 48 48"
|
||||
enable-background="new 0 0 48 48"
|
||||
id="svg828"
|
||||
sodipodi:docname="ok.svg"
|
||||
inkscape:version="0.92.4 (5da689c313, 2019-01-14)">
|
||||
<metadata
|
||||
id="metadata834">
|
||||
<rdf:RDF>
|
||||
<cc:Work
|
||||
rdf:about="">
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
<defs
|
||||
id="defs832" />
|
||||
<sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1075"
|
||||
id="namedview830"
|
||||
showgrid="false"
|
||||
inkscape:zoom="4.9166667"
|
||||
inkscape:cx="-11.389831"
|
||||
inkscape:cy="24"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="0"
|
||||
inkscape:current-layer="svg828" />
|
||||
<circle
|
||||
fill="#4CAF50"
|
||||
cx="24"
|
||||
cy="24"
|
||||
r="21"
|
||||
id="circle824" />
|
||||
<polygon
|
||||
fill="#CCFF90"
|
||||
points="34.6,14.6 21,28.2 15.4,22.6 12.6,25.4 21,33.8 37.4,17.4"
|
||||
id="polygon826"
|
||||
style="fill:#ffffff;fill-opacity:1" />
|
||||
</svg>
|
Przed Szerokość: | Wysokość: | Rozmiar: 1.7 KiB |
Po Szerokość: | Wysokość: | Rozmiar: 310 KiB |
|
@ -4,131 +4,104 @@
|
|||
{% block title %}{% trans 'Edit' %} {{form.instance}}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<form method="POST" enctype="multipart/form-data" class="cms">
|
||||
{% csrf_token %}
|
||||
{{form.media}}
|
||||
<div class="wrapper">
|
||||
|
||||
{% if form %}
|
||||
<section>
|
||||
<div class="wrapper">
|
||||
{% if form.non_field_errors %}
|
||||
<div class="global_error">
|
||||
{{form.non_field_errors}}
|
||||
</div>
|
||||
{% elif form.errors or formset.errors %}
|
||||
<div class="global_error">
|
||||
{% trans 'Please correct the error(s) below and save again' %}
|
||||
</div>
|
||||
{% endif %}
|
||||
{% for field in form %}
|
||||
{% if field.field.widget.input_type == 'checkbox' %}
|
||||
{% include 'cms/formfield_checkbox.html' with field=field %}
|
||||
{% else %}
|
||||
{% include 'cms/formfield.html' with field=field %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
<form method="POST" enctype="multipart/form-data" class="cms">
|
||||
{% csrf_token %}
|
||||
{{form.media}}
|
||||
|
||||
{% if form.errors or formset.errors %}
|
||||
<div class="global_error">
|
||||
{% trans 'Please correct the error(s) below and save again' %}
|
||||
</div>
|
||||
</section>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
{% for field in form %}
|
||||
{% include 'cms/formfield.html' with field=field %}
|
||||
{% endfor %}
|
||||
|
||||
{% if formset %}
|
||||
<div id="formset">
|
||||
<div id="{{formset.prefix}}">
|
||||
{{formset.management_form}}
|
||||
{% for form in formset %}
|
||||
<div class="formset_form">
|
||||
<div class="formset_form" id="{{form.prefix}}" {% if forloop.last %}style="display:none"{% endif %}>
|
||||
{{form.media}}
|
||||
{% for field in form.hidden_fields %}
|
||||
{{field}}
|
||||
{% endfor %}
|
||||
<section>
|
||||
<div class="wrapper">
|
||||
<div class="subform">
|
||||
{% for field in form.visible_fields %}
|
||||
{% if field.name == 'DELETE' and not form.instance.pk %}
|
||||
|
||||
{% elif field.field.widget.input_type == 'checkbox' %}
|
||||
{% include 'cms/formfield_checkbox.html' with field=field counter=forloop.parentloop.counter0 %}
|
||||
{% else %}
|
||||
{% include 'cms/formfield.html' with field=field counter=forloop.parentloop.counter0 %}
|
||||
{% endif %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
{{form.formset.management_form}}
|
||||
{% for form in form.formset %}
|
||||
<div class="anothersubform" style="padding: 10px; border:2px dotted black;margin-bottom: 10px">
|
||||
{% for field in form.visible_fields %}
|
||||
<div class="field {{field.name}}">
|
||||
{% include 'cms/formfield.html' with field=field %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
|
||||
{% for formset in form.formsets %}
|
||||
<div class="field {{formset.name}}" id="{{formset.prefix}}">
|
||||
{{formset.management_form}}
|
||||
{% for form in formset %}
|
||||
<div class="subform">
|
||||
{{form.media}}
|
||||
{% for field in form.hidden_fields %}
|
||||
{{field}}
|
||||
{% endfor %}
|
||||
{% for field in form.visible_fields %}
|
||||
{% if field.name == 'DELETE' and not form.instance.pk %}
|
||||
|
||||
{% elif field.field.widget.input_type == 'checkbox' %}
|
||||
{% include 'cms/formfield_checkbox.html' with field=field counter=forloop.parentloop.counter0 %}
|
||||
{% else %}
|
||||
{% include 'cms/formfield.html' with field=field counter=forloop.parentloop.counter0 %}
|
||||
{% endif %}
|
||||
{% include 'cms/formfield.html' with field=field %}
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
<img onclick="addForm(this, '{{formset.prefix}}')" src="{% static 'cms/add_small.png' %}" width="50">
|
||||
</div>
|
||||
</section>
|
||||
{% endfor %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
<img onclick="addForm(this, '{{formset.prefix}}')" src="{% static 'cms/add.png' %}" width="75" style="display:block;clear:both">
|
||||
</div>
|
||||
<style>
|
||||
div.formset_form:last-child {
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
<section>
|
||||
<div class="wrapper">
|
||||
<a class="button" href="#" onclick="addForm(); return false">+</a>
|
||||
</div>
|
||||
</section>
|
||||
{% endif %}
|
||||
|
||||
<div class="edit page">
|
||||
<button><img src="{% static 'cms/ok.svg' %}"></button>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
<div class="edit page">
|
||||
<button><img src="{% static 'cms/save.png' %}"></button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block extrabody %}
|
||||
<script type="text/javascript" src="/static/admin/js/urlify.js"></script>
|
||||
<script>
|
||||
NodeList.prototype.last = function(){
|
||||
NodeList.prototype.last = function() {
|
||||
return this[this.length - 1];
|
||||
};
|
||||
|
||||
var formset = document.getElementById('formset');
|
||||
var counter = formset.firstElementChild;
|
||||
var re = /(.+)-(\d)-(.+)/;
|
||||
|
||||
function updateIndex(el) {
|
||||
matches = el.name.match(re);
|
||||
let re = /^(.+)-(\d)-(.+)$/;
|
||||
let matches = el.name.match(re);
|
||||
if (matches) {
|
||||
prefix = matches[1];
|
||||
suffix = matches[3];
|
||||
index = parseInt(matches[2]) + 1;
|
||||
let prefix = matches[1];
|
||||
let suffix = matches[3];
|
||||
let index = parseInt(matches[2]) + 1;
|
||||
el.name = `${prefix}-${index}-${suffix}`;
|
||||
}
|
||||
}
|
||||
|
||||
function addForm() {
|
||||
final_form = document.querySelectorAll('div.formset_form').last();
|
||||
extra_form = final_form.cloneNode(true);
|
||||
inputs = extra_form.querySelectorAll("input, select, textarea");
|
||||
function addForm(node, parent_id) {
|
||||
let base = node.previousElementSibling;
|
||||
let parent = document.getElementById(parent_id);
|
||||
let counter = parent.firstElementChild;
|
||||
let extra_form = base.cloneNode(true);
|
||||
let inputs = extra_form.querySelectorAll("input, select, textarea");
|
||||
for (input of inputs) {
|
||||
updateIndex(input);
|
||||
}
|
||||
|
||||
formset.appendChild(extra_form);
|
||||
extra_form.style.display = 'block';
|
||||
node.remove();
|
||||
parent.appendChild(extra_form);
|
||||
parent.appendChild(node);
|
||||
counter.value = parseInt(counter.value) + 1;
|
||||
setEventHandlers();
|
||||
resizeTextareas();
|
||||
}
|
||||
|
||||
function resizeTextareas() {
|
||||
var tx = document.getElementsByTagName('textarea');
|
||||
for (var i = 0; i < tx.length; i++) {
|
||||
let tx = document.getElementsByTagName('textarea');
|
||||
for (let i = 0; i < tx.length; i++) {
|
||||
tx[i].setAttribute('style', 'height:0;overflow-y:hidden;');
|
||||
tx[i].style.height = (tx[i].scrollHeight) + 'px';
|
||||
tx[i].addEventListener('input', function() {
|
||||
|
@ -138,76 +111,32 @@
|
|||
}
|
||||
}
|
||||
|
||||
document.addEventListener("DOMContentLoaded", function(event) {
|
||||
resizeTextareas();
|
||||
|
||||
// My own implementation of Django's prepopulate.js
|
||||
|
||||
var slugfield = document.getElementById('id_slug');
|
||||
var titlefield = document.getElementById('id_title');
|
||||
if (slugfield && titlefield) {
|
||||
var virgin = slugfield.value === '';
|
||||
if (virgin) {
|
||||
titlefield.addEventListener('input', function(event) {
|
||||
if (virgin) {
|
||||
slugfield.value = URLify(titlefield.value);
|
||||
}
|
||||
});
|
||||
slugfield.addEventListener('input', function(event) {
|
||||
virgin = false;
|
||||
});
|
||||
function showRelevantFields(form, type) {
|
||||
let fields_per_type = {{fields_per_type|safe}};
|
||||
for (let field of form.querySelectorAll('div.field')) {
|
||||
field.style.display = 'none';
|
||||
}
|
||||
for (let name of fields_per_type[type]) {
|
||||
for (let field of form.querySelectorAll('div.field.' + name)) {
|
||||
field.style.display = 'block';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Auto-hide non-relevant fields, based on type field
|
||||
|
||||
{% if fields_per_type %}
|
||||
var typefield = document.getElementById('id_type');
|
||||
fields_per_type = {{fields_per_type|safe}};
|
||||
|
||||
if (typefield) {
|
||||
function show_relevant_fields(type) {
|
||||
for (el of document.querySelectorAll('div.formfield')) {
|
||||
el.style.display = 'none';
|
||||
}
|
||||
|
||||
for (field of fields_per_type[type]) {
|
||||
el = document.getElementById(field);
|
||||
el.style.display = 'block';
|
||||
}
|
||||
}
|
||||
|
||||
typefield.addEventListener('input', function(event) {
|
||||
show_relevant_fields(typefield.value.toLowerCase());
|
||||
function setEventHandlers() {
|
||||
for (let typefield of document.querySelectorAll('select.type')) {
|
||||
let form = document.getElementById(typefield.dataset.form);
|
||||
let type = typefield.value.toLowerCase();
|
||||
showRelevantFields(form, type);
|
||||
typefield.addEventListener('input', function() {
|
||||
type = typefield.value.toLowerCase();
|
||||
showRelevantFields(form, type);
|
||||
resizeTextareas();
|
||||
});
|
||||
show_relevant_fields(typefield.value.toLowerCase());
|
||||
}
|
||||
}
|
||||
|
||||
subforms = document.querySelectorAll('div.subform');
|
||||
for (let i = 0; i < subforms.length; ++i) {
|
||||
let typefield = document.getElementById('id_sections-' + i + '-type');
|
||||
|
||||
function show_relevant_fields(type) {
|
||||
for (field of subforms[i].querySelectorAll('div.formfield')) {
|
||||
field.style.display = 'none';
|
||||
}
|
||||
|
||||
delete_checkbox = document.getElementById('DELETE' + i);
|
||||
if (delete_checkbox) {
|
||||
delete_checkbox.style.display = 'block';
|
||||
}
|
||||
for (field of fields_per_type[type]) {
|
||||
el = document.getElementById(field + i);
|
||||
el.style.display = 'block';
|
||||
}
|
||||
}
|
||||
|
||||
typefield.addEventListener('input', function(event) {
|
||||
(show_relevant_fields(typefield.value.toLowerCase()));
|
||||
});
|
||||
show_relevant_fields(typefield.value.toLowerCase());
|
||||
}
|
||||
{% endif %}
|
||||
});
|
||||
setEventHandlers();
|
||||
resizeTextareas();
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
|
|
@ -1,14 +1,26 @@
|
|||
<div class="formfield{% if field.errors %} error{% endif %}{% if field.field.required %} required{% endif %} {{field.name}}" id="{{field.name}}{{counter}}">
|
||||
<div class="errors">
|
||||
{{field.errors}}
|
||||
</div>
|
||||
<div class="label">
|
||||
{{field.label_tag}}
|
||||
</div>
|
||||
<div class="input">
|
||||
{{field}}
|
||||
</div>
|
||||
<div class="helptext">
|
||||
{% if field.name == 'DELETE' and not form.instance.pk %}
|
||||
|
||||
{% else %}
|
||||
<div class="formfield{% if field.errors %} error{% endif %}{% if field.field.required %} required{% endif %} {{field.name}}">
|
||||
<div class="errors">
|
||||
{{field.errors}}
|
||||
</div>
|
||||
|
||||
{% if field.field.widget.input_type == 'checkbox' %}
|
||||
<div class="input">
|
||||
{{field}} {{field.label_tag}}
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="label">
|
||||
{{field.label_tag}}
|
||||
</div>
|
||||
<div class="input">
|
||||
{{field}}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
<div class="helptext">
|
||||
{{field.help_text}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
|
|
@ -1,11 +0,0 @@
|
|||
<div class="formfield{% if field.errors %} error{% endif %}{% if field.field.required %} required{% endif %} {{field.name}}" id="{{field.name}}{{counter}}">
|
||||
<div class="errors">
|
||||
{{field.errors}}
|
||||
</div>
|
||||
<div class="input">
|
||||
{{field}} {{field.label_tag}}
|
||||
</div>
|
||||
<div class="helptext">
|
||||
{{field.help_text}}
|
||||
</div>
|
||||
</div>
|
10
cms/views.py
|
@ -157,8 +157,7 @@ class EditPage(UserPassesTestMixin, edit.ModelFormMixin, base.TemplateResponseMi
|
|||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super().get_context_data(**kwargs)
|
||||
if 'formset' not in context:
|
||||
context['formset'] = SectionFormSet(instance=self.object, form_kwargs={'label_suffix': ''})
|
||||
# context['formset'] = SectionFormSet(instance=self.object, form_kwargs={'label_suffix': ''})
|
||||
fields_per_type = {}
|
||||
for model, _ in Section.TYPES:
|
||||
ctype = ContentType.objects.get(
|
||||
|
@ -180,10 +179,13 @@ class EditPage(UserPassesTestMixin, edit.ModelFormMixin, base.TemplateResponseMi
|
|||
|
||||
def get(self, request, *args, **kwargs):
|
||||
self.object = self.get_object()
|
||||
return self.render_to_response(self.get_context_data())
|
||||
formset = self.get_formset()
|
||||
return self.render_to_response(self.get_context_data(formset=formset))
|
||||
|
||||
def get_formset(self):
|
||||
return SectionFormSet(self.request.POST, self.request.FILES, instance=self.object)
|
||||
if self.request.POST:
|
||||
return SectionFormSet(self.request.POST, self.request.FILES, instance=self.object)
|
||||
return SectionFormSet(instance=self.object)
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
self.object = self.get_object()
|
||||
|
|