Flask: include template as several blocks - python

I am building a Flask application where I have widgets to include in several pages.
For example, a widget is dedicated to display a rotating gif when an Ajax request is made: html, js and css code defines this widget. Here is a simplified version:
includes/spinner.html
<style>
#loading {
position: absolute;
top: 50vh;
left: 50vw;
}
</style>
<img id="loading" src="{{url_for('static', filename='img/loader.gif')}}"/>
<script>
$(document).ajaxStart(function () {
activateSpinning();
}).ajaxStop(function () {
deactivateSpinning();
});
</script>
I'd like to figure out the best way to embed this code in the pages without requiring to create three different files to include.
Something like using the same include statement with a context variable?
includes/spinner.html
{% if context == "headers" %}...{% endif %}
{% if context == "body" %}...{% endif %}
{% if context == "js" %}...{% endif %}
layout.html
{% set context = "headers" %}
{% include "includes/spinner.html" %}
Or perhaps rendering multiple templates for a page:
includes/spinner.html
{% extends "layout/layout.html" %}
{% block headers %}...{% endblock %}
{% block body %}...{% endblock %}
{% block js %}...{% endblock %}
app/routes.py
#app.route("/", methods=["GET", "POST"])
def index():
...
render_template(["pages/index.html", "includes/spinner.html"], title="Import image")
Both solutions work but I'm not sure of the right way to do it and I can't really find a guide on best practices related to this use case. Do you have any ideas about this?

Related

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

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 %}

Processing Payments Via Paypal in Django-Oscar

I am trying to set up a basic e-commerce site using Django Oscar and am having difficulties. The majority of the problem has to do with the absence of examples of how to hook up meaningful (think Paypal, Stripe, Braintree) methods of payment and presence of obscure ones of which I have never heard before.
Either way, I am attempting to use django-oscar-paypal and follow its documentation. The Paypal Express part seems to work in that the button shows up and something akin to check out and processing happens.
However, if I choose to proceed with checkout in a regular way (with hopes of paying with a card), I am taken to the following page (the message in parentheses is mine)
Which is a product of the following template:
{% extends "checkout/checkout.html" %}
{% load i18n %}
{% block title %}
{% trans "Payment details" %} | {{ block.super }}
{% endblock %}
{% block checkout_nav %}
{% include 'checkout/nav.html' with step=3 %}
{% endblock %}
{% block checkout_title %}{% trans "Enter payment details" %}{% endblock %}
{% block order_contents %}{% endblock %}
{% block shipping_address %}{% endblock %}
{% block shipping_method %}{% endblock %}
{% block payment_method %}{% endblock %}
{% block payment_details %}
{% block payment_details_content %}
<p>{% trans "(*** Message from ./templates/tshirt-theme/ ***) This page needs implementing within your project. You may want to use one of Oscar's payment gateway libraries:" %}</p>
<ul>
<li>django-oscar-paypal</li>
<li>django-oscar-datacash</li>
<li>django-oscar-gocardless</li>
<li>django-oscar-paymentexpress</li>
<li>django-oscar-accounts</li>
</ul>
<a id="view_preview" href="{% url 'checkout:preview' %}" class="btn btn-primary btn-lg">{% trans "Continue" %}</a>
{% endblock payment_details_content %}
{% endblock payment_details %}
When I click "Continue", I am taken to something resembling a pre-order page on which the Payment Method is empty. When I click "Change" on it, it takes me back to the page on the screenshot.
My question is how do I get credit cards to work with this setup? Is there a better way of doing this thing altogether? I am somewhat familiar with Django, but this seemingly simple task seems to require a lot of knowledge and/or a lot of re-inventing the wheel. The latter must be the case because there is no documentation or tutorials on any of this, but many sites allegedly use Django-Oscar.
Any help or advice is appreciated.
From the django-paypal repo view the sandbox code, in particular the templates folder, settings.py and urls.py. I followed the instructions and added the necessary paypal keys to settings.py as well as the urls.py but failed to copy the templates, since that was documented less carefully.
For me simply adding at the very least the same templates as the sandbox made the screen you are viewing be replaced with working paypal buttons. In particular, the sandbox/templates/checkout/payment_details.html seems to be what gets rendered in place of this reminder message you are seeing — note that the template has both Express and Flow options, so use only what your site is set to use.
Add the below code to oscar/checkout/preview.html, and also change the client ID #
<body>
<div class="col-sm-5 col-sm-offset-7">
<!-- Set up a container element for the button -->
<div id="paypal-button-container" ></div>
<!-- Include the PayPal JavaScript SDK -->
<script src="https://www.paypal.com/sdk/js?client-id={{'Askdlsfhslfkdlfkdsflskd-wkJzFrkfldfkjhdlkfrW3-5U-RW0-ZsZskflsfu_YT-85r'}}&currency=PLN&locale=pl_PL"></script>
<script>
// Render the PayPal button into #paypal-button-container
paypal.Buttons({
style: {
layout: 'horizontal',
size: 'small',
color: 'blue',
shape: 'rect',
label: 'pay',
height: 44,
tagline: 'true'
},
enableStandardCardFields: false,
// Set up the transaction
createOrder: function(data, actions) {
return actions.order.create({
purchase_units: [{
amount: {
value: JSON.parse({{ order_total.incl_tax }}) // pass variable with amount to script
// <!-- value: '0.01', -->
}
}]
});
},
// Finalize the transaction
onApprove: function(data, actions) {
return actions.order.capture().then(function(details) {
// Show a success message to the buyer
alert('Transaction completed by ' + details.payer.name.given_name + '!');
});
}
}).render('#paypal-button-container');
</script>
</div>
</body>

Flask Jinja2 template is not rendering style tag?

I am making (learning) a simple web app using flask. I am not using the templates for css input. I'm directly coding the css using the 'style' tag in the jinja2 template. But the style tag is not working. Even though using inspect element in chrome i can see the style tag being rendered?
index.html code, which is to be rendered
{% extends "base.html" %}
{% block style %}
body{
line-height:1.5;
background-color:gray;
font-size:10px;
}
{% endblock %}
base.html
<html>
<head>
{% if title %}
<title>{{ title }}</title>
{% else %}
<title>Welcome to Micro-blogger Index page!</title>
{% endif %}
<style>
{% block style %}{% endblock %}
</style>
</head>
<body>
<h1><a href='/'>Index</a>
<br/>
{% block content %}{% endblock %}
</body>
</html>
Edit: By not working i mean like the background color is still white and font size & line-height are not as coded.
Edit:When I put the css element in the base.html , the rendering works. But when i pass it through variable in the index.html file the prev. mentioned thing happens. Yeah i could link a stylesheet or pass the css element from the base.html template , but what would i do in a scenario (i hope i don't have to , maybe for debug purposes) when i have two html docs both inheriting from same base.html but need to have different css attributes (like different background color etc.)
What is the need to add style using the ninja template custom tags.
Instead you can directly use the style tag.
Here is the code.
<style>
body{
line-height:1.5;
background-color:gray;
font-size:10px;
}
</style>
and you can directly place it wherever you want.

webapp2, Jinja2: how to cut large html file into multiple html files

When I blog, I like to separate each blog-post into its own .html file (is that ok?)
This prevents the file getting too big, and makes it easy to go back and edit a previously written blog post if need be.
Occasionally the blog post will contain css/js/ajax/template variables.
But on my website, I like all the blog posts on one page (so I can scroll through them all, instead of going to a separate page for each post)
Here is an html file that contains two blog posts:
{% extends "base.html" %}
{% block blog_posts %}
<!-- links/targest for the side menu to jump to a post -->
<li>Post2 - April 2012</li>
<li>Post1 - Feb 2012</li>
{% endblock %}
{% block content %}
<div id="post1">
spam1 blah blah
</div>
<div id="post2">
spam2
</div>
{% endblock %}
and in base.html I have something like:
<div id="content-container">
<div id="section-navigation">
<ul>
{% block blog_posts %}
{% endblock %}
</ul>
</div>
<div id="content">
{% block content %}{% endblock %}
</div>
</div>
What is the best way for me to split these blog posts out into separate files using webapp2 and jinja2?
e.g. blog1.html might look like:
{% block blog_posts %}
<!-- links/targest for the side menu to jump to a post -->
<li>Post1 - Feb 2012</li>
{% endblock %}
{% block content %}
<div id="post1">
spam1 blah blah
</div>
{% endblock %}
(And I would want the links and the blogposts to be displayed in the right order on the website)
I could think of a way of doing it where post2 extends post1.html, post3 extends post2.html etc, but I would prefer to fan out more
"Henry and Kafura introduced Software Structure Metrics Based on Information Flow in 1981[2] which measures complexity as a function of fan in and fan out."
Thanks
#robert king, your design has data embedded directly in the template. Templates should only contain the blueprint to a view, and they should be rendered with new data generated from your main code every time. I simulate this process here (Edited to illustrate the use of a loop to extract post titles, and the display of a single post.):
import jinja2
# NOTE: in this template there is no data relating to specific posts.
# There are only references to data structures passed in from your main code
page_template = jinja2.Template('''
<!-- this is a navigation block that should probably be in base.html -->
{% block blog_posts %}
<!-- links/targets for the side menu to jump to a post -->
{% for post in posts %}
<li><a href="{{ post.url }}">{{ post.title }}
- {{ post.date }}</a></li>
{% endfor %}
{% endblock %}
<!-- this is a content block that should probably be in page.html -->
{% block content %}
<div id="post">
<h1>{{ current.title }}</h1>
<h2>{{ current.date }}</h2>
<p>{{ current.content }}</p>
</div>
{% endblock %}
''')
# NOTE your main code would create a data structure such as this
# list of dictionaries ready to pass in to your template
list_of_posts = [
{ 'url' : '#post1',
'title' : 'My first post',
'date' : 'Feb 2012',
'content' : 'My first post is about Hello World.'},
{ 'url' : '#post2',
'title' : 'My second post',
'date' : 'Apr 2012',
'content' : 'My second post is about Foo Bar.'}
]
# Pass in a full list of posts and a variable containing the last
# post in the list, assumed to be the most recent.
print page_template.render(posts = list_of_posts,
current = list_of_posts[-1])
Hope this helps.
EDIT See also my answer to a question on "Site fragments - composite views"
I just found another option in the jinja2 tutorial. I think it makes more sense for my handler to pass my template a list of filenames of blog posts, and then to include the blog posts.
include - returns the rendered contents of that file into the current namespace:
{% include 'header.html' %}
<div ...
{% include 'footer.html' %}
Included templates have access to the variables of the active context by default. For more details about context behavior of imports and includes see Import Context Behavior.
From Jinja 2.2 onwards you can mark an include with ignore missing in which case Jinja will ignore the statement if the template to be ignored does not exist. When combined with with or without context it has to be placed before the context visibility statement. Here some valid examples:
{% include "sidebar.html" ignore missing %}
{% include "sidebar.html" ignore missing with context %}
{% include "sidebar.html" ignore missing without context %}
New in version 2.2.
You can also provide a list of templates that are checked for existence before inclusion. The first template that exists will be included. If ignore missing is given, it will fall back to rendering nothing if none of the templates exist, otherwise it will raise an exception. Example:
{% include ['page_detailed.html', 'page.html'] %}
{% include ['special_sidebar.html', 'sidebar.html'] ignore missing %}
When I read the raw html file (file.read()) and passed the data to my template, it escaped all the html.
instead of {{data}} i had to use {{data|safe}} which allowed raw html.
something like:
class HomeHandler(BaseHandler):
def get(self):
file_names = sorted(os.listdir('blog_posts'))
html = [open('blog_posts/%s' % fn).read() for fn in file_names]
templates = {'html': enumerate(html)}
self.render_template('home.html', **templates)
{% block content %}
{% for num,data in html %}
<div id="post{{num}}">
{{data|safe}}
</div>
<br />
<img src="http://www.sadmuffin.net/screamcute/graphics/graphics-page-divider/page-divider-007.gif" border=0>
<br />
{% endfor %}
{% endblock %}
(make sure the directory isn't a static directory)

Highlighting link of currently opened category tab in css

I am trying to highlight a link of a currently opened category tab, here is what i have already done:
globs.py
def globs(request):
cats = Category.objects.all()
return {'cats': cats}
views.py
def news_by_category(request, slug):
c = Category.objects.get(slug=slug)
news = News.objects.filter(category=c, status='p').order_by('-id')
#news = c.news_set.all().order_by('-id')
return object_list(
request,
news,
paginate_by = 5,
extra_context = {'c':c},
template_name = 'news_by_category.html')
base.html #bodyclass
<body class="{% block bodyclass %}{% endblock %}">
news_by_category.html
{% block bodyclass %}{{c|cut:" "}}{% endblock %}
base.html
<li><h4>Categories:</h4></li>
{% for i in cats %}
<li class="{{i.name|safe|cut:" "}}_li">
{{ i.name }}
</li> {% endfor %}
What i need to do now is to create style for every category, in category list, I could achieve this easily by styling inside a html file, but I'm not sure wether that would be proper (Would it?). I came up with some css styling,
{% for i in cats %}
body.{{ i|safe|cut:" "}} li.{{i|safe|cut:" "}}_li {
color: red;
}
but as I can't use django template tags inside my .css file, this wont work.
My questions:
1) How could i make this css file work for me. Any chance for a little step by step?
2) If I failed step1, how improper would it be to style those few li elements inside html file?
EDIT: /trying another way
I tried using:
base.html
{% for i in cats %}
<li class="{% ifequal 'request.get_full_path' '/k/{{ i.slug }}/' %}active{% endifequal %}">
{{ i.name }}
</li> {% endfor %}
.css
.active {{color:red;}
When i compared {{ request.get_full_path }} and /k/{{i.slug}}/ both returned same thing... but if its inside ifequal it doesnt seem to work.
You can create a simple class named "active" or something along those lines and add it to the current tab. Then, in your CSS you apply the active styles to that class. So you just append the active class and it'll automatically take the active style.
If you have a url:
{% url app:home i.slug as home %}
<li {% ifequal request.get_full_path home %}class="active"{% endifequal %}>

Categories

Resources