Стоит задача сделать меню сайта максимально гибко и просто настраиваемым. В идеале - максимально приблизиться к управлению меню в Drupal, где ноды можно цеплять в меню сразу из интерфейса их редактирования.
Для этого сделан базовый класс Page, от которого наследуются все закрепляемые в меню страницы
class Page(models.Model):
title = models.CharField(verbose_name="название", max_length=50)
text = RichTextField(verbose_name="содержимое страницы")
slug = AutoSlugField(populate_from='title', editable=True)
objects = InheritanceManager()
def __str__(self):
return self.title
def get_absolute_url(self):
from django.core.urlresolvers import reverse
return reverse('app.views.page_details', args=[str(self.slug)])
class Meta:
verbose_name = "страница"
verbose_name_plural = "страницы"
само меню состоит из элементов MenuItem, от которого могут быть наследованы более специфичные, например PageMenuItem
class MenuItem(models.Model):
title = models.CharField(verbose_name="название", max_length=50)
description = models.CharField(verbose_name="описание", max_length=100, null=True, blank=True)
weight = models.PositiveSmallIntegerField(verbose_name="вес", default=0)
parent = models.ForeignKey('MenuItem', verbose_name="родительский пункт", null=True, blank=True)
objects = InheritanceManager()
def __str__(self):
return self.title
def url(self):
return "#"
def as_dict(self):
return dict(
id=self.id,
title=self.title,
description=self.description,
weight=self.weight,
url=self.url()
)
def children(self):
menu_objects = MenuItem.objects.filter(parent=self).select_subclasses()
menu_list = list()
for menu_item in menu_objects:
item = menu_item.as_dict()
item['items'] = menu_item.children()
menu_list.append(item)
return menu_list
class Meta:
verbose_name = "пункт меню"
verbose_name_plural = "пункт меню"
ordering = ['weight']
class PageMenuItem(MenuItem):
page = models.OneToOneField(Page, verbose_name="страница")
def url(self):
page = Page.objects.get_subclass(id=self.page_id)
return page.get_absolute_url()
Вроде всё хорошо, но встал вопрос -- как можно добавить пункты меню, ведущие на страницы -- списки объектов? В голове крутятся синглтоны, для которых можно было бы просто указать признак активности, вес, и выводимое название с описанием, но с ними в django напряжёнка -- django_solo делает их с фиксированными id=1, что неприменимо в случае с наследованием.
Есть ли какие-то более красивые варианты?