0

I'm working on a custom Django CMS plugin and encountered a situation where I need nested inlines. Below are my model structures.

class Link(NavLink):
    card = models.ForeignKey('CardPanel', related_name='card_links')

class CardPanel(models.Model):
    title = models.CharField(max_length=50)
    image = FilerImageField(null=True, blank=True, related_name="navigation_vertical_link_image")
    link_description = HTMLField(blank=True, null=True, max_length=150)
    button_link_internal = PageField(blank=True, null=True)
    button_link_external = models.URLField(blank=True, null=True)
    plugin = models.ForeignKey('Panel')

class Panel(CMSPlugin):
    pass

What I ideally need is nested inlines. So as Link model has m:1 relationship with CardPanel and CardPanel has m:1 relationship with the Panel model, I want to be able to add multiple CardPanels containing multiple Link models. What is the best way achieving this through the ModelAdmin in Django?

markwalker_
  • 12,078
  • 7
  • 62
  • 99
IVI
  • 1,893
  • 1
  • 16
  • 19
  • Possible duplicate of [Nested inlines in the Django admin?](https://stackoverflow.com/questions/3681258/nested-inlines-in-the-django-admin) – Thiago F. Pappacena Jan 13 '19 at 22:55
  • What is `NavLink`? I don't usually register models used for plugins in admin, so I think there is a better way to do this, but curious what that model is. – markwalker_ Jan 13 '19 at 23:05
  • NavLink is an abstract model containing a link title, url and external link. I'm looking for the best way no just specifically admin only. – IVI Jan 13 '19 at 23:11

1 Answers1

1

If it's a plugin you're creating here then since 3.0 these are only managed by the frontend:

In the new system, Placeholders and their plugins are no longer managed in the admin site, but only from the frontend.

So, there are various attributes of CMSPlugins which I think you'll find useful for this, including some of the standard plugins that come with CMS. You also don't need to specify a plugin attribute on your model if it's for a plugin.

I'd adjust your plugin class & corresponding model to be a bit more like;

# models.py
from cms.models.fields import PlaceholderField

class CardPanel(CMSPlugin):
    title = models.CharField(max_length=50)
    image = FilerImageField(
        null=True,
        blank=True,
        related_name="navigation_vertical_link_image"
    )
    content = PlaceholderField('card_panel_content')

# cms_plugins.py

from cms.plugin_base import CMSPluginBase
from cms.plugin_pool import plugin_pool

from .models import CardPanel


@plugin_pool.register_plugin
class CardPanel(CMSPluginBase):
    """ Plugin to contain card panels """
    model = CardPanel
    parent_classes = ['Panel']  # Include this if a card panel only exists in a panel

@plugin_pool.register_plugin
class Panel(CMSPluginBase):
    """ Plugin to contain card panels """
    model = CMSPlugin
    allow_children = True  # Allow the Panel to include other plugins
    child_classes = ['CardPanel']

By including a PlaceholderField on your CardPanel you can then render a placeholder for the model instance & add CMS plugins to that instance the same way you can add them to a page. This way, you can just add as many link plugins as you need and that plugin, if you don't use it, allows for page links or external links.

A placeholder field is rendered in the template like this;

{% load cms_tags %}

{% render_placeholder card_panel_instance.content %}

PlaceholderField can also be registered with admin; http://docs.django-cms.org/en/latest/how_to/placeholders.html#admin-integration

markwalker_
  • 12,078
  • 7
  • 62
  • 99