Simplify CRUD logic

Whe needs CRUD? If a page exists, you edit it. If it doesn't, the same form
creates it. No more sections on a page? It gets automatically deleted. The
only thing the user has to remember is: nothing. Brilliant, right?
main
Jaap Joris Vens 2020-02-19 16:35:23 +01:00
rodzic b783424ff5
commit c0464a5ab6
14 zmienionych plików z 366 dodań i 250 usunięć

Wyświetl plik

@ -60,9 +60,9 @@ class Numbered:
class BasePage(Numbered, models.Model): class BasePage(Numbered, models.Model):
'''Abstract base model for pages''' '''Abstract base model for pages'''
number = models.PositiveIntegerField(_('number'), blank=True) title = VarCharField(_('page'))
title = VarCharField(_('title'))
slug = models.SlugField(_('slug'), blank=True, unique=True) slug = models.SlugField(_('slug'), blank=True, unique=True)
number = models.PositiveIntegerField(_('number'), blank=True)
menu = models.BooleanField(_('visible in menu'), default=True) menu = models.BooleanField(_('visible in menu'), default=True)
def __str__(self): def __str__(self):
@ -85,10 +85,10 @@ class BaseSection(Numbered, PolymorphicModel):
'''Abstract base model for sections''' '''Abstract base model for sections'''
TYPES = [] # Will be populated by @register_model() TYPES = [] # Will be populated by @register_model()
page = models.ForeignKey(swapper.get_model_name('cms', 'Page'), verbose_name=_('page'), related_name='sections', on_delete=models.PROTECT) page = models.ForeignKey(swapper.get_model_name('cms', 'Page'), verbose_name=_('page'), related_name='sections', on_delete=models.PROTECT)
type = VarCharField(_('type'), blank=True) title = VarCharField(_('section'))
type = VarCharField(_('type'))
number = models.PositiveIntegerField(_('number'), blank=True) number = models.PositiveIntegerField(_('number'), blank=True)
title = VarCharField(_('title'), blank=True)
content = models.TextField(_('content'), blank=True) content = models.TextField(_('content'), blank=True)
image = models.ImageField(_('image'), blank=True) image = models.ImageField(_('image'), blank=True)
video = EmbedVideoField(_('video'), blank=True, help_text=_('Paste a YouTube, Vimeo, or SoundCloud link')) video = EmbedVideoField(_('video'), blank=True, help_text=_('Paste a YouTube, Vimeo, or SoundCloud link'))

Wyświetl plik

@ -66,10 +66,6 @@ div.spacer {
clear: both; clear: both;
} }
header, nav, section, footer {
padding: 1rem;
}
nav { nav {
padding: 0; padding: 0;
@ -181,9 +177,16 @@ div.edit {
text-align: center; text-align: center;
&.page { &.page {
position: fixed; position: fixed;
left: 1em; right: 1em;
top: 1em; bottom: 1em;
z-index: 1000; z-index: 1000;
img {
width: 75px;
height: auto;
}
a:before, button:before, a:after, button:after {
display: none;
}
} }
} }
@ -210,7 +213,6 @@ div.edit a, div.edit button, a.edit{
section { section {
clear: both; clear: both;
border-bottom: 2px solid $blue;
div.image { div.image {
img { img {
@ -259,8 +261,8 @@ section.contactsection {
/* Form elements */ /* Form elements */
form.cms { form.cms {
div.wrapper { section {
overflow: hidden; margin-top: 3em;
} }
div.global_error { div.global_error {
@ -288,8 +290,8 @@ form.cms {
clear: both; clear: both;
box-sizing: border-box; box-sizing: border-box;
&.type, &.number { &.type, &.number, &.slug {
width: 75%; width: 77%;
clear: none; clear: none;
float: left; float: left;
} }
@ -308,15 +310,16 @@ form.cms {
div.label { div.label {
font-weight: 700; font-weight: 700;
} }
input, select, .textarea, div.textarea { input {
border: 1px solid black; //font-weight: bold;
} }
} }
div.label, label { div.label, label {
font-size: 0.8rem; font-size: 0.7rem;
font-weight: 400; font-weight: 400;
text-align: left; text-align: left;
margin-bottom: 2px;
} }
div.input { div.input {
@ -336,39 +339,32 @@ form.cms {
input, select, textarea { input, select, textarea {
background: white; background: white;
color: black; color: black;
border: 1px solid #aaa; border: 0.5px solid #ccc;
border-radius: 3px;
font-size: 1rem; font-size: 1rem;
display: block; display: block;
width: 100%; width: 100%;
box-sizing: border-box; box-sizing: border-box;
margin: 0; margin: 0;
padding: 5px; padding: 5px 8px;
font-family: inherit; font-family: inherit;
} }
input[type=checkbox] { input[type=checkbox] {
width: auto;
display: inline-block; display: inline-block;
vertical-align: middle; vertical-align: middle;
} }
input[name$=title] {
font-weight: bold;
}
textarea { textarea {
font-size: 1rem; font-size: 1rem;
padding: 5px 5px;
height: 15em; height: 15em;
line-height: 1.5;
} }
input[type=checkbox] {
width: auto;
}
select { select {
background: white; background: white;
} padding-left: 3px;
div.filefield {
border: 1px solid #aaa;
background: white;
padding: 4px;
input { border: none }
} }
ul.errorlist { ul.errorlist {

Wyświetl plik

@ -49,9 +49,6 @@ div.spacer {
height: 1rem; height: 1rem;
clear: both; } clear: both; }
header, nav, section, footer {
padding: 1rem; }
nav { nav {
padding: 0; } padding: 0; }
nav button#hamburger { nav button#hamburger {
@ -133,9 +130,14 @@ div.edit {
text-align: center; } text-align: center; }
div.edit.page { div.edit.page {
position: fixed; position: fixed;
left: 1em; right: 1em;
top: 1em; bottom: 1em;
z-index: 1000; } z-index: 1000; }
div.edit.page img {
width: 75px;
height: auto; }
div.edit.page a:before, div.edit.page button:before, div.edit.page a:after, div.edit.page button:after {
display: none; }
div.edit a, div.edit button, a.edit { div.edit a, div.edit button, a.edit {
font-family: inherit; font-family: inherit;
@ -155,8 +157,7 @@ div.edit a, div.edit button, a.edit {
content: ' ]'; } content: ' ]'; }
section { section {
clear: both; clear: both; }
border-bottom: 2px solid #3573a8; }
section div.image img { section div.image img {
width: 100%; } width: 100%; }
section div.title { section div.title {
@ -182,8 +183,8 @@ section.contactsection textarea, section.contactsection input {
font-family: inherit; } font-family: inherit; }
/* Form elements */ /* Form elements */
form.cms div.wrapper { form.cms section {
overflow: hidden; } margin-top: 3em; }
form.cms div.global_error { form.cms div.global_error {
border: 2px dotted red; border: 2px dotted red;
@ -205,8 +206,8 @@ form.cms div.formfield {
margin-bottom: 10px; margin-bottom: 10px;
clear: both; clear: both;
box-sizing: border-box; } box-sizing: border-box; }
form.cms div.formfield.type, form.cms div.formfield.number { form.cms div.formfield.type, form.cms div.formfield.number, form.cms div.formfield.slug {
width: 75%; width: 77%;
clear: none; clear: none;
float: left; } float: left; }
form.cms div.formfield.number { form.cms div.formfield.number {
@ -222,13 +223,11 @@ form.cms div.formfield.error {
form.cms div.formfield.required div.label { form.cms div.formfield.required div.label {
font-weight: 700; } font-weight: 700; }
form.cms div.formfield.required input, form.cms div.formfield.required select, form.cms div.formfield.required .textarea, form.cms div.formfield.required div.textarea {
border: 1px solid black; }
form.cms div.label, form.cms label { form.cms div.label, form.cms label {
font-size: 0.8rem; font-size: 0.7rem;
font-weight: 400; font-weight: 400;
text-align: left; } text-align: left;
margin-bottom: 2px; }
form.cms div.input { form.cms div.input {
overflow: hidden; } overflow: hidden; }
@ -244,36 +243,32 @@ form.cms span.required {
form.cms input, form.cms select, form.cms textarea { form.cms input, form.cms select, form.cms textarea {
background: white; background: white;
color: black; color: black;
border: 1px solid #aaa; border: 0.5px solid #ccc;
border-radius: 3px;
font-size: 1rem; font-size: 1rem;
display: block; display: block;
width: 100%; width: 100%;
box-sizing: border-box; box-sizing: border-box;
margin: 0; margin: 0;
padding: 5px; padding: 5px 8px;
font-family: inherit; } font-family: inherit; }
form.cms input[type=checkbox] { form.cms input[type=checkbox] {
width: auto;
display: inline-block; display: inline-block;
vertical-align: middle; } vertical-align: middle; }
form.cms input[name$=title] {
font-weight: bold; }
form.cms textarea { form.cms textarea {
font-size: 1rem; font-size: 1rem;
padding: 5px 5px; height: 15em;
height: 15em; } line-height: 1.5; }
form.cms input[type=checkbox] {
width: auto; }
form.cms select { form.cms select {
background: white; }
form.cms div.filefield {
border: 1px solid #aaa;
background: white; background: white;
padding: 4px; } padding-left: 3px; }
form.cms div.filefield input {
border: none; }
form.cms ul.errorlist { form.cms ul.errorlist {
margin: 0; margin: 0;

File diff suppressed because one or more lines are too long

Wyświetl plik

@ -0,0 +1,75 @@
<?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>

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 2.3 KiB

Wyświetl plik

@ -0,0 +1,60 @@
<?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>

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 1.7 KiB

Wyświetl plik

@ -0,0 +1 @@
{% extends 'cms/admin.html' %}

Wyświetl plik

@ -0,0 +1,19 @@
{% load static %}
{% load i18n %}
<!DOCTYPE html>
<html lang="{% get_current_language as lang%}{{lang}}">
<head>
<title>{% block title %}{% endblock %}</title>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="{% static 'cms/cms.scss.css' %}">
{% block extrahead %}{% endblock %}
</head>
<body>
{% block content %}
{% endblock %}
{% block extrabody %}{% endblock %}
</body>
</html>

Wyświetl plik

@ -1,19 +1,16 @@
{% extends 'base.html' %} {% extends 'admin.html' %}
{% load i18n %} {% load static i18n %}
{% block title %}{% trans 'Edit' %} {{form.instance}}{% endblock %}
{% block content %} {% block content %}
<form method="POST" enctype="multipart/form-data" class="cms"> <form method="POST" enctype="multipart/form-data" class="cms">
{% csrf_token %} {% csrf_token %}
{{form.media}} {{form.media}}
<div class="edit page">
<button>{% trans 'save' %}</button>
</div>
{% if form %}
<section> <section>
<div class="wrapper"> <div class="wrapper">
<div class="title">
<h1>{{form.instance}}</h1>
</div>
{% if form.errors or formset.errors %} {% if form.errors or formset.errors %}
<div class="global_error"> <div class="global_error">
{% trans 'Please correct the error(s) below and save again' %} {% trans 'Please correct the error(s) below and save again' %}
@ -26,16 +23,9 @@
{% include 'cms/formfield.html' with field=field %} {% include 'cms/formfield.html' with field=field %}
{% endif %} {% endif %}
{% endfor %} {% endfor %}
{% if form.instance.pk %}
<div class="formfield">
<div class="input">
<input type="checkbox" name="delete" id="id_delete"> <label for="id_delete">{% trans 'Delete' %}</label>
</div>
</div>
{% endif %}
</div> </div>
</section> </section>
{% endif %}
{% if formset %} {% if formset %}
{{formset.management_form}} {{formset.management_form}}
@ -46,7 +36,6 @@
{% endfor %} {% endfor %}
<section> <section>
<div class="wrapper"> <div class="wrapper">
<h1>{{form.instance}}</h1>
<div class="subform"> <div class="subform">
{% for field in form.visible_fields %} {% for field in form.visible_fields %}
{% if field.name == 'DELETE' and not form.instance.pk %} {% if field.name == 'DELETE' and not form.instance.pk %}
@ -62,13 +51,20 @@
</section> </section>
{% endfor %} {% endfor %}
{% endif %} {% endif %}
<div class="edit page">
<button><img src="{% static 'cms/ok.svg' %}"></button>
</div>
</form> </form>
{% endblock %} {% endblock %}
{% block extrabody %} {% block extrabody %}
<script type="text/javascript" src="/static/admin/js/urlify.js"></script> <script type="text/javascript" src="/static/admin/js/urlify.js"></script>
<script> <script>
// https://stackoverflow.com/a/25621277 document.addEventListener("DOMContentLoaded", function(event) {
// Auto-resize <textarea's>
var tx = document.getElementsByTagName('textarea'); var tx = document.getElementsByTagName('textarea');
for (var i = 0; i < tx.length; i++) { for (var i = 0; i < tx.length; i++) {
tx[i].setAttribute('style', 'height:0;overflow-y:hidden;'); tx[i].setAttribute('style', 'height:0;overflow-y:hidden;');
@ -80,14 +76,12 @@
this.style.height = (this.scrollHeight) + 'px'; this.style.height = (this.scrollHeight) + 'px';
} }
// My own implementation of Django's prepopulate.js
var slugfield = document.getElementById('id_slug'); var slugfield = document.getElementById('id_slug');
var titlefield = document.getElementById('id_title'); var titlefield = document.getElementById('id_title');
/* My own implementation of Django's prepopulate.js */
if (slugfield && titlefield) { if (slugfield && titlefield) {
var virgin = slugfield.value === ''; var virgin = slugfield.value === '';
if (virgin) { if (virgin) {
titlefield.addEventListener('input', function(event) { titlefield.addEventListener('input', function(event) {
if (virgin) { if (virgin) {
@ -100,8 +94,9 @@
} }
} }
{% if fields_per_type %} // Auto-hide non-relevant fields, based on type field
{% if fields_per_type %}
var typefield = document.getElementById('id_type'); var typefield = document.getElementById('id_type');
fields_per_type = {{fields_per_type|safe}}; fields_per_type = {{fields_per_type|safe}};
@ -148,5 +143,6 @@
show_relevant_fields(typefield.value.toLowerCase()); show_relevant_fields(typefield.value.toLowerCase());
} }
{% endif %} {% endif %}
});
</script> </script>
{% endblock %} {% endblock %}

Wyświetl plik

@ -1,3 +0,0 @@
{% extends 'cms/edit.html' %}
{% block formset %}{% endblock %}

Wyświetl plik

@ -1,5 +1,5 @@
{% extends 'base.html' %} {% extends 'base.html' %}
{% load i18n %} {% load static %}
{% load cms %} {% load cms %}
{% block title %}{{block.super}} - {{page.title}}{% endblock %} {% block title %}{{block.super}} - {{page.title}}{% endblock %}
@ -11,7 +11,7 @@
{% if user.is_staff %} {% if user.is_staff %}
<div class="edit page"> <div class="edit page">
<a href="{% if page.slug %}{% url 'cms:updatepage' page.pk %}{% else %}{% url 'cms:updatehomepage' %}{% endif %}">{% trans 'edit this page' %}</a> <a href="{% if page.slug %}{% url 'cms:editpage' page.slug %}{% else %}{% url 'cms:editpage' %}{% endif %}"><img src="{% static 'cms/edit.svg' %}"></a>
</div> </div>
{% endif %} {% endif %}

Wyświetl plik

@ -1,14 +1,12 @@
from django.urls import path from django.urls import path
from .views import PageView, UpdatePage, CreatePage, UpdateSection, CreateSection from .views import PageView, EditPage, CreatePage
app_name = 'cms' app_name = 'cms'
urlpatterns = [ urlpatterns = [
path('updatepage/', UpdatePage.as_view(), {'slug': ''}, name='updatehomepage'), path('edit/', EditPage.as_view(), name='editpage'),
path('updatepage/<int:pk>/', UpdatePage.as_view(), name='updatepage'), path('<slug:slug>/edit/', EditPage.as_view(), name='editpage'),
path('updatesection/<int:pk>/', UpdateSection.as_view(), name='updatesection'),
path('createpage/', CreatePage.as_view(), name='createpage'), path('createpage/', CreatePage.as_view(), name='createpage'),
path('createsection/<int:pk>', CreateSection.as_view(), name='createsection'),
path('', PageView.as_view(), name='page'), path('', PageView.as_view(), name='page'),
path('<slug:slug>/', PageView.as_view(), name='page'), path('<slug:slug>/', PageView.as_view(), name='page'),
] ]

Wyświetl plik

@ -2,8 +2,10 @@ import json
import swapper import swapper
from django.views import generic from django.views import generic
from django.http import Http404
from django.shortcuts import redirect from django.shortcuts import redirect
from django.views.generic.edit import FormMixin from django.views.generic.edit import FormMixin
from django.views.generic.detail import SingleObjectMixin
from django.contrib.admin.utils import NestedObjects from django.contrib.admin.utils import NestedObjects
from django.core.exceptions import ImproperlyConfigured from django.core.exceptions import ImproperlyConfigured
from django.contrib.contenttypes.models import ContentType from django.contrib.contenttypes.models import ContentType
@ -63,24 +65,7 @@ class SectionFormSetView(SectionView):
kwargs['formset'] = self.get_formset() kwargs['formset'] = self.get_formset()
return super().get_context_data(**kwargs) return super().get_context_data(**kwargs)
class MenuMixin: class PageView(generic.DetailView):
'''Add pages to template context'''
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
pages = Page.objects.filter(menu=True)
context.update({
'pages': pages,
})
return context
class MemoryMixin:
'''Remember the previous page in session'''
def dispatch(self, request, *args, **kwargs):
if request.user.is_authenticated:
request.session['previous_url'] = request.path
return super().dispatch(request, *args, **kwargs)
class PageView(MenuMixin, MemoryMixin, generic.DetailView):
'''View of a page with heterogeneous (polymorphic) sections''' '''View of a page with heterogeneous (polymorphic) sections'''
model = Page model = Page
template_name = 'cms/page.html' template_name = 'cms/page.html'
@ -99,7 +84,13 @@ class PageView(MenuMixin, MemoryMixin, generic.DetailView):
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
'''Initialize sections and render final response''' '''Initialize sections and render final response'''
try:
page = self.object = self.get_object() page = self.object = self.get_object()
except Http404:
if self.request.user.has_perm('cms_page_create'):
return redirect('cms:editpage', self.kwargs['slug'])
else:
raise
context = self.get_context_data(**kwargs) context = self.get_context_data(**kwargs)
sections = page.sections.all() sections = page.sections.all()
for section in sections: for section in sections:
@ -135,15 +126,46 @@ class PageView(MenuMixin, MemoryMixin, generic.DetailView):
}) })
return self.render_to_response(context) return self.render_to_response(context)
# The following views require a logged-in staff member
class StaffRequiredMixin(UserPassesTestMixin):
def test_func(self):
return self.request.user.is_staff
class TypeMixin(MenuMixin):
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs) context = super().get_context_data(**kwargs)
pages = Page.objects.filter(menu=True)
context.update({
'pages': pages,
})
return context
class EditPage(UserPassesTestMixin, generic.UpdateView):
model = Page
form_class = PageForm
template_name = 'cms/edit.html'
def setup(self, *args, slug='', **kwargs):
'''Supply a default argument for slug'''
super().setup(*args, slug=slug, **kwargs)
def test_func(self):
return self.request.user.has_perm('cms_page_change')
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs.update({'label_suffix': ''})
return kwargs
def form_valid(self, form):
object = form.save()
return redirect(object.get_absolute_url())
def get(self, request, *args, **kwargs):
try:
page = self.object = self.get_object()
except Http404:
return CreatePage.as_view()(request, slug=self.kwargs['slug'])
return super().get(request, *args, **kwargs)
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': ''})
fields_per_type = {} fields_per_type = {}
for model, _ in Section.TYPES: for model, _ in Section.TYPES:
ctype = ContentType.objects.get( ctype = ContentType.objects.get(
@ -157,38 +179,6 @@ class TypeMixin(MenuMixin):
}) })
return context return context
class BaseUpdateView(generic.UpdateView):
template_name = 'cms/edit.html'
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
kwargs.update({'label_suffix': ''})
return kwargs
def form_valid(self, form):
if 'delete' in self.request.POST:
collector = NestedObjects(using='default')
collector.collect([self.object])
self.template_name = 'cms/confirm.html'
return self.render_to_response(self.get_context_data(
deleted = collector.nested(),
protected = collector.protected,
object = self.object,
))
else:
form.save()
return redirect(self.request.session.get('previous_url'))
class UpdatePage(StaffRequiredMixin, TypeMixin, BaseUpdateView):
model = Page
form_class = PageForm
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': ''})
return context
def post(self, request, *args, **kwargs): def post(self, request, *args, **kwargs):
self.object = self.get_object() self.object = self.get_object()
form = self.get_form() form = self.get_form()
@ -202,21 +192,10 @@ class UpdatePage(StaffRequiredMixin, TypeMixin, BaseUpdateView):
def form_invalid(self, form, formset): def form_invalid(self, form, formset):
return self.render_to_response(self.get_context_data(form=form, formset=formset)) return self.render_to_response(self.get_context_data(form=form, formset=formset))
class UpdateSection(StaffRequiredMixin, TypeMixin, BaseUpdateView): class CreatePage(generic.CreateView):
model = Section
form_class = SectionForm
class CreatePage(StaffRequiredMixin, MenuMixin, generic.CreateView):
model = Page model = Page
form_class = PageForm form_class = PageForm
template_name = 'cms/new.html' template_name = 'cms/edit.html'
class CreateSection(StaffRequiredMixin, TypeMixin, generic.CreateView): def get_form_kwargs(self, *args, **kwargs):
model = Section return {'initial': {'slug': self.kwargs['slug']}}
form_class = SectionForm
template_name = 'cms/new.html'
def form_valid(self, form):
form.instance.page = Page.objects.get(pk=self.kwargs.get('pk'))
form.save()
return redirect(self.request.session.get('previous_url'))

Wyświetl plik

@ -23,7 +23,7 @@ class TextSection(Section):
@register_model('Button') @register_model('Button')
class ButtonSection(Section): class ButtonSection(Section):
fields = ['button_text', 'button_link'] fields = ['title', 'href']
class Meta: class Meta:
proxy = True proxy = True