DEV Community

Arindam Dawn
Arindam Dawn

Posted on • Originally published at tabandspace.com

30 Days of Python 👨‍💻 - Day 25 - Web Development Extras

Starting from the basic skeleton of the Flask app, today I implemented some basic features to complete our simple minimalist blog application. First using flask's template inheritance pattern, I worked on re-using common template code and then added two dummy posts to render them dynamically. Finally, generating a requirements.txt file to store all the package dependencies in a single file which can then be used to download all the packages using a single command.

Template Inheritance

Currently, the index.html, about.html templates contain duplicate HTML code. Since Flask uses Jinja as the templating engine, we can use the powerful template inheritance concept to create a base template file which would contain all the common HTML code such as the HTML skeleton, navbar, footer etc. Other templates can then extend this base template hence making our template code more reusable. I created a base template file layout.html to contain the common template structure as follows.

layout.html

{% extends "layout.html" %}
{% block title %}Index{% endblock %}
{% block head %}
  {{ super() }}
  <style type="text/css">
    .important { color: #336699; }
  </style>
{% endblock %}
{% block content %}
  <h1>About</h1>
  <p class="important">
    Hi. I am Arindam. I love building User Interfaces. I am currently learning
    Python and created this simple blog using the Flask web development
    framework.
{% endblock %}
Enter fullscreen mode Exit fullscreen mode

A template re-usable block can be created and the code for that block needs to be placed within {% block block_name %} {% endblock %} The {% block %} informs the template engine that a child template may override these portions of the template.

The other files can then inherit this base template like this:

about.html

{% extends "layout.html" %}
{% block title %}Index{% endblock %}
{% block head %}
  {{ super() }}
  <style type="text/css">
    .important { color: #336699; }
  </style>
{% endblock %}
{% block content %}
  <h1>About</h1>
  <p class="important">
    Hi. I am Arindam. I love building User Interfaces. I am currently learning
    Python and created this simple blog using the Flask web development
    framework.
{% endblock %}
Enter fullscreen mode Exit fullscreen mode

Here the super() is used to render the contents of a block defined in the parent template.

More on templating using Flask can be found here https://flask.palletsprojects.com/en/1.1.x/patterns/templateinheritance/

Creating a page not found route

In Flask we can handle various exceptions that can happen while loading the contents from the server such showing a custom 404 page when a route is not found, showing a custom page when there is a 500 internal server error etc. I created a simple not_found.html file which would be rendered when a route is not found.

not_found.html

{% extends 'layout.html' %} 
{% block title %}Page Not Found{% endblock %}
{% block content %}
  <h1>Page Not Found</h1>
  <p class="important">
    Sorry, this page does not exist!
{% endblock %}
Enter fullscreen mode Exit fullscreen mode

server.py

@app.errorhandler(404)
def page_not_found(error):
    return render_template('not_found.html'), 404
Enter fullscreen mode Exit fullscreen mode

More on Flask error handling can be found here https://flask.palletsprojects.com/en/1.1.x/patterns/errorpages/

Adding Sample Posts

For simplicity, I created a posts folder inside the templates directory and created two dummy post files first-post.html and second-post.html. Then I created a [posts.py](http://posts.py) file to read all the post files and store the names of the files in a list which can be rendered in the home page.

posts.py

import os

def get_all_post_names():
    try:
        post_files = os.listdir('templates/posts')
        post_names = list(map(lambda x: x.split('.')[0], post_files))
        return post_names
    except:
        print('An error occurred while fetching posts')
        return []
Enter fullscreen mode Exit fullscreen mode
{% extends "layout.html" %} 
{% block title %}Index{% endblock %} 
{% block head%}
{{ super() }}
 {% endblock %} 
{% block content %}

<h1 class="title">
  Welcome to the Python Blog.
</h1>
<h2>Recent Posts</h2>
<ul class="post-list">
  {% for post in post_names %}
  <li><a href="posts/{{post}}.html">{{post}}</a></li>
  {% endfor %}
</ul>

{% endblock %}
Enter fullscreen mode Exit fullscreen mode

All Python statements such are written inside {% %} blocks as per the Jinja template syntax. Dynamic values are placed with {{ }}. We should now be able to see the two posts on the home page. Now on clicking on a post, it should open the respective post page. For that, we have to create a route which handles the post pages dynamically.

@app.route('/posts/<string:post_name>')
def show_post(post_name):
    return render_template(f'posts/{post_name}.html')
Enter fullscreen mode Exit fullscreen mode

This route will handle page names and render the respective post pages dynamically.

Generating a requirement.txt file

In our Python project, we can use several external packages. If we want to share this project with someone, we need to share all the project files and also mention all the packages that are required to run the project along with the correct version number. This is not quite a practical approach.

All the installed dependencies used in the project can be listed in a single requirements.txt file (as per convention) along with their version using this command.

$ pip freeze > requirements.txt
Enter fullscreen mode Exit fullscreen mode

This generates the requirements.txt file for the project. This file can be uploaded to any GIT repository. When someone downloads the project, they just need to run pip install -r requirement.txt and all the project related dependencies would be installed with the exact versions automatically.

The complete project code can be found here

References

The above project is a very minute use case of creating web apps using Python. I would like to list some useful references for further exploration:

That's all for today. For the remaining part of this series, I will be briefly exploring advanced topics such as the basics of Machine Learning and Data Science with Python.

Have a great one!

Top comments (2)

Collapse
 
drewgoodfellow profile image
DrewGoodfellow

I'm really enjoying the lessons, thank you. If I clone the github repo launch the page and click on one of the posts, I get the following error:

jinja2.exceptions.TemplateNotFound

jinja2.exceptions.TemplateNotFound: posts/first-post.html.html

Something is adding an extra .html onto the end of the post file. Do you know what might be causing this?

Best wishes.

Collapse
 
g3nark0 profile image
Joel Rivera-Pintado

I removed the ".html" on the show_post function and it worked:
return render_template(f'posts/{post_name}')