• Hendrik

Creating a Blog Using Python

Creating a Blog Using Python

Hello everyone! Welcome to my blog, where I will (hopefully) document my journey of learning to code. After over a year of coding as a hobby, completing several projects and having no idea what to do with them, I decided to create a blog to document and showcase some of my projects.
There are dozens of blog frameworks and toolkits out there that simplify creating a blog, but as I wanted to get a taste of the world of web development anyway, I thought it would be best to create a blog from scratch. Join me as I share the challenges I faced, the lessons I learned, and the joy of seeing my blog come together.

Where to start?

Knowing absolutely nothing about web development the first step I had to take was to research on how a website works internally and which components are needed to create and deploy one. When searching online I quickly realised that it might not be as easy as I first thought as there are quite a few components that are needed when developing a website from scratch.

  1. A domain: You obviously need a domain to deploy your website somewhere. Fortunately I was able to buy the top level domain (TLD) hendrik.page for a low price a few months prior.
  2. Web Hosting: Because my blog has to be hosted somewhere. As I have rented a Linux server for previous projects I decided to host this blog on my own server via a docker container.
  3. HTML: I need HTML to display blog posts as well as every other information on this website. To enable interaction with the website the HTML needs to be dynamic.
  4. CSS: To give my blog an overall theme and enhance readability of blog posts I will have to use CSS to prettify my blog.
  5. Web Development Framework: Frameworks provide a structured way to develop web applications. They work as a back end for the web application and offer features like routing, database integration, and user management.

With two of five components already out of the way I was able to focus on the remaining three: HTML, CSS and Web Development Frameworks. As I was already quite familiar with Python, I decided to look for solutions that incorporated Python to give me a bit of a head start.

Choosing the right web development framework

When looking for a Python based web framework the first challenge I faced was the decision on which framework to use. When searching the web you quickly come across three predominantly used frameworks: Flask, Django and FastAPI.

While Django and Flask have long been popular choices, FastAPI emerges as an excellent option for beginners due to its intuitive API design and clear documentation. FastAPI's API design closely resembles the widely adopted and standardized approach of using decorators and route decorators for defining routes and request handlers. This approach made it really easy for me to understand and work with FastAPI's routing system. Let's have a look at the request handler for the blog post you are reading right now.

@app.get("/post/{post_name}", response_class=HTMLResponse)  
async def page(request: Request, post_name: str):  
    data = openfile(folder_path="app/posts", filename=post_name+".md")  
  
    return templates.TemplateResponse("post.html", {"request": request, "data": data})

This example demonstrates how to handle a GET request for a specific post page and render a HTML response using a template file in FastAPI.

  1. First we define a GET request handler using the @app.get decorator. It specifies the route /post/{post_name}, where {post_name} is a path parameter that represents the desired post name in the URL. /post/blog-creation for this blog post.

  2. The response_class=HTMLResponse parameter indicates that the response returned by this handler will be a HTML response. This ensures that the client receives the response with the appropriate content type.

  3. The async def page(request: Request, post_name: str): function signature defines the request handler. It takes two parameters: request, which represents the incoming request, and post_name, which is the captured value from the path parameter. As you can tell by the preceding async when defining the request handler function, FastAPI has built-in support for asynchronous programming. While my blog may not require asynchronous capabilities, I can gradually explore and harness the power of asynchronous programming for future projects without having to switch frameworks later on.

  4. Inside the request handler, we call a function named openfile() to retrieve the content and metadata of the post. It takes two arguments: folder_path, which specifies the folder where the post files are stored, and filename, which is the name of the post file with the .md extension. Using the markdown Python library this function allows us to write posts in markdown format and easily convert them into HTML.

  5. The templates.TemplateResponse is used to generate an HTML response using a template file called "post.html" (we will have a look at templating later on). It takes two parameters: the template file name and a dictionary containing the data to be passed to the template. In this case, we pass the request object and the data returned by openfile().

So, now when a request is made to the URL for this blog post /post/blog-creation, the post_name path parameter will be captured and used to retrieve the corresponding post data. The HTML template "post.html" will be rendered, and the response will be returned to the client.

HTML templating

Now that the foundation for the blog is ready to go by setting up the backend with FastAPI we can have a look at HTML templating and the way is used it to create my blog's web pages.

While plain HTML is always static, HTML templates contain a mix of HTML markup and placeholders for dynamic content. They provide a structured and standardized way to define the structure and layout of web pages. HTML templates separate the presentation layer from the underlying logic of an application, allowing developers to generate dynamic content by replacing placeholders with actual data.

HTML templates typically include static content, such as headers, footers, navigation menus, and other components that remain consistent across multiple pages. They also incorporate placeholders, known as template variables or tags, which are used to inject dynamic data at runtime. These placeholders are typically enclosed within delimiters like {{ variable }} or {% code block %}.

By utilizing HTML templates, I was able create reusable components, reduce code duplication, and ensure consistent styling and layout across all pages of the blog. Templates facilitate the maintenance and update process, as changes made to a template automatically propagate to all pages that utilize it. This separation of concerns between the structure (HTML template) and the data (dynamic content) enhances the maintainability and flexibility of web applications.

For my blog I chose Jinja2 as the templating engine since it seemed to work really well in combination with FastAPI. FastAPI's templates directory and Jinja2's template loading mechanisms complement each other flawlessly. FastAPI's TemplateResponse class, combined with Jinja2's environment and rendering capabilities, simplifies the process of rendering dynamic HTML responses effortlessly. Additionally Jinja2 allow the insertion of dynamic HTML elements in an almost pythonic way which I found really easy to get into. Lets have a look at the HTML template for the index page of this blog. Note: In this modified version, all the CSS classes have been removed from the HTML elements, providing a simpler structure without any specific styling.

{% extends "base.html" %}

{% block content %}

    <main>
        <div>
            <div>
                <div>
                    <img src="{{ url_for('static', path='/images/mimicrii_logo.png') }}" alt="Logo">
                    <div></div>
                </div>
                <div>
                <h1>hendrik.page</h1>
                <p>my coding journey</p>
                </div>
            </div>

            {% for post in posts %}
                <div>
                    <div>
                        <a href="post/{{ post.metadata.page_name }}">
                            <h2>{{ post.metadata.title }}</h2>
                        </a>
                        <p>{{ post.metadata.date }}</p>
                    </div>
                    <p>{{ post.metadata.short_description }}</p>
                    <div>
						{% for tag in post.metadata.tags %}
							<a>{{ tag }}</a>
						{% endfor %}
                    </div>
                </div>
            {% endfor %}

        </div>
    </main>

{% endblock %}
  1. {% extends "base.html" %}: We use the extends tag to extend a base template named "base.html". The base template serves as the main layout and contains common elements shared across multiple pages like the HTML head aswell as the navbar and footer.

  2. {% block content %} and {% endblock %}: We use these tags to define a block named "content" where the specific content for this template will be inserted. The content within this block will replace the corresponding block in the base template.

  3. The {% for post in posts %} loop iterates over a list of posts returned by the respective request handler function. For each post, we generate a block of HTML containing information of the post such as metadata, post title and date. The anchor <a> wrapping the <h2> allows for linking short the description tile on the index page to the full post page. Finally the {% for tag in post.metadata.tags %} loop is used to iterate over the post's tags. Notice how every for loop has to be closed with a {% endfor %} tag. This example demonstrates how easy it is to integrate the content of python elements such as lists into our html templates to make the web page more dynamic.

Overall, HTML templates are essential tools for building dynamic and interactive web applications. They enable developers to create visually appealing and functional web pages while maintaining a clean separation between presentation and application logic.

Styling with Tailwind CSS

As a newbie to web development, finding the right CSS framework to style my website was a daunting task. As I did not want to invest too much time into styling my blog while still keeping some kind of flexibility, Tailwind CSS proved to be an excellent choice, simplifying the styling process while offering immense flexibility. Below we will explore why Tailwind CSS is a great option for beginners to streamline the styling workflow.

  1. Intuitive and Easy-to-Use: Tailwind CSS embraces a utility-first approach, providing a comprehensive set of pre-defined utility classes that can be directly applied to HTML elements. This eliminates the need to write custom CSS rules from scratch, making it beginner-friendly as well as time efficient. With Tailwind's descriptive class names, I was able to understand and apply styling without diving into complex CSS syntax. For example, generating a round button with a blue background that changes colour on hovering is as easy as assigning three utility classes to the corresponding button-tag. <button class="bg-blue-700 rounded-full hover:bg-red-700">

  2. Rapid Prototyping and Productivity: With Tailwind CSS, I was able to rapidly prototype and iterate on designs. Its extensive library of utility classes allows for quick modifications, such as changing colours, adjusting margins, or aligning elements. This speed and flexibility enabled me to focus on experimenting and refining the visual aspects of my blog efficiently.

  3. Responsive Design Made Easy: Tailwind CSS provides a mobile-first approach, making it straightforward to create responsive designs. By using responsive utility classes like sm, md, lg, and xl, you can adjust styles based on different screen sizes without complex media queries. This simplifies the process of making websites look great across various devices.

  4. Extensibility and Customization: Tailwind CSS offers a highly customizable experience. I was able extend the existing colour palette by my own custom colours by simply editing the tailwind.config.js file like this:

module.exports = {  
  content: ["./templates/**/*.{html,js}"],  
  theme: {  
    extend: {  
      colors: {  
        'custom-bg-1':'#192025',  
        'custom-bg-2':'#2b333e',  
        'custom-font-color':'#f2dfbf',  
        'custom-accent-1':'#742629',  
        'custom-accent-2':'#e5644a',  
        'custom-accent-3':'#fdbf49',  
        'custom-accent-4':'#e79d4b'  
      }  
    },  
  },  
  plugins: [],  
  } 

However, adding custom colours is only a small part of all the customisation you can do to achieve your desired design. From adding, modifying or removing utility classes to theme customisation or plugins that add new features, utility classes, components or even entire design systems to Tailwind CSS. I added a plugin called prose that adds utility classes to format blog posts like this one.

In conclusion: Tailwind has been an ideal companion in the development of this blog, providing an intuitive and efficient approach to styling my blog. With its easy-to-use utility classes, rapid prototyping capabilities, built-in responsiveness, consistency and extensibility, Tailwind enabled me to create a visually appealing and professional-looking website. By incorporating Tailwind into your projects, you can increase your productivity, streamline your styling workflow, and gain confidence in creating eye-catching websites.

Conclusion

My journey of building a website with no previous knowledge of web development has been a great learning experience. From setting up the development environment to deploying the website, I have gained some very useful skills and knowledge. I look forward to more web development projects to come.