django-simplecms/cms/models.py

124 wiersze
4.2 KiB
Python

import swapper
from django.db import models
from django.urls import reverse
from django.forms import TextInput, Select
from django.utils.translation import gettext_lazy as _
from embed_video.fields import EmbedVideoField
from polymorphic.models import PolymorphicModel
class VarCharField(models.TextField):
'''Variable width CharField'''
def formfield(self, **kwargs):
kwargs.update({'widget': TextInput})
return super().formfield(**kwargs)
class Numbered:
'''Mixin for numbered models. Overrides the save() method to
automatically renumber all instances returned by
number_with_respect_to()
'''
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(Numbered, 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'''
number = models.PositiveIntegerField(_('number'), blank=True)
title = VarCharField(_('title'))
slug = models.SlugField(_('slug'), blank=True, unique=True)
menu = models.BooleanField(_('visible in menu'), default=True)
def __str__(self):
if not self.pk:
return str(_('New page'))
return self.title
def get_absolute_url(self):
if self.slug:
return reverse('cms:page', args=[self.slug])
return reverse('cms:page')
class Meta:
abstract = True
verbose_name = _('Page')
verbose_name_plural = _('Pages')
ordering = ['number']
class BaseSection(Numbered, PolymorphicModel):
'''Abstract base model for sections'''
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)
type = VarCharField(_('type'), blank=True)
number = models.PositiveIntegerField(_('number'), blank=True)
title = VarCharField(_('title'), blank=True)
content = models.TextField(_('content'), blank=True)
image = models.ImageField(_('image'), blank=True)
video = EmbedVideoField(_('video'), blank=True, help_text=_('Paste a YouTube, Vimeo, or SoundCloud link'))
button_text = VarCharField(_('button text'), blank=True)
button_link = VarCharField(_('button link'), blank=True)
def number_with_respect_to(self):
return self.page.sections.all()
def __str__(self):
if not self.pk:
return str(_('New section'))
elif not self.title:
return str(_('Untitled'))
else:
return self.title
class Meta:
abstract = True
verbose_name = _('section')
verbose_name_plural = _('sections')
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')