Overriding templates in Pelican when using multiple configs
web-dev ~ 2017-10-19This website and all its pieces now needs 4 pelican config files. And the latest addition requires different direct templates than the others. So I needed a way to override the direct templates without royally fucking anything else up. This is what I came up with.
Setup
I have 4 pelican configs. The main config blog_conf.py
is what builds this website. The second one 'secret_conf.py' builds my secret portion of the website. The third thumbs_conf.py
builds only the thumbnails so I do not waste space on my HDD by using symlinks. The fourth product.py
builds the product page I am working on now, which relies on the css and general templates I have already created.
Thankfully I have a few key abstractions in place that made this go surprisingly smooth.
- All of my configs inherit from the
blog_conf.py
to be as DRY as possible which eliminates duplication of efforts. Especially in the plugins department - I use the
webassets
plugin to handle my css and js linking - My css is broken into small pieces so it's easily manageable which styles end up which page. See this post
- I use a
base.html
jinja template that all other templates inherit from
FIX:
FIRST, update generators.py so it will search EXTRA_TEMPLATES_PATHS first. Otherwise you cannot use extra tempaltes for override
generators.py
This is what I changed to get this magic to happen... Look between the "THESE LINES" lines :p
class Generator(object):
"""Baseclass generator"""
def __init__(self, context, settings, path, theme, output_path,
....
# templates cache
self._templates = {}
# __________THESE LINES __________
self._templates_path = self.settings['EXTRA_TEMPLATES_PATHS']
self._templates_path.append(os.path.expanduser(
os.path.join(self.theme, 'templates')))
# ______________________________
Example
Then in our extra folder we can "override" the templates we need to modify. Because of our setup with the base.html and webassets with piecemeal css it is easy to slap in the extra css needed to override styles from the original theme. Although this does kill my "one css file per page rule". Whatever.
blog_conf.py
This is the main pelican conf that builds the root content
THEME = 'theme'
DIRECT_TEMPLATES = ['index', 'archives']
PAGINATED_DIRECT_TEMPLATES = ['index']
EXTRA_TEMPLATES_PATHS = []
product.py
This is the pelican conf that builds the product content in
from blog_conf import *
THEME = 'theme'
DIRECT_TEMPLATES = ['index', 'archives']
PAGINATED_DIRECT_TEMPLATES = ['index']
EXTRA_TEMPLATES_PATHS = ['theme/templates/product']
This is my folder structure
theme/templates/
base.html
archives.html
index.html
fragments/
sidebar.html
footer.html
product/
base.html
index.html
fragments/
menu.html
footer.html
theme/templates/base.html
This is the ROOT base.html I use. Note the sidebar and footer fragments
<!DOCTYPE html>
<html lang="{{ DEFAULT_LANG }}">
<head>
<title>{% block page_title %}{% endblock page_title %} | Main Blog</title>
{% include 'fragments/head.html' %}
{% block assets %} {% endblock %}
</head>
<body>
{% include 'fragments/sidebar.html' %}
<div class="content">
{% block title %} {% endblock %}
{% block content %} {% endblock %}
{% include 'fragments/footer.html' %}
</div>
</body>
</html>
theme/templates/product/base.html
NOTE: I remove the above sidebar and put in the menu. I add in another asset reference that will override some of the other default css from the original theme using the webassets plugin. Pelican plugins FTW! :+1:
<!DOCTYPE html>
<html lang="{{ DEFAULT_LANG }}">
<head>
<title>{% block page_title %}{% endblock page_title %} | Product</title>
{% include 'fragments/head.html' %}
{% block assets %} {% endblock %}
{% assets "css-product-base" %}
<link rel="stylesheet" href="{{ SITEURL }}/{{ ASSET_URL }}">
{% endassets %}
</head>
<body>
<div class="content">
{% include 'fragments/menu.html' %}
{% block title %} {% endblock %}
{% block content %} {% endblock %}
{% include 'fragments/footer.html' %}
</div>
</body>
</html>
theme/templates/archives.html
This is where it gets awesome. While this relative url in the archive folder looks like it is pointing to the same folder, the environment picks up the base.html in the product folder first. So we need to change nothing in order to reuse the same template but get all the same benefits. The power of abstraction right? :rofl:
{% extends "base.html" %}