kopia lustrzana https://github.com/rtts/django-simplecms
Cleanup source tree
The separate apps numberedmodel and simplesass have been merged into cmsreadwriteweb
rodzic
5f5f303187
commit
2824d290f8
|
@ -5,8 +5,8 @@ test -d $dev/$1 && echo "That project already exists!" && exit 1
|
||||||
|
|
||||||
mkdir $1 && cd $1
|
mkdir $1 && cd $1
|
||||||
pip3 freeze > requirements.txt
|
pip3 freeze > requirements.txt
|
||||||
examples_dir=$(python3 -c 'import os,examples;print(os.path.dirname(examples.__file__))')
|
example_dir=$(python3 -c 'import os,examples;print(os.path.dirname(example.__file__))')
|
||||||
cp -r $examples_dir/{project,app,manage.py} .
|
cp -r $example_dir/{project,app,manage.py} .
|
||||||
sed -i s/example/$1/ project/settings.py
|
sed -i s/example/$1/ project/settings.py
|
||||||
|
|
||||||
# Assume the user has sudo access to postgres
|
# Assume the user has sudo access to postgres
|
||||||
|
@ -17,6 +17,7 @@ cat << EOF > .gitignore
|
||||||
__pycache__/
|
__pycache__/
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
|
./manage.py makemigrations app
|
||||||
./manage.py migrate
|
./manage.py migrate
|
||||||
./manage.py createsuperuser
|
./manage.py createsuperuser
|
||||||
./manage.py runserver --nostatic
|
./manage.py runserver --nostatic
|
||||||
|
|
|
@ -7,6 +7,6 @@ class CmsConfig(AppConfig):
|
||||||
verbose_name = _('Content Management System')
|
verbose_name = _('Content Management System')
|
||||||
|
|
||||||
def ready(self):
|
def ready(self):
|
||||||
# Need to load view models at startup to make the
|
# Need to load view models of all installed apps to make the
|
||||||
# register_view decorator work
|
# register_view decorator work
|
||||||
autodiscover_modules('views')
|
autodiscover_modules('views')
|
||||||
|
|
|
@ -1,16 +1,13 @@
|
||||||
def register_model(verbose_name):
|
def register_model(verbose_name):
|
||||||
'''Decorator to register a section subclass.
|
'''Decorator to register a section subclass'''
|
||||||
|
|
||||||
'''
|
|
||||||
def wrapper(model):
|
def wrapper(model):
|
||||||
model.__bases__[-1].TYPES.append((model.__name__.lower(), verbose_name))
|
parent_model = model.__bases__[-1]
|
||||||
|
parent_model.TYPES.append((model.__name__.lower(), verbose_name))
|
||||||
return model
|
return model
|
||||||
return wrapper
|
return wrapper
|
||||||
|
|
||||||
def register_view(section_class):
|
def register_view(section_class):
|
||||||
'''Decorator to connect a section model to a view class.
|
'''Decorator to connect a section model to a view class'''
|
||||||
|
|
||||||
'''
|
|
||||||
def wrapper(model):
|
def wrapper(model):
|
||||||
section_class.view_class = model
|
section_class.view_class = model
|
||||||
return model
|
return model
|
||||||
|
|
23
cms/forms.py
23
cms/forms.py
|
@ -1,8 +1,7 @@
|
||||||
|
import swapper
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.utils.translation import gettext_lazy as _
|
|
||||||
from django.contrib.contenttypes.models import ContentType
|
from django.contrib.contenttypes.models import ContentType
|
||||||
|
|
||||||
import swapper
|
|
||||||
Page = swapper.load_model('cms', 'Page')
|
Page = swapper.load_model('cms', 'Page')
|
||||||
Section = swapper.load_model('cms', 'Section')
|
Section = swapper.load_model('cms', 'Section')
|
||||||
|
|
||||||
|
@ -21,15 +20,12 @@ class SectionForm(forms.ModelForm):
|
||||||
|
|
||||||
def save(self):
|
def save(self):
|
||||||
section = super().save()
|
section = super().save()
|
||||||
app_label = section._meta.app_label
|
|
||||||
model = section.type
|
|
||||||
|
|
||||||
# Explanation: we'll get the content type of the model that
|
# Explanation: get the content type of the model that the user
|
||||||
# the user supplied when filling in this form, and save it's
|
# supplied when filling in this form, and save it's id to the
|
||||||
# id to the 'polymorphic_ctype_id' field. This way, the next
|
# 'polymorphic_ctype_id' field. The next time the object is
|
||||||
# time the object is requested from the database,
|
# requested from the database, django-polymorphic will convert
|
||||||
# django-polymorphic will automatically convert it to the
|
# it to the correct subclass.
|
||||||
# correct subclass.
|
|
||||||
section.polymorphic_ctype = ContentType.objects.get(
|
section.polymorphic_ctype = ContentType.objects.get(
|
||||||
app_label=section._meta.app_label,
|
app_label=section._meta.app_label,
|
||||||
model=section.type.lower(),
|
model=section.type.lower(),
|
||||||
|
@ -41,3 +37,10 @@ class SectionForm(forms.ModelForm):
|
||||||
class Meta:
|
class Meta:
|
||||||
model = Section
|
model = Section
|
||||||
exclude = ['page']
|
exclude = ['page']
|
||||||
|
#field_classes = {
|
||||||
|
# 'type': forms.ChoiceField,
|
||||||
|
#}
|
||||||
|
|
||||||
|
# There is definitely a bug in Django, since the above 'field_classes' gets
|
||||||
|
# ignored entirely. Workaround to force a ChoiceField anyway:
|
||||||
|
type = forms.ChoiceField()
|
||||||
|
|
|
@ -3,7 +3,7 @@ from django.conf import settings
|
||||||
from django.apps import apps
|
from django.apps import apps
|
||||||
from sass import compile
|
from sass import compile
|
||||||
|
|
||||||
class SimpleSassMiddleware:
|
class SassMiddleware:
|
||||||
def __init__(self, get_response):
|
def __init__(self, get_response):
|
||||||
self.get_response = get_response
|
self.get_response = get_response
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
# Generated by Django 3.0.2 on 2020-01-04 22:55
|
# Generated by Django 3.0.2 on 2020-01-05 11:43
|
||||||
|
|
||||||
import cms.models
|
import cms.models
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
@ -20,26 +20,27 @@ class Migration(migrations.Migration):
|
||||||
name='Page',
|
name='Page',
|
||||||
fields=[
|
fields=[
|
||||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
('slug', models.SlugField(blank=True, help_text='A short identifier to use in URLs', unique=True, verbose_name='slug')),
|
('number', models.PositiveIntegerField(blank=True, verbose_name='number')),
|
||||||
|
('slug', models.SlugField(blank=True, unique=True, verbose_name='slug')),
|
||||||
('title', cms.models.VarCharField(verbose_name='title')),
|
('title', cms.models.VarCharField(verbose_name='title')),
|
||||||
('position', models.PositiveIntegerField(blank=True, verbose_name='position')),
|
|
||||||
('menu', models.BooleanField(default=True, verbose_name='visible in menu')),
|
('menu', models.BooleanField(default=True, verbose_name='visible in menu')),
|
||||||
],
|
],
|
||||||
options={
|
options={
|
||||||
'verbose_name': 'Page',
|
'verbose_name': 'Page',
|
||||||
'verbose_name_plural': 'Pages',
|
'verbose_name_plural': 'Pages',
|
||||||
'ordering': ['position'],
|
'ordering': ['number'],
|
||||||
'abstract': False,
|
'abstract': False,
|
||||||
'swappable': 'CMS_PAGE_MODEL',
|
'swappable': 'CMS_PAGE_MODEL',
|
||||||
},
|
},
|
||||||
|
bases=(cms.models.Numbered, models.Model),
|
||||||
),
|
),
|
||||||
migrations.CreateModel(
|
migrations.CreateModel(
|
||||||
name='Section',
|
name='Section',
|
||||||
fields=[
|
fields=[
|
||||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
('type', cms.models.VarCharChoiceField(choices=[('textsection', 'Tekst'), ('imagesection', 'Afbeelding'), ('contactsection', 'Contact')], default='', verbose_name='type')),
|
('type', cms.models.VarCharField(blank=True, verbose_name='type')),
|
||||||
|
('number', models.PositiveIntegerField(blank=True, verbose_name='number')),
|
||||||
('title', cms.models.VarCharField(blank=True, verbose_name='title')),
|
('title', cms.models.VarCharField(blank=True, verbose_name='title')),
|
||||||
('position', models.PositiveIntegerField(blank=True, verbose_name='position')),
|
|
||||||
('content', models.TextField(blank=True, verbose_name='content')),
|
('content', models.TextField(blank=True, verbose_name='content')),
|
||||||
('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')),
|
||||||
|
@ -51,9 +52,10 @@ class Migration(migrations.Migration):
|
||||||
options={
|
options={
|
||||||
'verbose_name': 'section',
|
'verbose_name': 'section',
|
||||||
'verbose_name_plural': 'sections',
|
'verbose_name_plural': 'sections',
|
||||||
'ordering': ['position'],
|
'ordering': ['number'],
|
||||||
'abstract': False,
|
'abstract': False,
|
||||||
'swappable': 'CMS_SECTION_MODEL',
|
'swappable': 'CMS_SECTION_MODEL',
|
||||||
},
|
},
|
||||||
|
bases=(cms.models.Numbered, models.Model),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -4,7 +4,7 @@ Page = swapper.load_model('cms', 'Page')
|
||||||
|
|
||||||
def add_homepage(apps, schema_editor):
|
def add_homepage(apps, schema_editor):
|
||||||
if not Page.objects.exists():
|
if not Page.objects.exists():
|
||||||
Page(slug='', title='Homepage', position=1).save()
|
Page(slug='', title='Homepage', number=1).save()
|
||||||
|
|
||||||
class Migration(migrations.Migration):
|
class Migration(migrations.Migration):
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
'''CMS Models'''
|
|
||||||
|
|
||||||
import swapper
|
import swapper
|
||||||
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
|
@ -9,25 +7,62 @@ from django.utils.translation import gettext_lazy as _
|
||||||
from embed_video.fields import EmbedVideoField
|
from embed_video.fields import EmbedVideoField
|
||||||
from polymorphic.models import PolymorphicModel
|
from polymorphic.models import PolymorphicModel
|
||||||
|
|
||||||
from numberedmodel.models import NumberedModel
|
|
||||||
|
|
||||||
class VarCharField(models.TextField):
|
class VarCharField(models.TextField):
|
||||||
'''Variable width CharField'''
|
'''Variable width CharField'''
|
||||||
def formfield(self, **kwargs):
|
def formfield(self, **kwargs):
|
||||||
kwargs.update({'widget': TextInput})
|
kwargs.update({'widget': TextInput})
|
||||||
return super().formfield(**kwargs)
|
return super().formfield(**kwargs)
|
||||||
|
|
||||||
class VarCharChoiceField(models.TextField):
|
class Numbered:
|
||||||
'''Variable width CharField with choices'''
|
'''Mixin for numbered models. Overrides the save() method to
|
||||||
def formfield(self, **kwargs):
|
automatically renumber all instances returned by
|
||||||
kwargs.update({'widget': Select})
|
number_with_respect_to()
|
||||||
return super().formfield(**kwargs)
|
|
||||||
|
|
||||||
class BasePage(NumberedModel):
|
'''
|
||||||
|
def number_with_respect_to(self):
|
||||||
|
return self.__class__.objects.all()
|
||||||
|
|
||||||
|
def _renumber(self):
|
||||||
|
'''Renumbers the queryset while preserving the instance's number'''
|
||||||
|
|
||||||
|
queryset = self.number_with_respect_to()
|
||||||
|
field_name = self.__class__._meta.ordering[-1].lstrip('-')
|
||||||
|
this_nr = getattr(self, field_name)
|
||||||
|
if this_nr is None:
|
||||||
|
this_nr = len(queryset) + 1
|
||||||
|
|
||||||
|
# The algorithm: loop over the queryset and set each object's
|
||||||
|
# number to the counter. When an object's number equals the
|
||||||
|
# number of this instance, set this instance's number to the
|
||||||
|
# counter, increment the counter by 1, and finish the loop
|
||||||
|
counter = 1
|
||||||
|
inserted = False
|
||||||
|
for other in queryset.exclude(pk=self.pk):
|
||||||
|
other_nr = getattr(other, field_name)
|
||||||
|
if counter >= this_nr and not inserted:
|
||||||
|
setattr(self, field_name, counter)
|
||||||
|
inserted = True
|
||||||
|
counter += 1
|
||||||
|
if other_nr != counter:
|
||||||
|
setattr(other, field_name, counter)
|
||||||
|
super(NumberedModel, other).save()
|
||||||
|
counter += 1
|
||||||
|
if not inserted:
|
||||||
|
setattr(self, field_name, counter)
|
||||||
|
|
||||||
|
def save(self, *args, **kwargs):
|
||||||
|
self._renumber()
|
||||||
|
super().save(*args, **kwargs)
|
||||||
|
|
||||||
|
def delete(self, *args, **kwargs):
|
||||||
|
super().delete(*args, **kwargs)
|
||||||
|
self._renumber()
|
||||||
|
|
||||||
|
class BasePage(Numbered, models.Model):
|
||||||
'''Abstract base model for pages'''
|
'''Abstract base model for pages'''
|
||||||
slug = models.SlugField(_('slug'), help_text=_('A short identifier to use in URLs'), blank=True, unique=True)
|
number = models.PositiveIntegerField(_('number'), blank=True)
|
||||||
|
slug = models.SlugField(_('slug'), blank=True, unique=True)
|
||||||
title = VarCharField(_('title'))
|
title = VarCharField(_('title'))
|
||||||
position = models.PositiveIntegerField(_('position'), blank=True)
|
|
||||||
menu = models.BooleanField(_('visible in menu'), default=True)
|
menu = models.BooleanField(_('visible in menu'), default=True)
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
|
@ -44,15 +79,16 @@ class BasePage(NumberedModel):
|
||||||
abstract = True
|
abstract = True
|
||||||
verbose_name = _('Page')
|
verbose_name = _('Page')
|
||||||
verbose_name_plural = _('Pages')
|
verbose_name_plural = _('Pages')
|
||||||
ordering = ['position']
|
ordering = ['number']
|
||||||
|
|
||||||
class BaseSection(NumberedModel, PolymorphicModel):
|
class BaseSection(Numbered, PolymorphicModel):
|
||||||
'''Abstract base model for sections'''
|
'''Abstract base model for sections'''
|
||||||
TYPES = []
|
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 = VarCharChoiceField(_('type'), default='', choices=TYPES)
|
type = VarCharField(_('type'), blank=True)
|
||||||
|
number = models.PositiveIntegerField(_('number'), blank=True)
|
||||||
|
|
||||||
title = VarCharField(_('title'), blank=True)
|
title = VarCharField(_('title'), blank=True)
|
||||||
position = models.PositiveIntegerField(_('position'), 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'))
|
||||||
|
@ -74,7 +110,7 @@ class BaseSection(NumberedModel, PolymorphicModel):
|
||||||
abstract = True
|
abstract = True
|
||||||
verbose_name = _('section')
|
verbose_name = _('section')
|
||||||
verbose_name_plural = _('sections')
|
verbose_name_plural = _('sections')
|
||||||
ordering = ['position']
|
ordering = ['number']
|
||||||
|
|
||||||
class Page(BasePage):
|
class Page(BasePage):
|
||||||
'''Swappable page model'''
|
'''Swappable page model'''
|
||||||
|
|
|
@ -289,22 +289,14 @@ form.cms {
|
||||||
clear: both;
|
clear: both;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
|
|
||||||
&#type, &#position, &#title, &#slug {
|
&#type, &#number {
|
||||||
padding: 0 1em;
|
width: 75%;
|
||||||
clear: none;
|
clear: none;
|
||||||
float: left;
|
float: left;
|
||||||
display: inline-block;
|
|
||||||
width: 33%;
|
|
||||||
}
|
}
|
||||||
&#type, &#slug {
|
&#number {
|
||||||
padding-left: 0;
|
width: 20%;
|
||||||
}
|
|
||||||
&#title {
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
&#position {
|
|
||||||
float: right;
|
float: right;
|
||||||
padding-right: 0;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
div.formfield.error {
|
div.formfield.error {
|
||||||
|
|
|
@ -206,19 +206,13 @@ form.cms div.formfield {
|
||||||
padding: 10px 0;
|
padding: 10px 0;
|
||||||
clear: both;
|
clear: both;
|
||||||
box-sizing: border-box; }
|
box-sizing: border-box; }
|
||||||
form.cms div.formfield#type, form.cms div.formfield#position, form.cms div.formfield#title, form.cms div.formfield#slug {
|
form.cms div.formfield#type, form.cms div.formfield#number {
|
||||||
padding: 0 1em;
|
width: 75%;
|
||||||
clear: none;
|
clear: none;
|
||||||
float: left;
|
float: left; }
|
||||||
display: inline-block;
|
form.cms div.formfield#number {
|
||||||
width: 33%; }
|
width: 20%;
|
||||||
form.cms div.formfield#type, form.cms div.formfield#slug {
|
float: right; }
|
||||||
padding-left: 0; }
|
|
||||||
form.cms div.formfield#title {
|
|
||||||
padding: 0; }
|
|
||||||
form.cms div.formfield#position {
|
|
||||||
float: right;
|
|
||||||
padding-right: 0; }
|
|
||||||
|
|
||||||
form.cms div.formfield.error {
|
form.cms div.formfield.error {
|
||||||
border: 2px dotted red;
|
border: 2px dotted red;
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -93,6 +93,7 @@
|
||||||
|
|
||||||
for (field of fields_per_type[type]) {
|
for (field of fields_per_type[type]) {
|
||||||
el = document.getElementById(field);
|
el = document.getElementById(field);
|
||||||
|
console.log('showing ' + field)
|
||||||
el.style.display = 'block';
|
el.style.display = 'block';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
10
cms/views.py
10
cms/views.py
|
@ -1,11 +1,9 @@
|
||||||
'''CMS Views'''
|
|
||||||
|
|
||||||
import json
|
import json
|
||||||
import swapper
|
import swapper
|
||||||
|
|
||||||
from django.views import generic
|
from django.views import generic
|
||||||
from django.views.generic.edit import FormMixin
|
|
||||||
from django.shortcuts import redirect
|
from django.shortcuts import redirect
|
||||||
|
from django.views.generic.edit import FormMixin
|
||||||
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
|
||||||
from django.contrib.auth.mixins import UserPassesTestMixin
|
from django.contrib.auth.mixins import UserPassesTestMixin
|
||||||
|
@ -126,10 +124,10 @@ class TypeMixin(MenuMixin):
|
||||||
fields_per_type = {}
|
fields_per_type = {}
|
||||||
for model, _ in Section.TYPES:
|
for model, _ in Section.TYPES:
|
||||||
ctype = ContentType.objects.get(
|
ctype = ContentType.objects.get(
|
||||||
app_label=Section._meta.app_label,
|
app_label = Section._meta.app_label,
|
||||||
model=model.lower(),
|
model = model.lower(),
|
||||||
)
|
)
|
||||||
fields_per_type[ctype.model] = ctype.model_class().fields
|
fields_per_type[ctype.model] = ['type', 'number'] + ctype.model_class().fields
|
||||||
|
|
||||||
context.update({
|
context.update({
|
||||||
'fields_per_type': json.dumps(fields_per_type),
|
'fields_per_type': json.dumps(fields_per_type),
|
||||||
|
|
|
@ -18,30 +18,30 @@ class Section(BaseSection):
|
||||||
|
|
||||||
@register_model('Tekst')
|
@register_model('Tekst')
|
||||||
class TextSection(Section):
|
class TextSection(Section):
|
||||||
fields = ['type', 'position', 'title', 'color', 'content']
|
fields = ['title', 'content']
|
||||||
class Meta:
|
class Meta:
|
||||||
proxy = True
|
proxy = True
|
||||||
|
|
||||||
@register_model('Button')
|
@register_model('Button')
|
||||||
class ButtonSection(Section):
|
class ButtonSection(Section):
|
||||||
fields = ['type', 'position', 'title', 'button_text', 'button_link']
|
fields = ['button_text', 'button_link']
|
||||||
class Meta:
|
class Meta:
|
||||||
proxy = True
|
proxy = True
|
||||||
|
|
||||||
@register_model('Afbeelding')
|
@register_model('Afbeelding')
|
||||||
class ImageSection(Section):
|
class ImageSection(Section):
|
||||||
fields = ['type', 'position', 'title', 'image']
|
fields = ['title', 'image']
|
||||||
class Meta:
|
class Meta:
|
||||||
proxy = True
|
proxy = True
|
||||||
|
|
||||||
@register_model('Video')
|
@register_model('Video')
|
||||||
class VideoSection(Section):
|
class VideoSection(Section):
|
||||||
fields = ['type', 'position', 'title', 'video']
|
fields = ['title', 'video']
|
||||||
class Meta:
|
class Meta:
|
||||||
proxy = True
|
proxy = True
|
||||||
|
|
||||||
@register_model('Contact')
|
@register_model('Contact')
|
||||||
class ContactSection(Section):
|
class ContactSection(Section):
|
||||||
fields = ['type', 'position', 'title']
|
fields = ['title']
|
||||||
class Meta:
|
class Meta:
|
||||||
proxy = True
|
proxy = True
|
|
@ -38,7 +38,8 @@ except IOError:
|
||||||
write(KEYFILE, SECRET_KEY)
|
write(KEYFILE, SECRET_KEY)
|
||||||
|
|
||||||
SECTION_COLORS = [
|
SECTION_COLORS = [
|
||||||
(1, 'Wit'),
|
(1, 'Licht'),
|
||||||
|
(2, 'Donker'),
|
||||||
]
|
]
|
||||||
|
|
||||||
INSTALLED_APPS = [
|
INSTALLED_APPS = [
|
||||||
|
@ -50,7 +51,6 @@ INSTALLED_APPS = [
|
||||||
'django.contrib.messages',
|
'django.contrib.messages',
|
||||||
'django.contrib.staticfiles',
|
'django.contrib.staticfiles',
|
||||||
'cms',
|
'cms',
|
||||||
'simplesass',
|
|
||||||
'polymorphic',
|
'polymorphic',
|
||||||
'embed_video',
|
'embed_video',
|
||||||
'easy_thumbnails',
|
'easy_thumbnails',
|
||||||
|
@ -58,7 +58,7 @@ INSTALLED_APPS = [
|
||||||
]
|
]
|
||||||
|
|
||||||
MIDDLEWARE = [
|
MIDDLEWARE = [
|
||||||
'simplesass.middleware.SimpleSassMiddleware',
|
'cms.middleware.SassMiddleware',
|
||||||
'django.middleware.security.SecurityMiddleware',
|
'django.middleware.security.SecurityMiddleware',
|
||||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||||
'django.middleware.common.CommonMiddleware',
|
'django.middleware.common.CommonMiddleware',
|
|
@ -1,114 +0,0 @@
|
||||||
# Generated by Django 3.0.2 on 2020-01-05 02:29
|
|
||||||
|
|
||||||
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 = [
|
|
||||||
('contenttypes', '0002_remove_content_type_name'),
|
|
||||||
migrations.swappable_dependency(settings.CMS_PAGE_MODEL),
|
|
||||||
]
|
|
||||||
|
|
||||||
operations = [
|
|
||||||
migrations.CreateModel(
|
|
||||||
name='Page',
|
|
||||||
fields=[
|
|
||||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
|
||||||
('slug', models.SlugField(blank=True, help_text='A short identifier to use in URLs', unique=True, verbose_name='slug')),
|
|
||||||
('title', cms.models.VarCharField(verbose_name='title')),
|
|
||||||
('position', models.PositiveIntegerField(blank=True, verbose_name='position')),
|
|
||||||
('menu', models.BooleanField(default=True, verbose_name='visible in menu')),
|
|
||||||
],
|
|
||||||
options={
|
|
||||||
'verbose_name': 'Page',
|
|
||||||
'verbose_name_plural': 'Pages',
|
|
||||||
'ordering': ['position'],
|
|
||||||
'abstract': False,
|
|
||||||
},
|
|
||||||
),
|
|
||||||
migrations.CreateModel(
|
|
||||||
name='Section',
|
|
||||||
fields=[
|
|
||||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
|
||||||
('type', cms.models.VarCharChoiceField(choices=[('textsection', 'Tekst'), ('buttonsection', 'Button'), ('imagesection', 'Afbeelding'), ('videosection', 'Video'), ('contactsection', 'Contact')], default='', verbose_name='type')),
|
|
||||||
('title', cms.models.VarCharField(blank=True, verbose_name='title')),
|
|
||||||
('position', models.PositiveIntegerField(blank=True, verbose_name='position')),
|
|
||||||
('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')),
|
|
||||||
('button_text', cms.models.VarCharField(blank=True, verbose_name='button text')),
|
|
||||||
('button_link', cms.models.VarCharField(blank=True, verbose_name='button link')),
|
|
||||||
('color', models.PositiveIntegerField(choices=[(1, 'Wit')], default=1, verbose_name='kleur')),
|
|
||||||
('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_app.section_set+', to='contenttypes.ContentType')),
|
|
||||||
],
|
|
||||||
options={
|
|
||||||
'verbose_name': 'section',
|
|
||||||
'verbose_name_plural': 'sections',
|
|
||||||
'ordering': ['position'],
|
|
||||||
'abstract': False,
|
|
||||||
},
|
|
||||||
),
|
|
||||||
migrations.CreateModel(
|
|
||||||
name='ButtonSection',
|
|
||||||
fields=[
|
|
||||||
],
|
|
||||||
options={
|
|
||||||
'proxy': True,
|
|
||||||
'indexes': [],
|
|
||||||
'constraints': [],
|
|
||||||
},
|
|
||||||
bases=('app.section',),
|
|
||||||
),
|
|
||||||
migrations.CreateModel(
|
|
||||||
name='ContactSection',
|
|
||||||
fields=[
|
|
||||||
],
|
|
||||||
options={
|
|
||||||
'proxy': True,
|
|
||||||
'indexes': [],
|
|
||||||
'constraints': [],
|
|
||||||
},
|
|
||||||
bases=('app.section',),
|
|
||||||
),
|
|
||||||
migrations.CreateModel(
|
|
||||||
name='ImageSection',
|
|
||||||
fields=[
|
|
||||||
],
|
|
||||||
options={
|
|
||||||
'proxy': True,
|
|
||||||
'indexes': [],
|
|
||||||
'constraints': [],
|
|
||||||
},
|
|
||||||
bases=('app.section',),
|
|
||||||
),
|
|
||||||
migrations.CreateModel(
|
|
||||||
name='TextSection',
|
|
||||||
fields=[
|
|
||||||
],
|
|
||||||
options={
|
|
||||||
'proxy': True,
|
|
||||||
'indexes': [],
|
|
||||||
'constraints': [],
|
|
||||||
},
|
|
||||||
bases=('app.section',),
|
|
||||||
),
|
|
||||||
migrations.CreateModel(
|
|
||||||
name='VideoSection',
|
|
||||||
fields=[
|
|
||||||
],
|
|
||||||
options={
|
|
||||||
'proxy': True,
|
|
||||||
'indexes': [],
|
|
||||||
'constraints': [],
|
|
||||||
},
|
|
||||||
bases=('app.section',),
|
|
||||||
),
|
|
||||||
]
|
|
|
@ -1 +0,0 @@
|
||||||
default_app_config = 'numberedmodel.apps.NumberedModelConfig'
|
|
|
@ -1,4 +0,0 @@
|
||||||
from django.apps import AppConfig
|
|
||||||
|
|
||||||
class NumberedModelConfig(AppConfig):
|
|
||||||
name = 'numberedmodel'
|
|
|
@ -1,44 +0,0 @@
|
||||||
from django.db import models
|
|
||||||
|
|
||||||
class NumberedModel(models.Model):
|
|
||||||
def number_with_respect_to(self):
|
|
||||||
return self.__class__.objects.all()
|
|
||||||
|
|
||||||
def _renumber(self):
|
|
||||||
'''Renumbers the queryset while preserving the instance's number'''
|
|
||||||
|
|
||||||
queryset = self.number_with_respect_to()
|
|
||||||
field_name = self.__class__._meta.ordering[-1].lstrip('-')
|
|
||||||
this_nr = getattr(self, field_name)
|
|
||||||
if this_nr is None:
|
|
||||||
this_nr = len(queryset) + 1
|
|
||||||
|
|
||||||
# The algorithm: loop over the queryset and set each object's
|
|
||||||
# number to the counter. When an object's number equals the
|
|
||||||
# number of this instance, set this instance's number to the
|
|
||||||
# counter, increment the counter by 1, and finish the loop
|
|
||||||
counter = 1
|
|
||||||
inserted = False
|
|
||||||
for other in queryset.exclude(pk=self.pk):
|
|
||||||
other_nr = getattr(other, field_name)
|
|
||||||
if counter >= this_nr and not inserted:
|
|
||||||
setattr(self, field_name, counter)
|
|
||||||
inserted = True
|
|
||||||
counter += 1
|
|
||||||
if other_nr != counter:
|
|
||||||
setattr(other, field_name, counter)
|
|
||||||
super(NumberedModel, other).save()
|
|
||||||
counter += 1
|
|
||||||
if not inserted:
|
|
||||||
setattr(self, field_name, counter)
|
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
|
||||||
self._renumber()
|
|
||||||
super(NumberedModel, self).save(*args, **kwargs)
|
|
||||||
|
|
||||||
def delete(self, *args, **kwargs):
|
|
||||||
super(NumberedModel, self).delete(*args, **kwargs)
|
|
||||||
self._renumber()
|
|
||||||
|
|
||||||
class Meta:
|
|
||||||
abstract = True
|
|
|
@ -1,3 +0,0 @@
|
||||||
from django.test import TestCase
|
|
||||||
|
|
||||||
# Create your tests here.
|
|
Ładowanie…
Reference in New Issue