|
|
<!doctype html>
|
|
|
<html lang="en">
|
|
|
<head>
|
|
|
<meta charset="utf-8">
|
|
|
|
|
|
<title></title>
|
|
|
|
|
|
<meta name="description" content="">
|
|
|
|
|
|
|
|
|
<meta name="apple-mobile-web-app-capable" content="yes" />
|
|
|
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
|
|
|
|
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
|
|
|
|
|
<!-- For syntax highlighting -->
|
|
|
<link rel="stylesheet" href="../../../../lib/css/zenburn.css">
|
|
|
<link rel="stylesheet" href="../../../../lib/css/prism.css">
|
|
|
|
|
|
<link rel="stylesheet" href="../../../../css/reveal.css">
|
|
|
<link rel="stylesheet" href="../../../../css/theme/ga-title.css" id="theme">
|
|
|
|
|
|
<!--[if lt IE 9]>
|
|
|
<script src="lib/js/html5shiv.js"></script>
|
|
|
<![endif]-->
|
|
|
|
|
|
<link rel="stylesheet" type="text/css" href="https://s3.amazonaws.com/python-ga/proxima-nova/fonts.css" />
|
|
|
|
|
|
</head>
|
|
|
|
|
|
<body class="language-javascript">
|
|
|
|
|
|
<div class="reveal">
|
|
|
|
|
|
<!-- Any section element inside of this container is displayed as a slide -->
|
|
|
<div class="slides">
|
|
|
|
|
|
|
|
|
<!--
|
|
|
title: Next Steps in Web Development
|
|
|
type: lesson
|
|
|
duration: "01:00"
|
|
|
creator: Kevin Coyle
|
|
|
-->
|
|
|
<section id="section" class="level2 separator">
|
|
|
<h2><img src="https://s3.amazonaws.com/python-ga/images/GA_Cog_Medium_White_RGB.png" /></h2>
|
|
|
<h1>
|
|
|
Templates
|
|
|
</h1>
|
|
|
<hr />
|
|
|
</section>
|
|
|
<section id="learning-objectives" class="level2">
|
|
|
<h2>Learning Objectives</h2>
|
|
|
<p><em>After this lesson, you will be able to:</em></p>
|
|
|
<ul>
|
|
|
<li>Create a template HTML document.</li>
|
|
|
<li>Pass variables to a template HTML document via a Flask app.</li>
|
|
|
</ul>
|
|
|
<hr />
|
|
|
</section>
|
|
|
<section id="a-million-copies" class="level2">
|
|
|
<h2>A Million Copies</h2>
|
|
|
<ul>
|
|
|
<li><p>Don’t you hate it when you have to repeat yourself?</p></li>
|
|
|
<li><p>What if you had a website with 10 pages that were almost the same?</p></li>
|
|
|
<li><p>Would you code them all from scratch?</p></li>
|
|
|
</ul>
|
|
|
<hr />
|
|
|
</section>
|
|
|
<section id="we-do-your-index.html-page" class="level2">
|
|
|
<h2>We Do: Your <code>index.html</code> Page</h2>
|
|
|
<ul>
|
|
|
<li><p>Any route can use an <code>html</code> page.</p></li>
|
|
|
<li>Try it!
|
|
|
<ul>
|
|
|
<li>In your <code>my_website.py</code>, set the <code>/randnum/<inte></code> and <code>/</code> routes to both <code>render_template("index.html")</code>.</li>
|
|
|
</ul></li>
|
|
|
<li>What if we want them to display a different heading?</li>
|
|
|
<li><p>Do we need to rewrite the whole file?</p></li>
|
|
|
</ul>
|
|
|
<hr />
|
|
|
</section>
|
|
|
<section id="no-thats-what-templates-are-for" class="level2">
|
|
|
<h2>No! That’s What Templates Are For!</h2>
|
|
|
<aside class="notes">
|
|
|
<p><strong>Teaching Tip:</strong></p>
|
|
|
<ul>
|
|
|
<li>Talk for a few minutes here about design patterns and why it’s important to split data from presentation.</li>
|
|
|
</ul>
|
|
|
</aside>
|
|
|
<p>We use templates to:</p>
|
|
|
<ul>
|
|
|
<li>Write one HTML file.</li>
|
|
|
<li>Pass it variables.</li>
|
|
|
<li>Transfer info from Flask to HTML.</li>
|
|
|
</ul>
|
|
|
<p>As well as for one important design reason:</p>
|
|
|
<ul>
|
|
|
<li>We can separate data from how we present data to users.</li>
|
|
|
</ul>
|
|
|
<hr />
|
|
|
</section>
|
|
|
<section id="jinja2" class="level2">
|
|
|
<h2>Jinja2</h2>
|
|
|
<ul>
|
|
|
<li>One of the most widely used template engines for Python.</li>
|
|
|
<li>Used in places that you might have visited already, like Instagram or NPR.</li>
|
|
|
</ul>
|
|
|
<hr />
|
|
|
</section>
|
|
|
<section id="why-jinja2" class="level2">
|
|
|
<h2>Why Jinja2?</h2>
|
|
|
<aside class="notes">
|
|
|
<p><strong>Talking Points:</strong></p>
|
|
|
<ul>
|
|
|
<li>Jinja2 is kind of like the engine that powers our vehicle (Flask). However, this happens behind the scenes.</li>
|
|
|
<li><p>We’re quickly peering behind the scenes to get an idea of what our engine can do.</p></li>
|
|
|
<li>Some examples of what makes Jinja2 awesome are:
|
|
|
<ul>
|
|
|
<li><strong>Template inheritance:</strong> You can extend templates in very efficient ways.</li>
|
|
|
<li><strong>HTML escaping:</strong> Malicious hackers can create XSS attacks by injecting HTML code into our site where other users insert data. Jinja2 helps us avoid that.</li>
|
|
|
<li><strong>Speed and efficiency:</strong> Jinja2 is very fast. It compiles optimized Python code.</li>
|
|
|
<li><strong>Flexibility and extensibility:</strong> It’s really easy to add our own filters and functions.</li>
|
|
|
</ul></li>
|
|
|
</ul>
|
|
|
</aside>
|
|
|
<p>Jinja2 has some really powerful features that web design folks want to take advantage of:</p>
|
|
|
<ul>
|
|
|
<li>Template inheritance
|
|
|
<ul>
|
|
|
<li>Like class inheritance!</li>
|
|
|
</ul></li>
|
|
|
<li>HTML escaping
|
|
|
<ul>
|
|
|
<li>Stops some hacking attacks (XSS and SQL injection).</li>
|
|
|
</ul></li>
|
|
|
<li>Speed and efficiency
|
|
|
<ul>
|
|
|
<li>Optimized Python code.</li>
|
|
|
</ul></li>
|
|
|
<li>Flexibility and extensibility
|
|
|
<ul>
|
|
|
<li>We can add our own filters and functions.</li>
|
|
|
</ul></li>
|
|
|
</ul>
|
|
|
</section>
|
|
|
<section id="expanding-on-our-index.html" class="level2">
|
|
|
<h2>Expanding on Our <code>index.html</code></h2>
|
|
|
<ul>
|
|
|
<li><p>We’ll send a <code>greeting</code> variable into our <code>index.html</code> from both routes.</p></li>
|
|
|
<li><p>The routes will display different things!</p></li>
|
|
|
</ul>
|
|
|
<hr />
|
|
|
</section>
|
|
|
<section id="adding-templates" class="level2">
|
|
|
<h2>Adding Templates</h2>
|
|
|
<aside class="notes">
|
|
|
<p><strong>Talking Points:</strong></p>
|
|
|
<ul>
|
|
|
<li>This is the function that Flask uses to… you guessed it: Render our template(s)!</li>
|
|
|
<li>For this exercise, we want to add some programming language (Python) into our HTML template.</li>
|
|
|
</ul>
|
|
|
</aside>
|
|
|
<ul>
|
|
|
<li>Remember <code>import render_template</code>?</li>
|
|
|
<li>This is the function that Flask uses to… you guessed it: Render our template(s)!</li>
|
|
|
</ul>
|
|
|
<hr />
|
|
|
</section>
|
|
|
<section id="edit-index.html" class="level2">
|
|
|
<h2>Edit <code>index.html</code></h2>
|
|
|
<ul>
|
|
|
<li>Change the <code><h1></code> to be <code>{{ greeting }}</code>.</li>
|
|
|
</ul>
|
|
|
<div class="sourceCode" id="cb1"><pre class="sourceCode html"><code class="sourceCode html"><a class="sourceLine" id="cb1-1" data-line-number="1">...</a>
|
|
|
<a class="sourceLine" id="cb1-2" data-line-number="2"> <span class="kw"><body></span></a>
|
|
|
<a class="sourceLine" id="cb1-3" data-line-number="3"> <span class="kw"><h1></span>Hello {{ name }}!<span class="kw"></h1></span></a>
|
|
|
<a class="sourceLine" id="cb1-4" data-line-number="4"> <span class="kw"><p></span>If music be the food of love, play on!<span class="kw"></p></span></a>
|
|
|
<a class="sourceLine" id="cb1-5" data-line-number="5"> ....</a></code></pre></div>
|
|
|
<hr />
|
|
|
</section>
|
|
|
<section id="templating-syntax-with-jinja" class="level2">
|
|
|
<h2>Templating Syntax With Jinja</h2>
|
|
|
<aside class="notes">
|
|
|
<p><strong>Talking Points:</strong></p>
|
|
|
<ul>
|
|
|
<li>What’s awesome about this is that inside of these brackets, we can pass in Python syntax.</li>
|
|
|
<li>In our example, we have a variable, which we’re calling <code>name</code>.</li>
|
|
|
<li>Whatever we assign to the variable <code>name</code> will be rendered when our page renders.</li>
|
|
|
<li>Statements are where we would pass in logic like {%if this thing%} {% else that thing%}.</li>
|
|
|
</ul>
|
|
|
</aside>
|
|
|
<ul>
|
|
|
<li><p>Recognize the <code>{{}}</code>?</p></li>
|
|
|
<li><p>In Jinja, <strong>templates</strong> are rendered with double curly brackets (<code>{{ }}</code>).</p></li>
|
|
|
<li><p><strong>Statements</strong> are rendered with curly brackets and percent signs (<code>{% %}</code>).</p>
|
|
|
<ul>
|
|
|
<li><p>A use case here is passing in logic like:</p>
|
|
|
<div class="sourceCode" id="cb2"><pre class="sourceCode python"><code class="sourceCode python"><a class="sourceLine" id="cb2-1" data-line-number="1">{<span class="op">%</span> <span class="cf">if</span> name <span class="op">==</span> <span class="st">'kevin'</span> <span class="op">%</span>}</a>
|
|
|
<a class="sourceLine" id="cb2-2" data-line-number="2"><span class="co"># Do the thing</span></a>
|
|
|
<a class="sourceLine" id="cb2-3" data-line-number="3">{<span class="op">%</span> <span class="cf">else</span> <span class="op">%</span>}</a>
|
|
|
<a class="sourceLine" id="cb2-4" data-line-number="4"><span class="co"># Do all the other things.</span></a></code></pre></div></li>
|
|
|
</ul></li>
|
|
|
</ul>
|
|
|
<hr />
|
|
|
</section>
|
|
|
<section id="rendering-a-template-in-flask" class="level2">
|
|
|
<h2>Rendering a Template in Flask</h2>
|
|
|
<aside class="notes">
|
|
|
<p><strong>Talking Points:</strong></p>
|
|
|
<ul>
|
|
|
<li>We’re going to modify the rest of our Flask app to pass some values into our variable in the template (curly braces).</li>
|
|
|
<li><p>Let’s change the rest of our <code>hello_flask.py</code> so that the whole thing looks the following script on screen.</p></li>
|
|
|
<li>Here, we use <code>render_template</code> function which takes in two arguments:
|
|
|
<ol type="1">
|
|
|
<li>Our template name, <code>index.html</code>.</li>
|
|
|
<li>Our <strong>context</strong> which, from the documentation is “the variables that should be available in the context of the template.”</li>
|
|
|
</ol></li>
|
|
|
<li>Here, our variable is name which is passed into the <user> part of our route, and then becomes the value that we assign to the variable called <code>name</code>.</li>
|
|
|
</ul>
|
|
|
</aside>
|
|
|
<p>Let’s change our <code>my_website.py</code> accordingly:</p>
|
|
|
<div class="sourceCode" id="cb3"><pre class="sourceCode python"><code class="sourceCode python"><a class="sourceLine" id="cb3-1" data-line-number="1"><span class="at">@app.route</span>(<span class="st">'/'</span>)</a>
|
|
|
<a class="sourceLine" id="cb3-2" data-line-number="2"><span class="kw">def</span> home():</a>
|
|
|
<a class="sourceLine" id="cb3-3" data-line-number="3"> <span class="cf">return</span> render_template(<span class="st">"index.html"</span>, greeting<span class="op">=</span><span class="st">"Hello World!"</span>)</a>
|
|
|
<a class="sourceLine" id="cb3-4" data-line-number="4"></a>
|
|
|
<a class="sourceLine" id="cb3-5" data-line-number="5">...</a>
|
|
|
<a class="sourceLine" id="cb3-6" data-line-number="6"></a>
|
|
|
<a class="sourceLine" id="cb3-7" data-line-number="7"><span class="at">@app.route</span>(<span class="st">'/shownum/<inte>'</span>)</a>
|
|
|
<a class="sourceLine" id="cb3-8" data-line-number="8"><span class="kw">def</span> shownum(inte):</a>
|
|
|
<a class="sourceLine" id="cb3-9" data-line-number="9"> my_greeting <span class="op">=</span> <span class="st">"Your number is "</span> <span class="op">+</span> <span class="bu">str</span>(inte)</a>
|
|
|
<a class="sourceLine" id="cb3-10" data-line-number="10"> <span class="cf">return</span> render_template(<span class="st">"index.html"</span>, greeting<span class="op">=</span>my_greeting)</a></code></pre></div>
|
|
|
<hr />
|
|
|
</section>
|
|
|
<section id="try-it" class="level2">
|
|
|
<h2>Try it!</h2>
|
|
|
<ul>
|
|
|
<li>Check out: <code>http://localhost:5000</code>.</li>
|
|
|
<li>Then: <code>http://localhost:5000/shownum/26</code>.</li>
|
|
|
</ul>
|
|
|
<p>Do your other routes still work?</p>
|
|
|
<hr />
|
|
|
</section>
|
|
|
<section id="knowledge-check-discussion" class="level2">
|
|
|
<h2>Knowledge Check: Discussion</h2>
|
|
|
<p>What two arguments did we pass into the <code>render_template</code> function?</p>
|
|
|
<aside class="notes">
|
|
|
<p><strong>Talking Point:</strong></p>
|
|
|
<ul>
|
|
|
<li>Answer: <code>index.html</code>, our template name, and <code>greeting</code>, which is our context.</li>
|
|
|
</ul>
|
|
|
</aside>
|
|
|
<p>What’s one reason we use templates?</p>
|
|
|
<aside class="notes">
|
|
|
<p><strong>Talking Points:</strong></p>
|
|
|
Possible answers: - Adding programming languages to our HTML templates. - Transferring info from Flask to HTML. - Lets us separate data from how we present data to users.
|
|
|
</aside>
|
|
|
<hr />
|
|
|
</section>
|
|
|
<section id="your-turn" class="level2">
|
|
|
<h2>Your Turn!</h2>
|
|
|
<ul>
|
|
|
<li>Create a new Flask app, <code>shakespeare.py</code>.</li>
|
|
|
<li>Create a new template HTML file, <code>hello.html</code>.
|
|
|
<ul>
|
|
|
<li>It will display a paragraph with a parameter <code>poem</code> in it.</li>
|
|
|
</ul></li>
|
|
|
<li>Render it from the index endpoint.</li>
|
|
|
<li>Remember calling in variables from the last lesson?
|
|
|
<ul>
|
|
|
<li>Have your Flask app read in the poem saved in <code>hi.txt</code>, then pass that to the <code>hello.html</code> template to display.</li>
|
|
|
</ul></li>
|
|
|
<li>Launch your Flask app and check the results!</li>
|
|
|
</ul>
|
|
|
<hr />
|
|
|
</section>
|
|
|
<section id="template-solution" class="level2">
|
|
|
<h2>Template Solution</h2>
|
|
|
<div class="sourceCode" id="cb4"><pre class="sourceCode html"><code class="sourceCode html"><a class="sourceLine" id="cb4-1" data-line-number="1"><span class="dt"><!doctype </span>html<span class="dt">></span></a>
|
|
|
<a class="sourceLine" id="cb4-2" data-line-number="2"><span class="kw"><html></span></a>
|
|
|
<a class="sourceLine" id="cb4-3" data-line-number="3"><span class="kw"><head></span></a>
|
|
|
<a class="sourceLine" id="cb4-4" data-line-number="4"> <span class="kw"><meta</span><span class="ot"> charset=</span><span class="st">"utf-8"</span><span class="kw">></span></a>
|
|
|
<a class="sourceLine" id="cb4-5" data-line-number="5"> <span class="kw"><title></span>Shakespeare<span class="kw"></title></span></a>
|
|
|
<a class="sourceLine" id="cb4-6" data-line-number="6"><span class="kw"></head></span></a>
|
|
|
<a class="sourceLine" id="cb4-7" data-line-number="7"><span class="kw"><body></span></a>
|
|
|
<a class="sourceLine" id="cb4-8" data-line-number="8"> <span class="kw"><p></span>{{text}}<span class="kw"></p></span></a>
|
|
|
<a class="sourceLine" id="cb4-9" data-line-number="9"><span class="kw"></body></span></a>
|
|
|
<a class="sourceLine" id="cb4-10" data-line-number="10"><span class="kw"></html></span></a></code></pre></div>
|
|
|
<hr />
|
|
|
</section>
|
|
|
<section id="python-solution" class="level2">
|
|
|
<h2>Python Solution</h2>
|
|
|
<div class="sourceCode" id="cb5"><pre class="sourceCode python"><code class="sourceCode python"><a class="sourceLine" id="cb5-1" data-line-number="1"><span class="im">from</span> flask <span class="im">import</span> Flask, render_template</a>
|
|
|
<a class="sourceLine" id="cb5-2" data-line-number="2"><span class="im">import</span> os <span class="co"># Note the new import — to be in the file system.</span></a>
|
|
|
<a class="sourceLine" id="cb5-3" data-line-number="3"></a>
|
|
|
<a class="sourceLine" id="cb5-4" data-line-number="4">app <span class="op">=</span> Flask(<span class="va">__name__</span>)</a>
|
|
|
<a class="sourceLine" id="cb5-5" data-line-number="5"></a>
|
|
|
<a class="sourceLine" id="cb5-6" data-line-number="6">file_path <span class="op">=</span> <span class="st">'.'</span></a>
|
|
|
<a class="sourceLine" id="cb5-7" data-line-number="7"></a>
|
|
|
<a class="sourceLine" id="cb5-8" data-line-number="8"><span class="cf">with</span> <span class="bu">open</span>(os.path.join(file_path, <span class="st">'hi.txt'</span>)) <span class="im">as</span> f:</a>
|
|
|
<a class="sourceLine" id="cb5-9" data-line-number="9"> the_text <span class="op">=</span> f.read()</a>
|
|
|
<a class="sourceLine" id="cb5-10" data-line-number="10"></a>
|
|
|
<a class="sourceLine" id="cb5-11" data-line-number="11"><span class="at">@app.route</span>(<span class="st">'/'</span>) <span class="co"># When someone goes here...</span></a>
|
|
|
<a class="sourceLine" id="cb5-12" data-line-number="12"><span class="kw">def</span> home(): <span class="co"># Do this.</span></a>
|
|
|
<a class="sourceLine" id="cb5-13" data-line-number="13"> <span class="cf">return</span> render_template(<span class="st">"hello.html"</span>, text<span class="op">=</span>the_text)</a>
|
|
|
<a class="sourceLine" id="cb5-14" data-line-number="14"></a>
|
|
|
<a class="sourceLine" id="cb5-15" data-line-number="15"><span class="cf">if</span> <span class="va">__name__</span> <span class="op">==</span> <span class="st">'__main__'</span>:</a>
|
|
|
<a class="sourceLine" id="cb5-16" data-line-number="16"> app.run(debug<span class="op">=</span><span class="va">True</span>)</a></code></pre></div>
|
|
|
<hr />
|
|
|
</section>
|
|
|
<section id="summary" class="level2">
|
|
|
<h2>Summary</h2>
|
|
|
<ul>
|
|
|
<li>Jinja:
|
|
|
<ul>
|
|
|
<li>A popular templating engine.</li>
|
|
|
<li>Templates save us time and abstract presentation from data.</li>
|
|
|
</ul></li>
|
|
|
<li>Template fun:
|
|
|
<ul>
|
|
|
<li>We can pass variables into the template from the Flask app and the URL.</li>
|
|
|
</ul></li>
|
|
|
</ul>
|
|
|
</section>
|
|
|
|
|
|
</div>
|
|
|
<footer><span class='slide-number'></span></footer>
|
|
|
</div>
|
|
|
<script src="../../../../lib/js/head.min.js"></script>
|
|
|
<script src="../../../../js/reveal.js"></script>
|
|
|
|
|
|
<script>
|
|
|
|
|
|
var dependencies = [
|
|
|
{ src: '../../../../lib/js/classList.js', condition: function() { return !document.body.classList; } },
|
|
|
{ src: '../../../../plugin/markdown/showdown.js', condition: function() { return !!document.querySelector( '[data-markdown]' ); } },
|
|
|
{ src: '../../../../plugin/markdown/markdown.js', condition: function() { return !!document.querySelector( '[data-markdown]' ); } },
|
|
|
{ src: '../../../../plugin/prism/prism.js', async: true, callback: function() { /*hljs.initHighlightingOnLoad();*/ } },
|
|
|
{ src: '../../../../plugin/zoom-js/zoom.js', async: true, condition: function() { return !!document.body.classList; } }
|
|
|
];
|
|
|
|
|
|
if (Reveal.getQueryHash().instructor === 1) {
|
|
|
dependencies.push({ src: '../../../../plugin/notes/notes.js', async: true, condition: function() { return !!document.body.classList; } });
|
|
|
}
|
|
|
// Full list of configuration options available here:
|
|
|
// https://github.com/hakimel/reveal.js#configuration
|
|
|
Reveal.initialize({
|
|
|
controls: true,
|
|
|
progress: true,
|
|
|
history: true,
|
|
|
center: false,
|
|
|
slideNumber: true,
|
|
|
|
|
|
// available themes are in /css/theme
|
|
|
theme: Reveal.getQueryHash().theme || 'default',
|
|
|
|
|
|
// default/cube/page/concave/zoom/linear/fade/none
|
|
|
transition: Reveal.getQueryHash().transition || 'slide',
|
|
|
|
|
|
// Optional libraries used to extend on reveal.js
|
|
|
dependencies: dependencies
|
|
|
});
|
|
|
|
|
|
if (Reveal.getQueryHash().instructor === 1) {
|
|
|
Reveal.configure(dependencies.push({ src: '../../../../plugin/notes/notes.js', async: true, condition: function() { return !!document.body.classList; } }));
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Reveal.addEventListener('ready', function() {
|
|
|
if (Reveal.getCurrentSlide().classList.contains('separator-subhead')) {
|
|
|
document.getElementById('theme').setAttribute('href', '../../../../css/theme/ga-subhead.css');
|
|
|
} else if (Reveal.getCurrentSlide().classList.contains('separator')) {
|
|
|
document.getElementById('theme').setAttribute('href', '../../../../css/theme/ga-title.css')
|
|
|
} else {
|
|
|
document.getElementById('theme').setAttribute('href', '../../../../css/theme/ga.css');
|
|
|
}
|
|
|
});
|
|
|
|
|
|
Reveal.addEventListener('slidechanged', function(e) {
|
|
|
if (Reveal.getCurrentSlide().classList.contains('separator-subhead')) {
|
|
|
document.getElementById('theme').setAttribute('href', '../../../../css/theme/ga-subhead.css');
|
|
|
} else if (Reveal.getCurrentSlide().classList.contains('separator')) {
|
|
|
document.getElementById('theme').setAttribute('href', '../../../../css/theme/ga-title.css')
|
|
|
} else {
|
|
|
document.getElementById('theme').setAttribute('href', '../../../../css/theme/ga.css');
|
|
|
}
|
|
|
});
|
|
|
</script>
|
|
|
|
|
|
</body>
|
|
|
</html>
|