from itertools import chain from django.contrib.contenttypes.fields import GenericRelation from django.db import models from cms.forms.fields import PageSelectFormField from cms.models.placeholdermodel import Placeholder class PageField(models.ForeignKey): """ This is a foreign key field to the :class:`cms.models.pagemodel.Page` model that defaults to the :class:`~cms.forms.fields.PageSelectFormField` form field when rendered in forms. It has the same API as the :class:`django:django.db.models.ForeignKey` but does not require the ``othermodel`` argument. """ default_form_class = PageSelectFormField def __init__(self, **kwargs): # We hard-code the `to` argument for ForeignKey.__init__ # since a PageField can only be a ForeignKey to a Page kwargs['to'] = 'cms.Page' kwargs['on_delete'] = models.CASCADE super().__init__(**kwargs) def formfield(self, **kwargs): defaults = { 'form_class': self.default_form_class, } defaults.update(kwargs) return super().formfield(**defaults) class PlaceholderField(models.ForeignKey): """ .. warning:: This field is for django CMS versions below 4 only. It may only be used inside migrations. The ``PlaceholderField`` has been replaced by the :class:`~cms.models.fields.PlaceholderRelationField`, the built-in migrations will automatically take care of the replacement. See documentation of :class:`~cms.models.fields.PlaceholderRelationField` for how to replace the code. """ system_check_removed_details = { "msg": ( "PlaceholderField is for django CMS versions below 4 only. It may only be used inside migrations." ), "hint": "Use PlaceholderRelationField instead.", "id": "cms.E001", } def __init__(self, slotname, *args, **kwargs): kwargs.update({'null': True}) # always allow Null kwargs.update({'editable': False}) # never allow edits in admin # We hard-code the `to` argument for ForeignKey.__init__ # since a PlaceholderField can only be a ForeignKey to a Placeholder self.slotname = slotname kwargs['to'] = 'cms.Placeholder' kwargs['on_delete'] = models.CASCADE super().__init__(**kwargs) def deconstruct(self): name, path, args, kwargs = super().deconstruct() kwargs['slotname'] = self.slotname return name, path, args, kwargs class PlaceholderRelationField(GenericRelation): """:class:`~django.contrib.contenttypes.fields.GenericForeignKey` to placeholders. If you create a model which contains placeholders you first create the ``PlaceHolderRelationField``:: from cms.utils.placeholder import get_placeholder_from_slot class Post(models.Model): ... placeholders = PlaceholderRelationField() # Generic relation @cached_property def content(self): return get_placeholder_from_slot(self.placeholders, "content") # A specific placeholder """ default_checks = [] def __init__(self, checks=None, **kwargs): self._checks = checks or () kwargs.pop('object_id_field', None) kwargs.pop('content_type_field', None) super().__init__( Placeholder, object_id_field='object_id', content_type_field='content_type', **kwargs ) @property def checks(self): return chain(self.default_checks, self._checks) def run_checks(self, placeholder, user): return all(check_(placeholder, user) for check_ in self.checks)