Simple orderable menus for Wagtail
The last few months I've been doing a good amount of work using Wagtail. Wagtail is a pretty cool CMS built on top of the Django framework. It hasn't been around for long but it has a pretty active community and a nice documentation. I've built a couple of relatively large sites with it and been quite happy with the result so far.
If you're new to Wagtail this demo is a great starting point. I highly recommend playing around for a bit and checking some of the cool features Wagtail ships with. One of these features are the so-called Snippets. Snippets are basically simple Django models that can be edited through a default Wagtail admin interface. They're a very handy tool to build something like a simple menu.
In your models.py you probably already have a LinkFields class as it comes with the Wagtail demo:
class LinkFields(models.Model): """ Represents a link to an external page, a document or a Wagtail page """ link_external = models.URLField( "External link", blank=True, null=True, help_text='Set an external link if you want the link to point somewhere outside the CMS.' ) link_page = models.ForeignKey( 'wagtailcore.Page', null=True, on_delete=models.SET_NULL, blank=True, related_name='+', help_text='Choose an existing page if you want the link to point somewhere inside the CMS.' ) link_document = models.ForeignKey( 'wagtaildocs.Document', null=True, on_delete=models.SET_NULL, blank=True, related_name='+', help_text='Choose an existing document if you want the link to open a document.' )
Define a class inheriting from LinkFields to represent your menu items:
class MenuItem(LinkFields): @property def url(self): return self.link def __unicode__(self): if self.link_external: title = self.link_external elif self.link_page: title = self.link_page.title elif self.link_document: title = self.link_document.title return title class Meta: verbose_name = "Menu item" description = "Items appearing in the menu"
* Feel free to add extra fields such as title, CSS class etc.. if you need them :)
And finally add the Menu class, the MenuManager class and another class to represent the relationship between a Menu and an item (MenuMenuItem)
class MenuMenuItem(Orderable, MenuItem): parent = ParentalKey(to='yourapp.Menu', related_name='menu_items') class MenuManager(models.Manager): def get_by_natural_key(self, name): return self.get(menu_name=name) @register_snippet class Menu(models.Model): objects = MenuManager() menu_name = models.CharField(max_length=255, null=False, blank=False) @property def items(self): return self.menu_items.all() def __unicode__(self): return self.menu_name class Meta: verbose_name = "Navigation menu" description = "Navigation menu" Menu.panels = [ FieldPanel('menu_name', classname='full title'), InlinePanel(Menu, 'menu_items', label="Menu Items", help_text='Set the menu items for the current menu.') ]
Don't forget to add the @register_snippet decorator!
If everything is fine you should end up with something like :
Enjoy!