Master Flask: Create a Markdown-based Website with Search Functionality
Creating a static file generator using Flask that processes Markdown files with front matter (often called "Graymatter") involves several steps.
This kind of generator is designed to efficiently handle the conversion of Markdown files into HTML format while also extracting and utilizing the Graymatter metadata located at the top of each file.
To successfully create this generator, follow the step-by-step guide below:
Step 1: Setting Up Your Flask Environment
First, you need to set up a Flask environment. If you haven't already, you should install Flask. You also need to install PyYAML
for parsing Graymatter and Markdown
for converting Markdown to HTML.
pip install Flask PyYAML Markdown
Step 2: Create Your Flask Application
Set up a basic Flask application. Create a new Python file, for example, app.py
, and set up your Flask app.
from flask import Flask, render_template
import os
app = Flask(__name__)
@app.route('/')
def index():
# This function will list all Markdown files
return "Welcome to the static file generator!"
if __name__ == "__main__":
app.run(debug=True)
Step 3: Markdown and Graymatter Processing
Create functions to process Markdown files and extract Graymatter. Graymatter is typically at the top of a Markdown file, delimited by ---
.
You can structure your Markdown files like this:
---
title: My First Post
date: 2024-01-26
---
This is the content of my first post.
And here's how you might process these files:
import yaml
from markdown import markdown
def extract_graymatter(file_content):
"""
Extracts the YAML graymatter from the file content.
"""
if file_content.startswith("---"):
parts = file_content.split("---", 2)
graymatter, content = parts[1], parts[2]
metadata = yaml.safe_load(graymatter)
return metadata, content
return {}, file_content
def convert_markdown_to_html(markdown_content):
"""
Converts Markdown content to HTML.
"""
return markdown(markdown_content)
Step 4: Integrating Markdown Processing with Flask Routes
Now, integrate the Markdown processing into your Flask app. For example, you might want to serve each Markdown file as a separate page.
@app.route('/posts/<filename>')
def post(filename):
filepath = os.path.join('posts', filename)
if os.path.exists(filepath):
with open(filepath, 'r') as file:
file_content = file.read()
metadata, markdown_content = extract_graymatter(file_content)
html_content = convert_markdown_to_html(markdown_content)
return render_template('post.html', content=html_content, metadata=metadata)
return "Post not found", 404
Step 5: Create a Template for Rendering Posts
In your Flask templates directory, create a post.html
file to render the HTML content.
<!-- templates/post.html -->
<!DOCTYPE html>
<html>
<head>
<title>{{ metadata.title }}</title>
</head>
<body>
<article>
{{ content | safe }}
</article>
</body>
</html>
Adding a Search Function to Your Markdown Site
To create a search function in your Flask application that searches through all Markdown files' titles, you need to modify your application to read and index the titles from the Markdown files. You'll also add a search form and a view to handle the search requests.
Step 1: Indexing Markdown Files
First, let's create a function that reads all Markdown files in a specific directory (e.g., posts/
) and extracts their titles from the Graymatter:
def index_markdown_files():
markdown_files = {}
posts_directory = 'posts'
for filename in os.listdir(posts_directory):
if filename.endswith('.md'):
filepath = os.path.join(posts_directory, filename)
with open(filepath, 'r') as file:
file_content = file.read()
metadata, _ = extract_graymatter(file_content)
title = metadata.get('title', 'No Title')
markdown_files[title] = filename
return markdown_files
Step 2: Creating the Search Form
You can create a simple search form in HTML. You can place this form in your base template or any specific template where you want the search functionality.
<!-- search_form.html -->
<form action="/search" method="get">
<input type="text" name="query" placeholder="Search...">
<button type="submit">Search</button>
</form>
Step 3: Adding a Search View in Flask
Now, you need to create a route in your Flask app to handle search queries:
@app.route('/search')
def search():
query = request.args.get('query', '')
if query:
indexed_files = index_markdown_files()
search_results = {title: filename for title, filename in indexed_files.items() if query.lower() in title.lower()}
return render_template('search_results.html', results=search_results, query=query)
return "No query provided", 400
Step 4: Creating a Search Results Template
Create a new template to display the search results:
<!-- templates/search_results.html -->
<!DOCTYPE html>
<html>
<head>
<title>Search Results</title>
</head>
<body>
<h1>Search Results for "{{ query }}"</h1>
{% if results %}
<ul>
{% for title, filename in results.items() %}
<li><a href="{{ url_for('post', filename=filename) }}">{{ title }}</a></li>
{% endfor %}
</ul>
{% else %}
<p>No results found</p>
{% endif %}
</body>
</html>
Step 5: Integrating the Search Form
Include the search form in your templates where you want it to appear. You can include it in your base template to make it available site-wide or only in specific templates.
<!-- Include this line in your base.html or other templates -->
{% include 'search_form.html' %}
Running Your Updated Flask App
Run your Flask app again using:
flask run
Final Note
This simple configuration sets up a Flask application that confidently serves static HTML content generated from Markdown files with Graymatter. You have the ability to enhance this configuration by incorporating additional functionalities such as a homepage that showcases all posts, applying CSS for styling, or even integrating a database for more advanced content management.