Generate full page or HTML fragment based on request header (HTMX) - python

When using HTMX framework with Python Flask, you have to be able to:
serve a request as a HTML fragment if it's done by HTMX (via AJAX)
server a request as a full page if it's done by the user (e.g. entered directly in the browser URL bar)
See Single-page-application with fixed header/footer with HTMX, with browsing URL history or Allow Manual Page Reloading
for more details.
How to do this with the Flask template system?
from flask import Flask, render_template, request
app = Flask("")
#app.route('/pages/<path>')
def main(path):
htmx_request = request.headers.get('HX-Request') is not None
return render_template(path + '.html', fullpage=not htmx_request)
app.run()
What's the standard way to output a full page (based on a parent template pagelayout.html):
{% extends "pagelayout.html" %}
{% block container %}
<button>Click me</button>
{% endblock %}
if fullpage is True, and just a HTML fragment:
<button>Click me</button>
if it is False?

This solution based on that we can use a dynamic variable when extending a base template. So depending on the type or the request, we use the full base template or a minimal base template that returns only our fragment's content.
Lets call our base template for fragments base-fragments.html:
{% block container %}
{% endblock %}
It's just returns the main block's content, nothing else. At the view function we have a new template variable baselayout, that contains the name of the base template depending on the request's type (originating from HTMX or not):
#app.route('/pages/<path>')
def main(path):
htmx_request = request.headers.get('HX-Request') is not None
baselayout = 'base-fragments.html' if htmx_request else 'pagelayout.html'
return render_template(path + '.html', baselayout=baselayout)
And in the page template, we use this baselayout variable at the extends:
{% extends baselayout %}
{% block container %}
<button>Click me</button>
{% endblock %}

As pointed in the section Null-Default Fallback of Jinja documentation, the extends tag can actually come in an if statement:
Jinja supports dynamic inheritance and does not distinguish between parent and child template as long as no extends tag is visited. While this leads to the surprising behavior that everything before the first extends tag including whitespace is printed out instead of being ignored, it can be used for a neat trick.
Usually child templates extend from one template that adds a basic HTML skeleton. However it’s possible to put the extends tag into an if tag to only extend from the layout template if the standalone variable evaluates to false which it does per default if it’s not defined. Additionally a very basic skeleton is added to the file so that if it’s indeed rendered with standalone set to True a very basic HTML skeleton is added:
{% if not standalone %}{% extends 'default.html' %}{% endif -%}
<!DOCTYPE html>
<title>{% block title %}The Page Title{% endblock %}</title>
<link rel="stylesheet" href="style.css" type="text/css">
{% block body %}
<p>This is the page body.</p>
{% endblock %}
Source: https://jinja.palletsprojects.com/en/3.0.x/tricks/#null-default-fallback
So, your requirement could be fulfilled doing:
{% if not fullpage %}{% extends 'pagelayout.html' %}{% endif -%}
{% block container -%}
<button>Click me</button>
{%- endblock %}

Related

Does flask.render_template() create variables with the None keyword assigned to them?

I have 4 files:
flask_blog.py
from flask import Flask, render_template
app = Flask(__name__)
#app.route("/")
#app.route("/home")
def home_page():
return render_template("home.html")
#app.route("/about")
def about():
return render_template("about.html", title = "About")
layout.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
{% if title %}
<title>Flask Blog - {{ title }}</title>
{% else %}
<title>Flask Blog</title>
{% endif %}
</head>
<body>
{% block content %}{% endblock content %}
</body>
</html>
home.html
{% extends "layout.html" %}
{% block content %}{% endblock content %}
about.html
{% extends "layout.html" %}
{% block content %}{% endblock content %}
On my about page, the title is "Flask Blog - About", and in my home page, the title is "Flask blog". Now, this is happening due to the if/else statements in the layout.html file. The if/else statements are:
{% if title %}
<title>Flask Blog - {{ title }}</title>
{% else %}
<title>Flask Blog</title>
{% endif %}
Now, the value of the 'title' variable must be 0 or None, since we are going to the else statement for the home page. Now, my question is, we didn't assign a value to the 'title' variable beforehand. Better yet, we haven't even created the 'title' variable in the flask_blog.py file. How are we not getting a NameError? Shouldn't we get NameError: name 'title' is not defined?
Related
The templating language used by default in flask is Jinja. Jinja is not Python, and as such it works a little differently sometimes.
When a variable is not defined, if statements in Jinja template evaluate to False. As a result it doesn't throw an exception but instead it just goes to the next else block.
More specifically, an undefined variable becomes of type Undefined, which has it's own documentation page: https://jinja.palletsprojects.com/en/3.1.x/api/#undefined-types
This is useful in a templating language, because it makes it easier to re-use complex templates without having to specify every parameter every time you render it. If a parameter (and corresponding if block) is not relevant for a call to render_template, you can just omit it and don't worry about it.

Flask doesn't load css correctly

I'm working with Flask and everything is going well until I created another path and the stylesheet didn't load.
The stylesheet is in the static folder and everything but I can't get it to load.
It works perfectly for the routes on first routes after home e.g. ("/home" , "/index", "/dashboard"). It removes the styles when it goes into secondary routes e.g. ("/home/first", "/index/first")
make sure the file extends the base or whatever file that contains the link to the css styling
{% extends "template_that_links_to_css.py" %}
The most powerful part of Jinja is template inheritance. Template inheritance allows you to build a base “skeleton” template that contains all the common elements of your site and defines blocks that child templates can override.
For example:
<!-- base.html -->
{% extends 'bootstrap/base.html' %}
{% block styles %}
{{ super() }}
<link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='css/styles.css') }}">
{% endblock %}
{% block content %}
<!-- your child templates will go here -->
{% endblock %}
This base template inherits the styles from flask-bootstrap to begin with. Then it defines the link to the styles.css file. All child templates' content will come within the block content. So regardless of what template you create, as long as it inherits this base template, your styles defined in styles.css will always apply.
A child template, say index.html, which is rendered when the home() view function is called will look like this:
<!-- index.html-->
{% extends 'base.html' %}
{% block content %}
<div class="row">
<div class="col-md-4">
<!-- content -->
</div>
</div>
{% endblock %}
You inherit the base template using the keyword extends. As mentioned ealier, the content will appear in the block content of the base template, so this has to be defined explictly.
The route to render this page can look like this:
# routes.py
from app import app
from flask import render_template
#app.route("/home")
def home():
return render_template("index.html")
So, regardless of whatever template you create, as long as you are inheriting the base template, your styles will apply in each one of them.
Read more from template inheritance doc.

Django: Is it possible to call a template from inside another template? [duplicate]

I have a very basic template (basic_template.html), and want to fill in the with data formatted using another partial template. The basic_template.html might contain several things formatted using the partial template.
How should I structure the code in views.py?
The reason I am doing this is that later on the will be filled using Ajax. Am I doing this right?
You can do:
<div class="basic">
{% include "main/includes/subtemplate.html" %}
</div>
where subtemplate.html is another Django template. In this subtemplate.html you can put the HTML that would be obtained with Ajax.
You can also include the template multiple times:
<div class="basic">
{% for item in items %}
{% include "main/includes/subtemplate.html" %}
{% endfor %}
</div>
You can do this using a block. Blocks are a Django Template tag which will override sections of a template you extend. I've included an example below.
basic_template.html
<body>
{% block 'body' %}
{% endblock %}
</body>
template you want to include: (i.e. example.html)
{% extends 'basic_template.html' %}
{% block 'body' %}
/* HTML goes here */
{% endblock %}
views.py:
return render_to_response(template='example.html', context, context_instance)
Doing this will load basic_template.html, but replace everything inside of {% block 'body' %} {% endblock %} in basic_template.html with whatever is contained within {% block 'body' %} {% endblock %}.
You can read more about blocks and template inheritance in the Django Docs
There are mainly 2 ways (2 easy ones)
1:
In base html put
{% include "myapp/sub.html" %}
And just write html code inside your sub.html file
2:
https://docs.djangoproject.com/en/dev/ref/templates/language/#template-inheritance
I just wanted to add differences of extend and include.
Both template and include can use models inserted in current app.
Template is for global usage by your any app. Include is for use in certain apps.
For ex: you want to insert Image Slider to your homepage and about page but nowhere else. You can create Slider app with its own model for convenience and import its model and include in that pages.
If you used template for this example, you would create 2 templates one with slider and everything else other template have.

Subnavigation in Django

I need a subnavigation on my pages. I inherit from base.html where the main navigation is located, but I don't know how to make a subnavigation which differs from page to page.
I've thought about making a template tag in which I can specify items to the subnavigation in each template file, and only output the subnavigation if any subnavigation items are specified. How do others do it?
Why can't you have a separate block for subnavigation and override that block in child templates?
base.html
Calls
Messages
{% block subnav %}
{% endblock %}
calls.html
{% extends "base.html" %}
{% block subnav %}
Outbound calls
Inbound calls
{% endblock %}
messages.html
{% extends "base.html" %}
{% block subnav %}
Sent messages
Recieved messages
{% endblock %}
A simple way to implement such could be to create a templatetag that takes an argument, for example the unique id or slug of the current page in the page tree.
E.g.
#register.simple_tag
def subnavigation_for_page(request, page_id, *args, **kwargs):
qs = Page.objects.filter(is_active=True)
current_page = qs.get(id=page_id)
sub_navigation = list()
for page in qs.filter(level__gt=current_page.level):
if page.level > current_page.level:
sub_navigation.append(page)
return sub_navigation
You could extend the example to return the rendered navigation as html or use the 'as' node to return a variable and define the html within the template itself.
Package that might be of interest:
django-mptt

Blocks in included files not being filled by extended templates

I have a template that looks like this:
{% include "base/top.html" with context %}
{% include "base/nav.html" with context %}
<div id="content">
Stuff
{% block content %}{% endblock %}
</div>
{% include "base/bottom.html" with context %}
base/nav.html and base/bottom.html contain static content, but base/top.html contains a {% block title %}. So when I have a second template as that attempts to inherit from the first file like so:
{% extends firstfile.html %}
{% block title %}Imarealpage!{% endblock %}
{% block content %}Lorem ipsum dorem smitshm{% endblock %}
The {% block title %} section isn't rendered. How do ensure that it, and any other blocks in included files and defined in extended templates are rendered as they should be?
You're misunderstanding how {% include %} works. The {% include %} tag is not a pre-processor; it doesn't plop the included template's code directly into the including template before rendering. Instead, {% include %} fires off a new independent template render of the included template (just like as if you had rendered the included template directly from your own code), and then includes the rendered results into the rendering of the included template.
The implication of this is that included templates have a totally separate inheritance hierarchy from their including template. You can, for instance, have a base component.html template with some blocks in it, and then have e.g. foo-component.html which starts with {% extends "component.html" %} and fills in some blocks from component.html. And then you can have a layout.html that does {% include "foo-component.html" %}, and that will render foo-component.html, complete with its inheritance of component.html, and place the result into that spot in layout.html. But there is zero relationship between any blocks in layout.html and any blocks in component.html -- they are separate renders with separate block structures and inheritance hierarchies.

Categories

Resources