djangodjango-viewsdjango-templatesdjango-cms

How do I correctly render a django-view using Apphooks in django-cms?


I am building a project in Django CMS using version 4.1.2. Part of the project will be a news-section, for which I defined a Django model like this:

# news/models.py

from django.db import models
from django.utils import timezone
from djangocms_text_ckeditor.fields import HTMLField

class Post(models.Model):
    title = models.CharField(max_length=200, verbose_name="Title")
    text = HTMLField(verbose_name="Text")
    user = models.ForeignKey('auth.User', on_delete=models.CASCADE, verbose_name="Author", blank=True, null=True)
    date = models.DateTimeField(default=timezone.now, verbose_name="Date")

    class Meta:
        verbose_name = "Post"
        verbose_name_plural = "Posts"

    def __str__(self):
        return self.title

I want this model to be a django model instead of handling it with CMS-Plugins, so that editors can manage Posts using the admin-interface and to make it easier to use things like pagination. I have a little experience with Django, but not really with Django CMS.

However, on the page displaying the news-section, I also want to have a header, a sidebar and a footer that should be editable using the CMS-Toolbar. Therefore I made an Apphook - from what I understand this should allow me to integrate the news-page with Django CMS. This is what my Apphook looks like:

# news/cms_apps.py

from cms.app_base import CMSApp
from cms.apphook_pool import apphook_pool
from django.urls import path
from django.utils.translation import gettext_lazy as _
from news import views

@apphook_pool.register
class NewsApp(CMSApp):
    app_name = "news_app"
    name = _("News Application")

    def get_urls(self, page=None, language=None, **kwargs):
        return [
            path("news/", views.news_view, name="news"),
        ]

The view that should be called by this Apphook looks like this:

# news/views.py

from django.shortcuts import render
from .models import Post

def news_view(request):
    posts = Post.objects.all().order_by('-date')
    print("Number of posts:", posts.count())

    context = {
        'posts': posts,
    }
    return render(request, 'news.html', context)

A minimal example of the template news.html looks like this:

{% extends "base.html" %}
{% load cms_tags sekizai_tags %}
{% load djangocms_alias_tags %}


{% block title %}News{% endblock %}

{% block header %}

    {% placeholder "header_placeholder" %}

{% endblock %}


{% block content %}
    <h1>News</h1>

    {% for post in posts %}
        {{ post.title }}
    {% endfor %}
    
    <p>End of news</p>
    
{% endblock %}

In the admin panel I created a page called "News" with the slug news/. In the advanced settings of that page, I selected the Apphook. As a template for that page, I selected news.html, which is also declared in settings.py:

# settings.py

CMS_TEMPLATES = [
    ('home.html', 'Content Template'),
    ('news.html', 'Newspage Template'),
]

With this whole setup I expected the view to pass the post-data to the template, which then renders it - in case of the minimal example between <h1>News</h1>and <p>End of news</p>. However, it doesn't. Nothing is being rendered between those tags, and it seems like there is no data at all being passed to the template, since I also don't see the print-results from the view.

Changing the get_urls()-method in the Apphook to this leads to the template being rendered as expected, but without being editable by CMS:

def get_urls(self, page=None, language=None, **kwargs):
        return [
            path("", views.news_view, name="news"),
        ]

Can someone maybe point me in the right direction to solve this? I don't really understand what is happening here, and I couldn't find anything on this in the documentation so far... Thank you!


Solution

  • The placeholder template tag only works on django CMS pages.

    What you probably want is a common header for all news items. You might want to look at the djangocms-alias package, it allows to define reusable content.

    Replace the placeholder template tag by a static_alias:

    {% load djangocms_alias_tags %}
    ...
    {% block header %}
    
        {% static_placeholder "header_placeholder" %}
    
    {% endblock %}
    ...
    

    You can edit and version the aliases independently of your news articles.