django-cms getting the page context in an application hook - django

Consider this cms_app.py
from django.utils.translation import ugettext_lazy as _
from cms.app_base import CMSApp
from cms.apphook_pool import apphook_pool
class FooHook(CMSApp):
name = _("FooHook Plugin")
urls = ["foo.urls"]
apphook_pool.register(FooHook)
The foo app, has a views model which contains to default django views, ListView and DetailView.. These have their default templates, inheriting from a base.html, these are foo_list.html and foo_detail.html..
The documentation is completely unclear how to get, for example the cms page's sidebar content or page context variable at all, within those templates..
Feincms has the {% fragment %} template tag for this purpose, how can one achieve this in django-cms?

To access the page you can use {{ request.current_page }}
To use the plugin system in your templates use {% static_placeholder my_name %} instead of the {% placeholder %} tags.
It is always preferable to use the static_placeholders in apphooks as you do not know what template and what placeholders the page actually has.

Related

Django - add link with custom admin page href

In my Django project, I have created a custom admin page for an app via the get_urls() method. I'd like to add a link to the app's main model index view that will take users to this custom page - however, I'm having some trouble creating this link element correctly and I don't seem to be able to piece together the right way to do it - I'm just left with a Reverse for 'export' not found. 'export' is not a valid view function or pattern name. error.
I've set up the admin for the app like so:
# my_project/observations/admin.py
from django.template.response import TemplateResponse
from django.urls import path
class ObservationAdmin(SimpleHistoryAdmin, SoftDeletionModelAdmin):
change_list_template = 'export_link.html'
def get_urls(self):
urls = super().get_urls()
custom_urls = [
path('export/', self.admin_site.admin_view(self.export_view), name='export')
]
return custom_urls + urls
def export_view(self, request):
context = dict(
self.admin_site.each_context(request),
)
return TemplateResponse(request, 'export.html', context)
and the two templates that are referenced:
# my_project/observations/templates/export.html
{% extends "admin/base_site.html" %}
{% block content %}
<div>
Some custom content
</div>
{% endblock %}
# my_project/observations/templates/export_link.html
{% extends 'admin/change_list.html' %}
{% block object-tools-items %}
<li>
Export
</li>
{{ block.super }}
{% endblock %}
Navigating directly to http://localhost:8000/admin/observations/observation/export/ works perfectly, I see the custom content page exactly as I want it... so the issue I'm striking is with the link template - I get the Reverse... error when I navigate to the model index page.
Perhaps the argument I'm passing to url is incorrect, or I need to register that URL elsewhere - but I don't quite know. The other examples of link elements like this that I've been able to find don't reference URLs created via the admin class' get_urls() method - so any guidance on this would be greatly appreciated.
Thanks very much, let me know if there's any other info that I can provide to help sort this out.
I think the problems is in missing namespace in your export_link.html template. Instead of:
Export
try:
Export

How to add Wagtail 'admin' menu to custom templates?

On templates that derive from the Wagtail Page model, there is a small Wagtail icon/menu in the lower right corner. This provides a quick way to edit the page and/or jump to the Wagtail Admin. However, this menu does not appear on custom view templates that are not derived from Wagtail Page model.
How can I tell Wagtail to display the small menu on my frontend templates, so the pages have consistent navigation?
The standard Wagtail user bar is rendered by placing in your template:
{% load wagtailuserbar %}
{% wagtailuserbar 'top-left' %}
I typically just put the above in base.html. The 'top-left' designation, of course, tells the template tag where to render the user bar. Reference
However, the wagtailuserbar is only rendered for Wagtail pages. What you want to do is render the user bar with only the Go to Wagtail Admin option (because no other options would be relevant). Therefore, you could create your own template tag and place it in base.html beside the wagtailuserbar. You would set it up so that it renders if there IS NO page in the context (the wagtailuserbar template tag checks to make sure there IS a page in the context). To create your tag, just start with the code from wagtailuserbar.py and modify it to create a template tag called wagtailuserbar_admin_only (untested):
from django import template
from django.template.loader import render_to_string
from wagtail.admin.templatetags.wagtailuserbar import get_page_instance
from wagtail.admin.userbar import (AdminItem)
#register.simple_tag(takes_context=True)
def wagtailuserbar_admin_only(context, position='bottom-right'):
# Find request object
try:
request = context['request']
except KeyError:
return ''
# Don't render without a user because we can't check their permissions
try:
user = request.user
except AttributeError:
return ''
# Don't render if user doesn't have permission to access the admin area
if not user.has_perm('wagtailadmin.access_admin'):
return ''
# Only render if the context does NOT contain a variable referencing a saved page
page = get_page_instance(context)
if page:
return ''
# Render the items
rendered_items = [AdminItem()]
# Render the userbar items
return render_to_string('wagtailadmin/userbar/base.html', {
'request': request,
'items': rendered_items,
'position': position,
})
Then, to use in templates, place in base.html:
{% load wagtailuserbar_admin_only %}
{% wagtailuserbar_admin_only 'top-left' %}

SelectDateWidget - how get to each subfield separately?

In project there is used SelectDateWidget and to render it in template it only need to write form.date_of_sth, but it renders all widgets (selects) in one. I would like to render each one separately. Is there any way to do it?
In Django 1.11:
Import the widget that you want to modify. Your choices can be found here: https://docs.djangoproject.com/en/1.11/ref/forms/widgets/#built-in-widgets
Create a subclass and specify the template name.
# app/widgets.py
from django.forms.widgets import DefaultWidget
class CustomWidget(DefaultWidget):
template_name = 'app/widget.html'
Create your template. This is copied exactly from django/forms/widgets/multiwidget.html but you can put anything you want here. You can also reference other existing widget templates for inspiration. Your choice can be found here: https://github.com/django/django/tree/master/django/forms/templates/django/forms/widgets
# app/templates/app/widget.html
{% spaceless %}
{% for widget in widget.subwidgets %}
{% include widget.template_name %}
{% endfor %}
{% endspaceless %}
Import your CustomWidget and add it to a field in your form.
# app/forms.py
from django import forms
from .widgets import CustomWidget
class MyForm(forms.Form):
my_field = forms.ExampleField(widget=CustomWidget())
Finally, in your template, render the main field and it will reference app/templates/app/widget.html to render each subfield manually based on the template you created.

How to create an article list view in django-cms

I am an absolute beginner in django-cms, just acquired some pieces of knowledge to create templates. Just wondering, how to create a portal page that has a few acticles in each different categories?
Please simply point out a practical way to do, no real code is needed.
Thank you.
As others have pointed out, the way to do this is by hooking your CMS page to another set of views. Django-CMS provides application hooks:
#cms_app.py
from cms.app_base import CMSApp
from cms.apphook_pool import apphook_pool
class YourModuleApp(CMSApp):
name = 'My App MOdule'
urls = ['my_app.urls']
apphook_pool.register(YourModuleApp)
So, if you had a module called "my_app" with a urls.py in it, Django-CMS will add those patterns to the page. Look in the "Advanced Settings" section of the page in admin for the application drop-down menu.
Once the app is hooked to the page, Django-CMS will pull any content and the layout template from the information it holds, then hand off processing to the additional URL patterns that are hooked to it. That's how you can pull in another model, add a form, handle a POST, etc.
You could just do it the normal Django way. Create a normal Django app, with a URL pointing to a view that renders a template. The view could look like this:
from django.shortcuts import render
from cms.models import Page
def articles(request):
pages = Page.objects.public()
render(request, 'example.html', {'pages': pages})
And the template could look like this:
{% load cms_tags %}
{% for page in pages %}
<p>{% page_attribute "page_title" page %}</p>
{% endfor %}
You could stop here. Or you could have...
Additional Django CMS integration with AppHooks
Do you want your non-developer content managers to be able to put a list of articles wherever they want? This is where AppHooks come in.
Create a CMSApp class in the file appname/cms_app.py like this:
from cms.app_base import CMSApp
from cms.apphook_pool import apphook_pool
from django.utils.translation import ugettext_lazy as _
class ArticleListApp(CMSApp):
app_name = 'articlelist'
name = _('Article List')
def get_urls(self, page=None, language=None, **kwargs):
return ['articlelist.urls']
apphook_pool.register(YourModuleApp)
Delete the URL entry in your project-wide urls.py as you no longer need it. Your app's urls.py needs to include a view for the regex ^$.
Now you or any content manager user with necessary permissions can create a page in the admin interface, and modify the advanced settings to select the "Article List" application:
One gotcha is that this will have no effect until the page is published (as well as all of its ancestor pages).

django inclusion_tag

I'm trying to create inclusion tag and place it on the page but it's not work.
My views.py:
from django.shortcuts import render_to_response, redirect
from django import template
register = template.Library()
#register.inclusion_tag('weather.html')
def weather():
return {'city': 'angola'}
def home(request):
return render_to_response('index.html')
index.html
<title> TITLE </title>
Hi everyone!
{% weather %}
weather.html
weather is fine in {{city}}
Django debug page says that "Invalid block tag: 'weather'" so I guess I put declaration of inclusion_tag in wrong place? Where I need to put it to get it work?
Template tags need to go in a module in your app's templatetags directory. See the code layout section of the custom template tag docs for full details.
You then have to load your tag library in the template before you use your tag.
{% load my_tags %}
{% weather %}