I’ve been testing out some custom Python classes for Django templating, due to Python 3.6’s addition of F-strings. In addition, templates now have the full capability to be programmed in Python itself, with the possibility of creating smart templates and components based on Python classes.
Instead of the template language (copying and pasting just a typical Django template here):
{% extends "base.html" %}
{% block content %}
<!--Section: Products v.3-->
<section class="text-center mb-4">
<!--Grid row-->
<div class="row wow fadeIn">
{% for item in items %}
<!--Grid column-->
<div class="col-lg-3 col-md-6 mb-4">
<!--Card-->
<div class="card">
<!--Card image-->
<div class="view overlay">
alt="">
<a>
<div class="mask rgba-white-slight"></div>
</a>
</div>
<!--Card image-->
<!--Card content-->
<div class="card-body text-center">
<!--Category & Title-->
<a href="" class="grey-text">
<h5>Shirt</h5>
</a>
<h5>
<strong>
<a href="" class="dark-grey-text">{{item}}
<span class="badge badge-pill danger-color">NEW</span>
</a>
</strong>
</h5>
<h4 class="font-weight-bold blue-text">
<strong>120$</strong>
</h4>
</div>
<!--Card content-->
</div>
<!--Card-->
</div>
<!--Grid column-->
{% endfor %}
</div>
<!--Grid row-->
</section>
<!--Section: Products v.3-->
{% endblock content %}
I have something like (example from a different project):
base_css = """
body {
margin: 2% auto 5% auto;
background: #fdfdf2;
color: #111;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
font-size: 16px;
line-height: 1.8;
text-shadow: 0 1px 0 #ffffff;
max-width: 73%;
}
h1, h2, h3, h4 {
line-height:1.2
}
h2, h3 {
color: #777
}
code {
background: white;
}
a {
border-bottom: 1px solid #444444;
color: #444444;
text-decoration: none;
}
a:hover {
border-bottom: 0;
}
.fixed {
white-space: nowrap;
vertical-align:top;
}
"""
class viewIndexHead(Block):
css = base_css
vary = []
def generateHTML(self,page):
return f'<title>Bobby Mozumder Homepage</title></head><body><h4><a href="/">
mozumder.net</a></h4>'
class viewArticleHead(Block):
css = base_css
vary = ['id']
def generateHTML(self,page):
return f'<title>{page.data.title}</title></head><body><h4><a href="/">
mozumder.net</a></h4>'
class article_body(Block):
vary = ['id']
def generateHTML(self,page):
html = f'<h1>{page.data.headline}</h1><h2>{page.data.subheadline}</h2><h3>{page.data.date_published:%B %d, %Y}</h3>{page.data.body}'
return html
class article_list(Block):
sql = 'get_view_headlines_fragment'
components = [
table()
]
def generateHTML(self,page):
page.articles_list = self.fetchall(page.request.session.session_key)
return super().generateHTML(page)
def fetchall(self,session):
self.c.execute('EXECUTE ' + self.sql + '(%s);', [session])
result = self.c.fetchall()
return result
With blocks generated from components:
class tableHTML(code):
def __iter__(self,page=None):
yield "<table>"
if page:
if page.request.user.is_authenticated:
for row in page.articles_list:
yield f'<tr><td class="fixed">{row.date_published:%B %d, %Y}</td><td><a href="/{row.slug}">{row.title}</a></td><td><a href="edit">edit</a><td><a href="delete">delete</a></td></tr>'
else:
for row in page.articles_list:
yield f'<tr><td>{row.date_published:%B %d, %Y}</td><td><a href="/{row.slug}">{row.title}</a></td></tr>'
yield "</table>"
class tableCSS(code):
def __iter__(self,page=None):
yield "table {border:0};"
class table(Component):
def __init__(self):
super().__init__()
self.html = tableHTML()
self.css = tableCSS()
With pages created by combining these block classes:
class viewArticle(UnchainedTemplate):
blocks = [viewArticleHead, article_body]
class index(UnchainedTemplate):
blocks = [viewIndexHead, article_list]
There are lots of properties of each class that I don’t show. Each Block class has properties for SQL, CSS, Header JS, Body initial JS, Body after JS. The UnchainedTemplate page classes do things like generate the HTML by combining each blocks from classes as well as reading in common HTML header/closer fragments from files.
It also calculates things like Content Security Policy headers for each piece of JS/CSS, and GZip compress each block BEFORE it goes to Redis cache - increasing my Redis cache size by 10x and speeding up page reads by not having to recompress it for every lookup. I also minimize CSS/JS from the class properties.
In the very long term, it’s entirely possible to create smart component-based UI classes that not only generate HTML/CSS/JS, but also things like iOS Swift UI classes, if you wanted to make a native iOS app from this, or a desktop UI app. Ideally there would be a library of pre-built smart components that a developer could use to rapidly make an app. I was inspired by Qt, which I loved and programmed in 10+ years ago. Qt has an interesting sockets/slots mechanism for event modeling.
This is an idea I’ve been lightly exploring a little bit for a couple of years, and is probably several years away from being usable by anyone else.