kopia lustrzana https://github.com/rtts/django-simplecms
The dependency on swapper has been replaced with an incredibly simply
"registry". Client projects register their models and views during import time with a decorator. The cms views use the registered classes to generate pages and forms. Furthermore, the example application now combines the app and the project in one, inspired by this line in the Django documentation: There’s no restriction that a project package can’t also be considered an application and have models, etc.main
rodzic
98b1a61af1
commit
0e221ccf33
|
@ -1,4 +1 @@
|
||||||
from .decorators import register
|
|
||||||
from .cms import SectionView, SectionFormView
|
|
||||||
|
|
||||||
default_app_config = 'cms.apps.CmsConfig'
|
default_app_config = 'cms.apps.CmsConfig'
|
||||||
|
|
25
cms/cms.py
25
cms/cms.py
|
@ -1,25 +0,0 @@
|
||||||
from django.views.generic import edit
|
|
||||||
from django.http import HttpResponseRedirect
|
|
||||||
|
|
||||||
class SectionView:
|
|
||||||
'''Generic section view'''
|
|
||||||
template_name = 'cms/sections/section.html'
|
|
||||||
|
|
||||||
def __init__(self, request):
|
|
||||||
'''Initialize request attribute'''
|
|
||||||
self.request = request
|
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
|
||||||
'''Override this to customize a section's context'''
|
|
||||||
return kwargs
|
|
||||||
|
|
||||||
class SectionFormView(edit.FormMixin, SectionView):
|
|
||||||
'''Generic section with associated form'''
|
|
||||||
|
|
||||||
def post(self, request):
|
|
||||||
'''Process form'''
|
|
||||||
form = self.get_form()
|
|
||||||
if form.is_valid():
|
|
||||||
form.save(request)
|
|
||||||
return HttpResponseRedirect(self.get_success_url())
|
|
||||||
return form
|
|
|
@ -1,25 +1,17 @@
|
||||||
def register(verbose_name):
|
from cms import registry
|
||||||
import swapper
|
|
||||||
Section = swapper.load_model('cms', 'Section')
|
|
||||||
|
|
||||||
'''Decorator to register a specific section type'''
|
def page_model(cls):
|
||||||
def wrapper(view):
|
'''Decorator to register the Page model'''
|
||||||
Section._cms_views[view.__name__.lower()] = view
|
registry.page_class = cls
|
||||||
Section.TYPES.append((view.__name__.lower(), verbose_name))
|
return cls
|
||||||
return view
|
|
||||||
return wrapper
|
|
||||||
|
|
||||||
# def register_model(verbose_name):
|
def section_model(cls):
|
||||||
# '''Decorator to register a section subclass'''
|
'''Decorator to register the Section model'''
|
||||||
# def wrapper(model):
|
registry.section_class = cls
|
||||||
# parent_model = model.__bases__[-1]
|
return cls
|
||||||
# parent_model.TYPES.append((model.__name__.lower(), verbose_name))
|
|
||||||
# return model
|
|
||||||
# return wrapper
|
|
||||||
|
|
||||||
# def register_view(section_class):
|
def section_view(cls):
|
||||||
# '''Decorator to connect a section model to a view class'''
|
'''Decorator to register a view for a specific section'''
|
||||||
# def wrapper(model):
|
registry.views_per_type[cls.__name__.lower()] = cls
|
||||||
# section_class.view_class = model
|
registry.section_class.TYPES.append((cls.__name__.lower(), cls.verbose_name))
|
||||||
# return model
|
return cls
|
||||||
# return wrapper
|
|
||||||
|
|
14
cms/forms.py
14
cms/forms.py
|
@ -1,4 +1,3 @@
|
||||||
import swapper
|
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from django.db.models import Prefetch
|
from django.db.models import Prefetch
|
||||||
|
@ -6,8 +5,7 @@ from django.contrib.contenttypes.models import ContentType
|
||||||
from django.core.mail import EmailMessage
|
from django.core.mail import EmailMessage
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
Page = swapper.load_model('cms', 'Page')
|
from . import registry
|
||||||
Section = swapper.load_model('cms', 'Section')
|
|
||||||
|
|
||||||
class PageForm(forms.ModelForm):
|
class PageForm(forms.ModelForm):
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
|
@ -15,8 +13,8 @@ class PageForm(forms.ModelForm):
|
||||||
self.label_suffix = ''
|
self.label_suffix = ''
|
||||||
extra = 1 if self.instance.pk else 2
|
extra = 1 if self.instance.pk else 2
|
||||||
self.formsets = [forms.inlineformset_factory(
|
self.formsets = [forms.inlineformset_factory(
|
||||||
parent_model = Page,
|
parent_model = registry.page_class,
|
||||||
model = Section,
|
model = registry.section_class,
|
||||||
form = SectionForm,
|
form = SectionForm,
|
||||||
extra=extra,
|
extra=extra,
|
||||||
)(
|
)(
|
||||||
|
@ -50,7 +48,7 @@ class PageForm(forms.ModelForm):
|
||||||
return page
|
return page
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Page
|
model = registry.page_class
|
||||||
fields = '__all__'
|
fields = '__all__'
|
||||||
|
|
||||||
class SectionForm(forms.ModelForm):
|
class SectionForm(forms.ModelForm):
|
||||||
|
@ -73,7 +71,7 @@ class SectionForm(forms.ModelForm):
|
||||||
for field in self.instance._meta.get_fields():
|
for field in self.instance._meta.get_fields():
|
||||||
if field.one_to_many:
|
if field.one_to_many:
|
||||||
formset = forms.inlineformset_factory(
|
formset = forms.inlineformset_factory(
|
||||||
parent_model=Section,
|
parent_model=registry.section_class,
|
||||||
model=field.related_model,
|
model=field.related_model,
|
||||||
fields='__all__',
|
fields='__all__',
|
||||||
extra=extra,
|
extra=extra,
|
||||||
|
@ -116,7 +114,7 @@ class SectionForm(forms.ModelForm):
|
||||||
return section
|
return section
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Section
|
model = registry.section_class
|
||||||
exclude = ['page']
|
exclude = ['page']
|
||||||
|
|
||||||
class ContactForm(forms.Form):
|
class ContactForm(forms.Form):
|
||||||
|
|
|
@ -1,61 +0,0 @@
|
||||||
# Generated by Django 3.0.2 on 2020-02-16 14:27
|
|
||||||
|
|
||||||
import cms.models
|
|
||||||
from django.conf import settings
|
|
||||||
from django.db import migrations, models
|
|
||||||
import django.db.models.deletion
|
|
||||||
import embed_video.fields
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
initial = True
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
migrations.swappable_dependency(settings.CMS_PAGE_MODEL),
|
|
||||||
('contenttypes', '0002_remove_content_type_name'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.CreateModel(
|
|
||||||
name='Page',
|
|
||||||
fields=[
|
|
||||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
|
||||||
('number', models.PositiveIntegerField(blank=True, verbose_name='number')),
|
|
||||||
('title', cms.models.VarCharField(verbose_name='page')),
|
|
||||||
('slug', models.SlugField(blank=True, unique=True, verbose_name='slug')),
|
|
||||||
('menu', models.BooleanField(default=True, verbose_name='visible in menu')),
|
|
||||||
],
|
|
||||||
options={
|
|
||||||
'verbose_name': 'Page',
|
|
||||||
'verbose_name_plural': 'Pages',
|
|
||||||
'ordering': ['number'],
|
|
||||||
'abstract': False,
|
|
||||||
'swappable': 'CMS_PAGE_MODEL',
|
|
||||||
},
|
|
||||||
bases=(cms.models.Numbered, models.Model),
|
|
||||||
),
|
|
||||||
migrations.CreateModel(
|
|
||||||
name='Section',
|
|
||||||
fields=[
|
|
||||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
|
||||||
('type', cms.models.VarCharField(verbose_name='type')),
|
|
||||||
('number', models.PositiveIntegerField(blank=True, verbose_name='number')),
|
|
||||||
('title', cms.models.VarCharField(verbose_name='section')),
|
|
||||||
('content', models.TextField(blank=True, verbose_name='content')),
|
|
||||||
('image', models.ImageField(blank=True, upload_to='', verbose_name='image')),
|
|
||||||
('video', embed_video.fields.EmbedVideoField(blank=True, help_text='Paste a YouTube, Vimeo, or SoundCloud link', verbose_name='video')),
|
|
||||||
('href', cms.models.VarCharField(blank=True, verbose_name='link')),
|
|
||||||
('page', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='sections', to=settings.CMS_PAGE_MODEL, verbose_name='page')),
|
|
||||||
('polymorphic_ctype', models.ForeignKey(editable=False, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='polymorphic_cms.section_set+', to='contenttypes.ContentType')),
|
|
||||||
],
|
|
||||||
options={
|
|
||||||
'verbose_name': 'section',
|
|
||||||
'verbose_name_plural': 'sections',
|
|
||||||
'ordering': ['number'],
|
|
||||||
'abstract': False,
|
|
||||||
'swappable': 'CMS_SECTION_MODEL',
|
|
||||||
},
|
|
||||||
bases=(cms.models.Numbered, models.Model),
|
|
||||||
),
|
|
||||||
]
|
|
|
@ -1,17 +0,0 @@
|
||||||
# Generated by Django 3.0.3 on 2020-03-21 10:23
|
|
||||||
|
|
||||||
from django.db import migrations
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('cms', '0002_initial_homepage'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.RemoveField(
|
|
||||||
model_name='section',
|
|
||||||
name='polymorphic_ctype',
|
|
||||||
),
|
|
||||||
]
|
|
|
@ -1,5 +1,3 @@
|
||||||
import swapper
|
|
||||||
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
from django.utils.text import slugify
|
from django.utils.text import slugify
|
||||||
|
@ -89,16 +87,10 @@ class BasePage(Numbered, models.Model):
|
||||||
|
|
||||||
class BaseSection(Numbered, models.Model):
|
class BaseSection(Numbered, models.Model):
|
||||||
'''Abstract base model for sections'''
|
'''Abstract base model for sections'''
|
||||||
|
|
||||||
# These will be populated by @register
|
|
||||||
TYPES = []
|
TYPES = []
|
||||||
_cms_views = {}
|
|
||||||
|
|
||||||
page = models.ForeignKey(swapper.get_model_name('cms', 'Page'), verbose_name=_('page'), related_name='sections', on_delete=models.PROTECT)
|
|
||||||
title = VarCharField(_('section'))
|
title = VarCharField(_('section'))
|
||||||
type = VarCharField(_('type'))
|
type = VarCharField(_('type'))
|
||||||
number = models.PositiveIntegerField(_('number'), blank=True)
|
number = models.PositiveIntegerField(_('number'), 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'))
|
||||||
|
@ -118,33 +110,8 @@ class BaseSection(Numbered, models.Model):
|
||||||
else:
|
else:
|
||||||
return self.title
|
return self.title
|
||||||
|
|
||||||
def get_view(self, request):
|
|
||||||
'''Try to instantiate the registered view for this section'''
|
|
||||||
try:
|
|
||||||
return self.__class__._cms_views[self.type](request)
|
|
||||||
except:
|
|
||||||
raise ImproperlyConfigured(
|
|
||||||
f'No view registered for sections of type {self.type}!')
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def get_fields_per_type(cls):
|
|
||||||
fields_per_type = {}
|
|
||||||
for name, view in cls._cms_views.items():
|
|
||||||
fields_per_type[name] = ['type', 'number'] + view.fields
|
|
||||||
return fields_per_type
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
abstract = True
|
abstract = True
|
||||||
verbose_name = _('section')
|
verbose_name = _('section')
|
||||||
verbose_name_plural = _('sections')
|
verbose_name_plural = _('sections')
|
||||||
ordering = ['number']
|
ordering = ['number']
|
||||||
|
|
||||||
class Page(BasePage):
|
|
||||||
'''Swappable page model'''
|
|
||||||
class Meta(BasePage.Meta):
|
|
||||||
swappable = swapper.swappable_setting('cms', 'Page')
|
|
||||||
|
|
||||||
class Section(BaseSection):
|
|
||||||
'''Swappable section model'''
|
|
||||||
class Meta(BaseSection.Meta):
|
|
||||||
swappable = swapper.swappable_setting('cms', 'Section')
|
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
views_per_type = {}
|
||||||
|
page_class = None
|
||||||
|
section_class = None
|
||||||
|
|
||||||
|
def get_view(section, request):
|
||||||
|
'''Instantiate the registered view of a section'''
|
||||||
|
return views_per_type[section.type](request)
|
||||||
|
|
||||||
|
def get_fields_per_type():
|
||||||
|
fields_per_type = {}
|
||||||
|
for name, view in views_per_type.items():
|
||||||
|
fields_per_type[name] = ['title', 'type', 'number'] + view.fields
|
||||||
|
return fields_per_type
|
|
@ -5,6 +5,8 @@ from django.shortcuts import reverse
|
||||||
from django.utils.safestring import mark_safe
|
from django.utils.safestring import mark_safe
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
|
from cms import registry
|
||||||
|
|
||||||
MARKDOWN_EXTENSIONS = ['extra', 'smarty']
|
MARKDOWN_EXTENSIONS = ['extra', 'smarty']
|
||||||
register = template.Library()
|
register = template.Library()
|
||||||
|
|
||||||
|
@ -51,7 +53,7 @@ class IncludeSectionNode(template.Node):
|
||||||
csrf_token = self.csrf_token.resolve(context)
|
csrf_token = self.csrf_token.resolve(context)
|
||||||
request = self.request.resolve(context)
|
request = self.request.resolve(context)
|
||||||
perms = self.perms.resolve(context)
|
perms = self.perms.resolve(context)
|
||||||
view = section.get_view(request)
|
view = registry.get_view(section, request)
|
||||||
section_context = view.get_context_data(
|
section_context = view.get_context_data(
|
||||||
csrf_token=csrf_token,
|
csrf_token=csrf_token,
|
||||||
section=section,
|
section=section,
|
||||||
|
|
57
cms/views.py
57
cms/views.py
|
@ -1,19 +1,39 @@
|
||||||
import json
|
import json
|
||||||
import swapper
|
|
||||||
|
|
||||||
from django.shortcuts import redirect
|
from django.shortcuts import redirect
|
||||||
from django.views.generic import base, detail, edit
|
from django.views.generic import base, detail, edit
|
||||||
from django.contrib.auth.mixins import UserPassesTestMixin
|
from django.contrib.auth.mixins import UserPassesTestMixin
|
||||||
from django.http import Http404, HttpResponse, HttpResponseRedirect, HttpResponseBadRequest
|
from django.http import Http404, HttpResponse, HttpResponseRedirect, HttpResponseBadRequest
|
||||||
|
|
||||||
|
from . import registry
|
||||||
from .forms import PageForm, SectionForm
|
from .forms import PageForm, SectionForm
|
||||||
|
|
||||||
Page = swapper.load_model('cms', 'Page')
|
class SectionView:
|
||||||
Section = swapper.load_model('cms', 'Section')
|
'''Generic section view'''
|
||||||
|
template_name = 'cms/sections/section.html'
|
||||||
|
|
||||||
|
def __init__(self, request):
|
||||||
|
'''Initialize request attribute'''
|
||||||
|
self.request = request
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
'''Override this to customize a section's context'''
|
||||||
|
return kwargs
|
||||||
|
|
||||||
|
class SectionFormView(edit.FormMixin, SectionView):
|
||||||
|
'''Generic section with associated form'''
|
||||||
|
|
||||||
|
def post(self, request):
|
||||||
|
'''Process form'''
|
||||||
|
form = self.get_form()
|
||||||
|
if form.is_valid():
|
||||||
|
form.save(request)
|
||||||
|
return HttpResponseRedirect(self.get_success_url())
|
||||||
|
return form
|
||||||
|
|
||||||
class PageView(detail.DetailView):
|
class PageView(detail.DetailView):
|
||||||
'''View of a page with heterogeneous sections'''
|
'''View of a page with heterogeneous sections'''
|
||||||
model = Page
|
model = registry.page_class
|
||||||
template_name = 'cms/page.html'
|
template_name = 'cms/page.html'
|
||||||
|
|
||||||
def setup(self, *args, slug='', **kwargs):
|
def setup(self, *args, slug='', **kwargs):
|
||||||
|
@ -25,8 +45,13 @@ class PageView(detail.DetailView):
|
||||||
try:
|
try:
|
||||||
page = self.object = self.get_object()
|
page = self.object = self.get_object()
|
||||||
except Http404:
|
except Http404:
|
||||||
if self.request.user.has_perm('cms_page_create'):
|
if self.kwargs['slug'] == '':
|
||||||
|
page = registry.page_class(title='Homepage', slug='')
|
||||||
|
page.save()
|
||||||
|
self.object = page
|
||||||
|
elif self.request.user.has_perm('cms_page_create'):
|
||||||
return redirect('cms:updatepage', self.kwargs['slug'])
|
return redirect('cms:updatepage', self.kwargs['slug'])
|
||||||
|
else:
|
||||||
raise
|
raise
|
||||||
context = self.get_context_data(**kwargs)
|
context = self.get_context_data(**kwargs)
|
||||||
sections = page.sections.all()
|
sections = page.sections.all()
|
||||||
|
@ -48,7 +73,7 @@ class PageView(detail.DetailView):
|
||||||
sections = page.sections.all()
|
sections = page.sections.all()
|
||||||
for section in sections:
|
for section in sections:
|
||||||
if section.pk == pk:
|
if section.pk == pk:
|
||||||
view = section.get_view(request)
|
view = registry.get_view(section, request)
|
||||||
result = view.post(request)
|
result = view.post(request)
|
||||||
if isinstance(result, HttpResponse):
|
if isinstance(result, HttpResponse):
|
||||||
return result
|
return result
|
||||||
|
@ -62,7 +87,7 @@ class PageView(detail.DetailView):
|
||||||
|
|
||||||
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)
|
pages = registry.page_class.objects.filter(menu=True)
|
||||||
context.update({
|
context.update({
|
||||||
'pages': pages,
|
'pages': pages,
|
||||||
})
|
})
|
||||||
|
@ -70,7 +95,7 @@ class PageView(detail.DetailView):
|
||||||
|
|
||||||
class EditPage(UserPassesTestMixin, edit.ModelFormMixin, base.TemplateResponseMixin, base.View):
|
class EditPage(UserPassesTestMixin, edit.ModelFormMixin, base.TemplateResponseMixin, base.View):
|
||||||
'''Base view with nested forms for editing the page and all its sections'''
|
'''Base view with nested forms for editing the page and all its sections'''
|
||||||
model = Page
|
model = registry.page_class
|
||||||
form_class = PageForm
|
form_class = PageForm
|
||||||
template_name = 'cms/edit.html'
|
template_name = 'cms/edit.html'
|
||||||
|
|
||||||
|
@ -88,7 +113,7 @@ class EditPage(UserPassesTestMixin, edit.ModelFormMixin, base.TemplateResponseMi
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
'''Populate the fields_per_type dict for use in javascript'''
|
'''Populate the fields_per_type dict for use in javascript'''
|
||||||
context = super().get_context_data(**kwargs)
|
context = super().get_context_data(**kwargs)
|
||||||
context['fields_per_type'] = json.dumps(Section.get_fields_per_type())
|
context['fields_per_type'] = json.dumps(registry.get_fields_per_type())
|
||||||
return context
|
return context
|
||||||
|
|
||||||
def get_object(self):
|
def get_object(self):
|
||||||
|
@ -118,13 +143,13 @@ class EditPage(UserPassesTestMixin, edit.ModelFormMixin, base.TemplateResponseMi
|
||||||
class CreatePage(EditPage):
|
class CreatePage(EditPage):
|
||||||
'''View for creating new pages'''
|
'''View for creating new pages'''
|
||||||
def get_object(self):
|
def get_object(self):
|
||||||
return Page()
|
return registry.page_class()
|
||||||
|
|
||||||
class UpdatePage(EditPage):
|
class UpdatePage(EditPage):
|
||||||
'''View for editing existing pages'''
|
'''View for editing existing pages'''
|
||||||
|
|
||||||
class EditSection(UserPassesTestMixin, edit.ModelFormMixin, base.TemplateResponseMixin, base.View):
|
class EditSection(UserPassesTestMixin, edit.ModelFormMixin, base.TemplateResponseMixin, base.View):
|
||||||
model = Section
|
model = registry.section_class
|
||||||
form_class = SectionForm
|
form_class = SectionForm
|
||||||
template_name = 'cms/edit.html'
|
template_name = 'cms/edit.html'
|
||||||
|
|
||||||
|
@ -140,20 +165,20 @@ class EditSection(UserPassesTestMixin, edit.ModelFormMixin, base.TemplateRespons
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
context = super().get_context_data(**kwargs)
|
context = super().get_context_data(**kwargs)
|
||||||
context['fields_per_type'] = json.dumps(Section.get_fields_per_type())
|
context['fields_per_type'] = json.dumps(registry.get_fields_per_type())
|
||||||
return context
|
return context
|
||||||
|
|
||||||
def get_object(self, queryset=None):
|
def get_object(self, queryset=None):
|
||||||
try:
|
try:
|
||||||
self.page = Page.objects.get(slug=self.kwargs['slug'])
|
self.page = registry.page_class.objects.get(slug=self.kwargs['slug'])
|
||||||
except Page.DoesNotExist:
|
except registry.page_class.DoesNotExist:
|
||||||
raise Http404()
|
raise Http404()
|
||||||
return self.get_section()
|
return self.get_section()
|
||||||
|
|
||||||
def get_section(self):
|
def get_section(self):
|
||||||
try:
|
try:
|
||||||
section = self.page.sections.get(number=self.kwargs['number'])
|
section = self.page.sections.get(number=self.kwargs['number'])
|
||||||
except Section.DoesNotExist:
|
except self.page.sections.DoesNotExist:
|
||||||
raise Http404()
|
raise Http404()
|
||||||
return section
|
return section
|
||||||
|
|
||||||
|
@ -177,7 +202,7 @@ class EditSection(UserPassesTestMixin, edit.ModelFormMixin, base.TemplateRespons
|
||||||
|
|
||||||
class CreateSection(EditSection):
|
class CreateSection(EditSection):
|
||||||
def get_section(self):
|
def get_section(self):
|
||||||
return Section(page=self.page)
|
return registry.section_class(page=self.page)
|
||||||
|
|
||||||
class UpdateSection(EditSection):
|
class UpdateSection(EditSection):
|
||||||
pass
|
pass
|
||||||
|
|
|
@ -1,26 +0,0 @@
|
||||||
# Generated by Django 3.0.2 on 2020-03-21 16:58
|
|
||||||
|
|
||||||
from django.conf import settings
|
|
||||||
from django.db import migrations, models
|
|
||||||
import django.db.models.deletion
|
|
||||||
|
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
|
||||||
|
|
||||||
dependencies = [
|
|
||||||
('app', '0001_initial'),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.CreateModel(
|
|
||||||
name='SectionImage',
|
|
||||||
fields=[
|
|
||||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
|
||||||
('image', models.ImageField(upload_to='', verbose_name='Image')),
|
|
||||||
('section', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='images', to=settings.CMS_SECTION_MODEL)),
|
|
||||||
],
|
|
||||||
options={
|
|
||||||
'ordering': ['pk'],
|
|
||||||
},
|
|
||||||
),
|
|
||||||
]
|
|
|
@ -1,24 +1,29 @@
|
||||||
import cms
|
from cms.views import SectionView, SectionFormView
|
||||||
|
from cms.decorators import section_view
|
||||||
from cms.forms import ContactForm
|
from cms.forms import ContactForm
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
|
||||||
@cms.register(_('Text'))
|
@section_view
|
||||||
class Text(cms.SectionView):
|
class Text(SectionView):
|
||||||
|
verbose_name = _('Text')
|
||||||
fields = ['title', 'content']
|
fields = ['title', 'content']
|
||||||
template_name = 'app/sections/text.html'
|
template_name = 'app/sections/text.html'
|
||||||
|
|
||||||
@cms.register(_('Images'))
|
@section_view
|
||||||
class Images(cms.SectionView):
|
class Images(SectionView):
|
||||||
|
verbose_name = _('Images')
|
||||||
fields = ['title', 'images']
|
fields = ['title', 'images']
|
||||||
template_name = 'app/sections/images.html'
|
template_name = 'app/sections/images.html'
|
||||||
|
|
||||||
@cms.register(_('Video'))
|
@section_view
|
||||||
class Video(cms.SectionView):
|
class Video(SectionView):
|
||||||
|
verbose_name = _('Video')
|
||||||
fields = ['title', 'video']
|
fields = ['title', 'video']
|
||||||
template_name = 'app/sections/video.html'
|
template_name = 'app/sections/video.html'
|
||||||
|
|
||||||
@cms.register(_('Contact'))
|
@section_view
|
||||||
class Contact(cms.SectionFormView):
|
class Contact(SectionFormView):
|
||||||
|
verbose_name = _('Contact')
|
||||||
fields = ['title']
|
fields = ['title']
|
||||||
form_class = ContactForm
|
form_class = ContactForm
|
||||||
success_url = '/thanks/'
|
success_url = '/thanks/'
|
|
@ -1,7 +1,6 @@
|
||||||
# Generated by Django 3.0.2 on 2020-03-21 16:44
|
# Generated by Django 3.0.2 on 2020-03-22 11:11
|
||||||
|
|
||||||
import cms.models
|
import cms.models
|
||||||
from django.conf import settings
|
|
||||||
from django.db import migrations, models
|
from django.db import migrations, models
|
||||||
import django.db.models.deletion
|
import django.db.models.deletion
|
||||||
import embed_video.fields
|
import embed_video.fields
|
||||||
|
@ -32,6 +31,16 @@ class Migration(migrations.Migration):
|
||||||
},
|
},
|
||||||
bases=(cms.models.Numbered, models.Model),
|
bases=(cms.models.Numbered, models.Model),
|
||||||
),
|
),
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='SectionImage',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('image', models.ImageField(upload_to='', verbose_name='Image')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'ordering': ['pk'],
|
||||||
|
},
|
||||||
|
),
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='Section',
|
name='Section',
|
||||||
fields=[
|
fields=[
|
||||||
|
@ -43,7 +52,7 @@ class Migration(migrations.Migration):
|
||||||
('image', models.ImageField(blank=True, upload_to='', verbose_name='image')),
|
('image', models.ImageField(blank=True, upload_to='', verbose_name='image')),
|
||||||
('video', embed_video.fields.EmbedVideoField(blank=True, help_text='Paste a YouTube, Vimeo, or SoundCloud link', verbose_name='video')),
|
('video', embed_video.fields.EmbedVideoField(blank=True, help_text='Paste a YouTube, Vimeo, or SoundCloud link', verbose_name='video')),
|
||||||
('href', cms.models.VarCharField(blank=True, verbose_name='link')),
|
('href', cms.models.VarCharField(blank=True, verbose_name='link')),
|
||||||
('page', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='sections', to=settings.CMS_PAGE_MODEL, verbose_name='page')),
|
('page', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, related_name='sections', to='example.Page', verbose_name='page')),
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
'verbose_name': 'section',
|
'verbose_name': 'section',
|
|
@ -1,21 +1,25 @@
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from cms.models import BasePage, BaseSection
|
|
||||||
from django.utils.translation import gettext_lazy as _
|
from django.utils.translation import gettext_lazy as _
|
||||||
|
from cms.models import BasePage, BaseSection
|
||||||
|
from cms.decorators import page_model, section_model
|
||||||
|
|
||||||
|
@page_model
|
||||||
class Page(BasePage):
|
class Page(BasePage):
|
||||||
'''Add custom fields here. Already existing fields: title, slug,
|
'''Add custom fields here. Already existing fields: title, slug,
|
||||||
number, menu
|
number, menu
|
||||||
|
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
@section_model
|
||||||
class Section(BaseSection):
|
class Section(BaseSection):
|
||||||
'''Add custom fields here. Already existing fields: title, type,
|
'''Add custom fields here. Already existing fields: title, type,
|
||||||
number, content, image, video, href
|
number, content, image, video, href
|
||||||
|
|
||||||
'''
|
'''
|
||||||
|
page = models.ForeignKey(Page, verbose_name=_('page'), related_name='sections', on_delete=models.PROTECT)
|
||||||
|
|
||||||
class SectionImage(models.Model):
|
class SectionImage(models.Model):
|
||||||
section = models.ForeignKey(Section, related_name='images', on_delete=models.CASCADE)
|
#section = models.ForeignKey(Section, related_name='images', on_delete=models.CASCADE)
|
||||||
image = models.ImageField(_('Image'))
|
image = models.ImageField(_('Image'))
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
|
@ -1,19 +1,13 @@
|
||||||
import os, random, string
|
import os, random, string
|
||||||
try:
|
|
||||||
import uwsgi
|
|
||||||
DEBUG = False
|
|
||||||
except ImportError:
|
|
||||||
DEBUG = True
|
|
||||||
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
|
|
||||||
|
|
||||||
PROJECT_NAME = 'example'
|
PROJECT_NAME = os.path.basename(os.path.dirname(os.path.abspath(__file__)))
|
||||||
KEYFILE = f'/tmp/{PROJECT_NAME}.secret'
|
KEYFILE = f'/tmp/{PROJECT_NAME}.secret'
|
||||||
ADMINS = [('JJ Vens', 'jj@rtts.eu')]
|
ADMINS = [('JJ Vens', 'jj@rtts.eu')]
|
||||||
DEFAULT_FROM_EMAIL = 'noreply@rtts.eu'
|
DEFAULT_FROM_EMAIL = 'noreply@rtts.eu'
|
||||||
DEFAULT_TO_EMAIL = 'jj@rtts.eu'
|
DEFAULT_TO_EMAIL = 'jj@rtts.eu'
|
||||||
ALLOWED_HOSTS = ['*']
|
ALLOWED_HOSTS = ['*']
|
||||||
ROOT_URLCONF = 'project.urls'
|
ROOT_URLCONF = PROJECT_NAME + '.urls'
|
||||||
WSGI_APPLICATION = 'project.wsgi.application'
|
WSGI_APPLICATION = PROJECT_NAME + '.wsgi.application'
|
||||||
LANGUAGE_CODE = 'nl'
|
LANGUAGE_CODE = 'nl'
|
||||||
TIME_ZONE = 'Europe/Amsterdam'
|
TIME_ZONE = 'Europe/Amsterdam'
|
||||||
USE_I18N = True
|
USE_I18N = True
|
||||||
|
@ -24,8 +18,13 @@ STATIC_ROOT = '/srv/' + PROJECT_NAME + '/static'
|
||||||
MEDIA_URL = '/media/'
|
MEDIA_URL = '/media/'
|
||||||
MEDIA_ROOT = '/srv/' + PROJECT_NAME + '/media'
|
MEDIA_ROOT = '/srv/' + PROJECT_NAME + '/media'
|
||||||
LOGIN_REDIRECT_URL = '/'
|
LOGIN_REDIRECT_URL = '/'
|
||||||
CMS_SECTION_MODEL = 'app.Section'
|
|
||||||
CMS_PAGE_MODEL = 'app.Page'
|
try:
|
||||||
|
import uwsgi
|
||||||
|
DEBUG = False
|
||||||
|
except ImportError:
|
||||||
|
DEBUG = True
|
||||||
|
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
|
||||||
|
|
||||||
def read(file):
|
def read(file):
|
||||||
with open(file) as f:
|
with open(file) as f:
|
||||||
|
@ -40,7 +39,7 @@ except IOError:
|
||||||
write(KEYFILE, SECRET_KEY)
|
write(KEYFILE, SECRET_KEY)
|
||||||
|
|
||||||
INSTALLED_APPS = [
|
INSTALLED_APPS = [
|
||||||
'app',
|
PROJECT_NAME,
|
||||||
'django.contrib.admin',
|
'django.contrib.admin',
|
||||||
'django.contrib.auth',
|
'django.contrib.auth',
|
||||||
'django.contrib.contenttypes',
|
'django.contrib.contenttypes',
|
|
@ -3,7 +3,7 @@ import os
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'project.settings')
|
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'example.settings')
|
||||||
try:
|
try:
|
||||||
from django.core.management import execute_from_command_line
|
from django.core.management import execute_from_command_line
|
||||||
except ImportError as exc:
|
except ImportError as exc:
|
4
setup.py
4
setup.py
|
@ -3,7 +3,7 @@ from setuptools import setup, find_packages
|
||||||
|
|
||||||
setup(
|
setup(
|
||||||
name = 'django-simplecms',
|
name = 'django-simplecms',
|
||||||
version = '2.3.1',
|
version = '3.0.0',
|
||||||
url = 'https://github.com/rtts/django-simplecms',
|
url = 'https://github.com/rtts/django-simplecms',
|
||||||
author = 'Jaap Joris Vens',
|
author = 'Jaap Joris Vens',
|
||||||
author_email = 'jj@rtts.eu',
|
author_email = 'jj@rtts.eu',
|
||||||
|
@ -15,11 +15,9 @@ setup(
|
||||||
'django',
|
'django',
|
||||||
'django-extensions',
|
'django-extensions',
|
||||||
'django-embed-video',
|
'django-embed-video',
|
||||||
'django-polymorphic',
|
|
||||||
'easy-thumbnails',
|
'easy-thumbnails',
|
||||||
'psycopg2',
|
'psycopg2',
|
||||||
'markdown',
|
'markdown',
|
||||||
'libsass',
|
'libsass',
|
||||||
'swapper',
|
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
Ładowanie…
Reference in New Issue