I’ve been using Jekyll for the blog over at gpxz.io but eventually the site is going to run on Django.
I want to keep using Jekyll to manage and render blog posts as it’s the best tool for the job and I already have it set up. But I also want the blog to inherit from the website’s Django templates for visual consistency, and I want requests to go through Django middleware.
I was able to get this working with the following approach:
We’ll start off my making kind of a meta template: a Django template that, when rendered, will produce a Jekyll template. The Django template can extend the base template you’re using for Django (with your navbar, footer, etc). {% verbose %}
tags are needed to prevent Django from trying to execute the liquid tags.
For example this template
<!-- django/myapp/templates/blog_post.html -->
{% extends "base.html" %}
{% block main %}
<div class="blog-post">
<h1>{% verbatim %}{{ page.title }}{% endverbatim %}</h1>
{% verbatim %}{{ content }}{% endverbatim %}
</div>
{% endblock %}
when rendered by django will result in a Jekyll template like
<!-- jekyll/_layouts/post.html -->
<html>
<head></head>
<body>
<nav></nav>
<div class="blog-post">
<h1>{{ page.title }}</h1>
{{ content }}
</div>
<footer></footer>
</body>
</html
Next we need a trigger to render the template into Jekyll’s _layouts
directory. I use autoreload in development for Django so I used AppConfig
to render the template whenever Django is started (which will happen whenever the template is modified), but you could also add this as a manage.py
command or similar:
# django/myapp/apps.py
from django.apps import AppConfig
from django.conf import settings
from django.template.loader import render_to_string
class MyAppConfig(AppConfig):
def ready(self):
if settings.WRITE_BLOG_TEMPLATES_ON_STARTUP:
html = render_to_string("www/blog-post.html")
dest_path = "/app/jekyll/_layouts/post.html"
with open(dest_path, "w") as f:
f.write(html)
Not much needs to change on the Jekyll side: just put layout: post
in your fontmatter.
Add the rendered blog folder as a template directory in Django
# django/myapp/settings.py
TEMPLATES = [
{
"BACKEND": "django.template.backends.django.DjangoTemplates",
"DIRS": [os.path.join(BASE_DIR, "myapp", "templates"), "/app/blog/_site/"],
"APP_DIRS": True,
"OPTIONS": {
"context_processors": [
"django.template.context_processors.debug",
"django.template.context_processors.request",
"django.contrib.auth.context_processors.auth",
"django.contrib.messages.context_processors.messages",
"django.template.context_processors.request",
],
},
},
]
Add a catchall url to route blog requests
# django/myapp/urls.py
urlpatterns += [
path("blog/<slug:slug>", BlogPostView.as_view())
]
and the corresponding view
# django/myapp/views.py
from django.views.generic import TemplateView
class BlogPostView(TemplateView):
def dispatch(self, request, *args, **kwargs):
post_name = self.kwargs['slug']
post_filename = post_name + '.html'
post_path = os.path.join('/app/blog/_site', post_filename)
return render(request, post_path)
then you should be good to go!
You can add views for feed.xml
and sitemap.xml
too.
/blog/*
requests to Jekyll which solves the SEO issue, but it’s still tough to manage two different css and template systems.