turn CellBase into an abstract class

This removes the django-model-utils dependency and will allow for a very
simple serialisation of objects.
This commit is contained in:
Frédéric Péters 2014-12-24 07:34:52 +01:00
parent 6cedb0e9d2
commit 5d53ad725d
11 changed files with 102 additions and 35 deletions

41
combo/data/library.py Normal file
View File

@ -0,0 +1,41 @@
# combo - content management system
# Copyright (C) 2014 Entr'ouvert
#
# This program is free software: you can redistribute it and/or modify it
# under the terms of the GNU Affero General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from django.contrib.contenttypes.models import ContentType
class Library(object):
"""Singleton object that serves as a registry of the classes
providing page cells."""
def __init__(self):
self.classes = {}
def get_cell_classes(self):
return self.classes.values()
def get_cell_class(self, content_type_id):
return self.classes[int(content_type_id)]
def register_cell_class(self, klass):
content_type = ContentType.objects.get_for_model(klass)
self.classes[content_type.id] = klass
return klass
library = Library() # singleton object
register_cell_class = library.register_cell_class
get_cell_classes = library.get_cell_classes
get_cell_class = library.get_cell_class

View File

@ -18,6 +18,7 @@ import inspect
from django.conf import settings
from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import ObjectDoesNotExist
from django.db import models
from django.db.models import Max
from django.forms import models as model_forms
@ -27,9 +28,10 @@ from django.utils.safestring import mark_safe
from django.utils.translation import ugettext_lazy as _
from ckeditor.fields import RichTextField
from model_utils.managers import InheritanceManager
import cmsplugin_blurp.utils
from .library import register_cell_class, get_cell_classes, get_cell_class
class Page(models.Model):
title = models.CharField(_('Title'), max_length=50)
@ -83,10 +85,11 @@ class CellBase(models.Model):
placeholder = models.CharField(max_length=20)
order = models.PositiveIntegerField()
objects = InheritanceManager()
default_form_class = None
class Meta:
abstract = True
@classmethod
def get_verbose_name(cls):
return cls._meta.verbose_name
@ -94,11 +97,33 @@ class CellBase(models.Model):
@classmethod
def get_cell_content_types(cls):
content_types = []
for symbol in globals().values():
if inspect.isclass(symbol) and issubclass(symbol, cls) and symbol is not cls:
content_types.extend(symbol.get_content_types())
for klass in get_cell_classes():
content_types.extend(klass.get_content_types())
return content_types
@classmethod
def get_cells(cls, **kwargs):
"""Returns the list of cells of various classes matching **kwargs"""
cells = []
for klass in get_cell_classes():
cells.extend(klass.objects.filter(**kwargs))
cells.sort(lambda x, y: cmp(x.order, y.order))
return cells
def get_reference(self):
"Returns a string that can serve as a unique reference to a cell"""
return '%s-%s' % (ContentType.objects.get_for_model(self).id, self.id)
@classmethod
def get_cell(cls, reference, **kwargs):
"""Returns the cell matching reference, and eventual **kwargs"""
content_id, cell_id = reference.split('-')
try:
klass = get_cell_class(content_id)
except KeyError:
raise ObjectDoesNotExist()
return klass.objects.get(id=cell_id, **kwargs)
@classmethod
def get_content_types(cls):
return [{
@ -117,13 +142,15 @@ class CellBase(models.Model):
if self.default_form_class:
return self.default_form_class
fields = [x.name for x in self._meta.local_concrete_fields if x.name != 'cellbase_ptr']
fields = [x.name for x in self._meta.local_concrete_fields
if x.name not in ('id', 'page', 'placeholder', 'order')]
if not fields:
return None
return model_forms.modelform_factory(self.__class__, fields=fields)
@register_cell_class
class TextCell(CellBase):
text = RichTextField(_('Text'), null=True)
@ -134,6 +161,7 @@ class TextCell(CellBase):
return mark_safe(self.text or '')
@register_cell_class
class FortuneCell(CellBase):
class Meta:
verbose_name = _('Fortune')
@ -143,6 +171,7 @@ class FortuneCell(CellBase):
return subprocess.check_output(['fortune'])
@register_cell_class
class BlurpCell(CellBase):
blurp_key = models.CharField(max_length=50)

View File

@ -64,9 +64,10 @@ $(function() {
update: function(event, ui) {
var new_order = Object();
$('.cell').each(function(i, x) {
new_order['pos_' + $(x).data('cell-id')] = i;
var cell_suffix = $(x).data('cell-reference');
new_order['pos_' + cell_suffix] = i;
new_placeholder = $(x).closest('.placeholder').data('placeholder-key');
new_order['ph_' + $(x).data('cell-id')] = new_placeholder;
new_order['ph_' + cell_suffix] = new_placeholder;
});
$.ajax({
url: $('#placeholders').data('cell-order-url'),

View File

@ -6,7 +6,7 @@
<p>There are no options for this cell</p>
{% endif %}
<div class="buttons">
<a rel="popup" href="{% url 'combo-manager-page-delete-cell' page_pk=page.id cell_pk=cell.id %}">Delete</a> | <a class="close-button" href="#">Close</a>
<a rel="popup" href="{% url 'combo-manager-page-delete-cell' page_pk=page.id cell_reference=cell.get_reference %}">Delete</a> | <a class="close-button" href="#">Close</a>
{% if form %}
<button class="save">Save</button>
{% endif %}

View File

@ -43,7 +43,7 @@
<h2>{{ placeholder.name }}</h2>
<div class="cell-list">
{% for cell in placeholder.cells %}
<div class="cell" data-cell-id="{{ cell.id }}">
<div class="cell" data-cell-reference="{{ cell.get_reference }}">
<h3>{{ cell.get_label }}</h3>
<div>{% cell_form cell %}</div>
</div>

View File

@ -23,10 +23,10 @@ register = template.Library()
@register.simple_tag(takes_context=True)
def cell_form(context, cell):
context['url'] = reverse('combo-manager-page-edit-cell', kwargs={
'page_pk': cell.page.id, 'cell_pk': cell.id})
'page_pk': cell.page.id, 'cell_reference': cell.get_reference()})
form_class = cell.get_default_form_class()
if form_class:
context['form'] = form_class(instance=cell, prefix='c%s' % cell.id)
context['form'] = form_class(instance=cell, prefix='c%s' % cell.get_reference())
else:
context['form'] = None
cell_form_template = template.loader.get_template('combo/cell_form.html')

View File

@ -30,9 +30,9 @@ urlpatterns = patterns('combo.views',
url(r'^pages/(?P<page_pk>\w+)/add-cell-to-(?P<ph_key>\w+)/(?P<cell_type>\w+)/(?P<variant>[\w-]+)/$',
views.page_add_cell,
name='combo-manager-page-add-cell'),
url(r'^pages/(?P<page_pk>\w+)/cell/(?P<cell_pk>\w+)/$', views.page_edit_cell,
url(r'^pages/(?P<page_pk>\w+)/cell/(?P<cell_reference>[\w-]+)/$', views.page_edit_cell,
name='combo-manager-page-edit-cell'),
url(r'^pages/(?P<page_pk>\w+)/cell/(?P<cell_pk>\w+)/delete$', views.page_delete_cell,
url(r'^pages/(?P<page_pk>\w+)/cell/(?P<cell_reference>[\w-]+)/delete$', views.page_delete_cell,
name='combo-manager-page-delete-cell'),
url(r'^pages/(?P<page_pk>\w+)/order$', views.cell_order,
name='combo-manager-cell-order'),

View File

@ -16,6 +16,7 @@
from django.conf import settings
from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import ObjectDoesNotExist
from django.core.urlresolvers import reverse
from django.forms import models as model_forms
from django.http import HttpResponse, Http404
@ -77,8 +78,7 @@ class PageView(DetailView):
def get_context_data(self, **kwargs):
context = super(DetailView, self).get_context_data(**kwargs)
context['cell_types'] = CellBase.get_cell_content_types()
cells = CellBase.objects.filter(page_id=self.object.id
).order_by('order').select_subclasses()
cells = CellBase.get_cells(page_id=self.object.id)
template = self.object.template_name
placeholders = []
combo_template = settings.COMBO_PUBLIC_TEMPLATES.get(template)
@ -99,7 +99,7 @@ class PageAddCellView(RedirectView):
cell_class = ContentType.objects.get(id=cell_type).model_class()
cell = cell_class(page_id=page_pk, placeholder=ph_key)
cell.set_variant(variant)
orders = [x.order for x in CellBase.objects.filter(page_id=page_pk)]
orders = [x.order for x in CellBase.get_cells(page_id=page_pk)]
if orders:
cell.order = max(orders)+1
else:
@ -113,19 +113,17 @@ page_add_cell = PageAddCellView.as_view()
class PageEditCellView(UpdateView):
def get_object(self, queryset=None):
page_pk = self.kwargs.get('page_pk')
cell_pk = self.kwargs.get('cell_pk')
cell_reference = self.kwargs.get('cell_reference')
try:
return CellBase.objects.get_subclass(id=cell_pk, page_id=page_pk)
except CellBase.DoesNotExist:
return CellBase.get_cell(cell_reference, page_id=page_pk)
except ObjectDoesNotExist:
raise Http404()
def get_prefix(self):
return 'c%s' % self.kwargs.get('cell_pk')
return 'c%s' % self.kwargs.get('cell_reference')
def get_form_class(self):
cell = self.object
fields = [x.name for x in cell._meta.local_concrete_fields if x.name != 'cellbase_ptr']
return model_forms.modelform_factory(cell.__class__, fields=fields)
return self.object.get_default_form_class()
def get_success_url(self):
return reverse('combo-manager-page-view', kwargs={'pk': self.kwargs.get('page_pk')})
@ -138,10 +136,10 @@ class PageDeleteCellView(DeleteView):
def get_object(self, queryset=None):
page_pk = self.kwargs.get('page_pk')
cell_pk = self.kwargs.get('cell_pk')
cell_reference = self.kwargs.get('cell_reference')
try:
return CellBase.objects.get_subclass(id=cell_pk, page_id=page_pk)
except CellBase.DoesNotExist:
return CellBase.get_cell(cell_reference, page_id=page_pk)
except ObjectDoesNotExist:
raise Http404()
def get_success_url(self):
@ -151,11 +149,12 @@ page_delete_cell = PageDeleteCellView.as_view()
def cell_order(request, page_pk):
for cell in CellBase.objects.filter(page_id=page_pk):
for cell in CellBase.get_cells(page_id=page_pk):
old_order = cell.order
old_placeholder = cell.placeholder
new_order = int(request.GET.get('pos_' + str(cell.id)))
new_placeholder = request.GET.get('ph_' + str(cell.id))
key_suffix = cell.get_reference()
new_order = int(request.GET.get('pos_' + key_suffix))
new_placeholder = request.GET.get('ph_' + key_suffix)
if new_order != old_order or new_placeholder != old_placeholder:
cell.order = new_order
cell.placeholder = new_placeholder

View File

@ -32,8 +32,7 @@ def page(request):
page = pages[-1]
ctx = {
'page': page,
'page_cells': CellBase.objects.filter(page_id=page.id
).order_by('order').select_subclasses(),
'page_cells': CellBase.get_cells(page_id=page.id),
'request': request,
}
template_name = settings.COMBO_PUBLIC_TEMPLATES[page.template_name]['template']

View File

@ -1,5 +1,4 @@
Django==1.6
django-ckeditor
-e git+http://repos.entrouvert.org/gadjo.git/#egg=gadjo
django-model-utils
django-cmsplugin-blurp

View File

@ -60,7 +60,6 @@ setup(
install_requires=['django == 1.6',
'django-ckeditor',
'gadjo',
'django-model-utils',
'django-cmsplugin-blurp',
],
zip_safe=False,