class PublishableFluentContents(PublishingModel): contentitem_set = ContentItemRelation() placeholder_set = PlaceholderRelation() class Meta: abstract = True def placeholders(self): # return a dict of placeholders, organised by slot, for access in # templates use `page.placeholders.<slot_name>.get_content_items` to # test if a placeholder has any items. return dict([(p.slot, p) for p in self.placeholder_set.all().select_related()])
class PlaceholderFieldTestPage(models.Model): """ A model with PlaceholderField, for testing, """ title = models.CharField(max_length=200) contents = PlaceholderField("field_slot1") placeholder_set = PlaceholderRelation() contentitem_set = ContentItemRelation() class Meta: verbose_name = "Test page" verbose_name_plural = "Test pages" def __str__(self): return self.title
class FluentFieldsMixin(LayoutFieldMixin): """ Add ``layout``, ``contentitem_set`` and ``placeholder_set`` fields so we can add modular content with ``django-fluent-contents``. """ contentitem_set = ContentItemRelation() placeholder_set = PlaceholderRelation() class Meta: abstract = True def placeholders(self): # return a dict of placeholders, organised by slot, for access in # templates use `page.placeholders.<slot_name>.get_content_items` to # test if a placeholder has any items. return dict([(p.slot, p) for p in self.placeholder_set.all().select_related()])
class FluentFieldsMixin(LayoutFieldMixin): """ Add ``layout``, ``contentitem_set`` and ``placeholder_set`` fields so we can add modular content with ``django-fluent-contents``. """ contentitem_set = ContentItemRelation() placeholder_set = PlaceholderRelation() class Meta: abstract = True # HACK: This is needed to work-around a `django-fluent-contents` issue # where it cannot handle placeholders being added to a template after an # object already has placeholder data in the database. # See: https://github.com/edoburu/django-fluent-contents/pull/63 def add_missing_placeholders(self): """ Add missing placeholders from templates. Return `True` if any missing placeholders were created. """ content_type = ContentType.objects.get_for_model(self) result = False if self.layout: for data in self.layout.get_placeholder_data(): placeholder, created = Placeholder.objects.update_or_create( parent_type=content_type, parent_id=self.pk, slot=data.slot, defaults=dict( role=data.role, title=data.title, )) result = result or created return result def placeholders(self): # return a dict of placeholders, organised by slot, for access in templates # use `page.placeholders.<slot_name>.get_content_items` to test if # a placeholder has any items. return dict([(p.slot, p) for p in self.placeholder_set.all().select_related()])
class Page(MPTTModel): title = models.CharField("Title", max_length=200) # The basic fields to make the url structure work: slug = models.SlugField("Slug") parent = models.ForeignKey('self', related_name='children', blank=True, null=True, on_delete=models.CASCADE) _cached_url = models.CharField(max_length=300, blank=True, editable=False, default='', db_index=True) # Allow different layouts template_name = models.CharField( "Layout", max_length=255, choices=appconfig.SIMPLECMS_TEMPLATE_CHOICES, default=appconfig.SIMPLECMS_DEFAULT_TEMPLATE) # Accessing the data of django-fluent-contents placeholder_set = PlaceholderRelation() contentitem_set = ContentItemRelation() class Meta: verbose_name = "Page" verbose_name_plural = "Pages" def __str__(self): return self.title def get_absolute_url(self): # cached_url always points to the URL within the URL config root. # when the application is mounted at a subfolder, or the 'cms.urls' config # is included at a sublevel, it needs to be prepended. root = reverse('simplecms-page').rstrip('/') return root + self._cached_url # ---- updating _cached_url: # This block of code is largely inspired and based on FeinCMS # (c) Matthias Kestenholz, BSD licensed def __init__(self, *args, **kwargs): super(Page, self).__init__(*args, **kwargs) self._original_cached_url = self._cached_url # This code runs in a transaction since it's potentially editing a lot of records (all decendant urls). @transaction.atomic def save(self, *args, **kwargs): """ Save the model, and update caches. """ # Store this object self._make_slug_unique() self._update_cached_url() super(Page, self).save(*args, **kwargs) # Update others self._update_decendant_urls() return super(Page, self).save(*args, **kwargs) # Following of the principles for "clean code" # the save() method is split in the 3 methods below, # each "do one thing, and only one thing". def _make_slug_unique(self): """ Check for duplicate slugs at the same level, and make the current object unique. """ origslug = self.slug dupnr = 1 while True: others = Page.objects.filter(parent=self.parent, slug=self.slug) if self.pk: others = others.exclude(pk=self.pk) if not others.count(): break dupnr += 1 self.slug = "%s-%d" % (origslug, dupnr) def _update_cached_url(self): """ Update the URLs """ # determine own URL if self.is_root_node(): self._cached_url = u'/%s/' % self.slug else: self._cached_url = u'%s%s/' % (self.parent._cached_url, self.slug) def _update_decendant_urls(self): """ Update the URLs of all decendant pages. """ # Performance optimisation: avoid traversing and updating many records # when nothing changed in the URL. if self._cached_url == self._original_cached_url: return # Keep cache cached_page_urls = {self.id: self._cached_url} # Update all sub objects subobjects = self.get_descendants().order_by('lft') for subobject in subobjects: # Set URL, using cache for parent URL. subobject._cached_url = u'%s%s/' % ( cached_page_urls[subobject.parent_id], subobject.slug) cached_page_urls[subobject.id] = subobject._cached_url # call base class, do not recurse super(Page, subobject).save()