Fading Coder

One Final Commit for the Last Sprint

Home > Tech > Content

Crafting Frontend Interfaces with Jinja2 for a Flask Q&A Site

Tech 2

A Q&A platform built with Flask relies on mutliple HTML pages for user registration, login, question submission, list display, and detailed view. Each template extends a common layout and uses Jinja2 to inject dynamic content while maintaining a consistent Bootstrap-based styling.

The template below implements a registration form with email, verification code, username, and password fields. The verification button triggers a client-side script to request a code.

{% extends 'layout.html' %}
{% block head %}
    <link rel="stylesheet" href="{{ url_for('static', filename='css/auth.css') }}">
    <script src="{{ url_for('static', filename='js/verification.js') }}" defer></script>
{% endblock %}
{% block title %}Create Account{% endblock %}
{% block body %}
    <h2 class="page-heading">Sign Up</h2>
    <form action="" method="post" novalidate>
        <div class="form-wrapper">
            <div class="mb-3">
                <input type="email" name="user_email" class="form-input" placeholder="Email address" required>
            </div>
            <div class="mb-3 input-group">
                <input type="text" name="verification_token" class="form-input" placeholder="Verification code">
                <button type="button" class="btn-outline" id="get-code-btn">Send Code</button>
            </div>
            <div class="mb-3">
                <input type="text" name="display_name" class="form-input" placeholder="Username" required>
            </div>
            <div class="mb-3">
                <input type="password" name="passwd" class="form-input" placeholder="Password" required>
            </div>
            <div class="mb-3">
                <input type="password" name="passwd2" class="form-input" placeholder="Confirm password" required>
            </div>
            <div class="mb-3">
                <button type="submit" class="btn-primary full-width">Register</button>
            </div>
        </div>
    </form>
{% endblock %}

The login page accepts an identity field (email or username) and a password.

{% extends 'layout.html' %}
{% block head %}
    <link rel="stylesheet" href="{{ url_for('static', filename='css/auth.css') }}">
{% endblock %}
{% block title %}Sign In{% endblock %}
{% block body %}
    <h2 class="page-heading">Log In</h2>
    <form method="post">
        <div class="form-wrapper">
            <div class="mb-3">
                <input type="text" name="identity" class="form-input" placeholder="Email or username" required>
            </div>
            <div class="mb-3">
                <input type="password" name="passwd" class="form-input" placeholder="Password" required>
            </div>
            <div class="mb-3">
                <button type="submit" class="btn-primary full-width">Sign In</button>
            </div>
        </div>
    </form>
{% endblock %}

Posting a question uses a title input and a larger text area for Description.

{% extends 'layout.html' %}
{% block head %}
    <link rel="stylesheet" href="{{ url_for('static', filename='css/editor.css') }}">
{% endblock %}
{% block title %}Ask a Question{% endblock %}
{% block body %}
    <h2 class="page-heading">New Question</h2>
    <form method="post">
        <div class="form-wrapper">
            <div class="mb-3">
                <input type="text" name="topic" class="form-input" placeholder="Question title" required>
            </div>
            <div class="mb-3">
                <textarea name="description" class="form-input" rows="5" placeholder="Describe your question..."></textarea>
            </div>
            <div class="mb-3 text-right">
                <button type="submit" class="btn-primary">Post Question</button>
            </div>
        </div>
    </form>
{% endblock %}

The home page renders a feed of questions with truncated content and metadata, each linking to its detail view.

{% extends 'layout.html' %}
{% block head %}
    <link rel="stylesheet" href="{{ url_for('static', filename='css/feed.css') }}">
{% endblock %}
{% block title %}Home{% endblock %}
{% block body %}
    <section class="question-feed">
        {% for item in questions %}
            <article class="card">
                <div class="card-avatar">
                    <img src="{{ url_for('static', filename='images/default_avatar.png') }}" alt="avatar">
                </div>
                <div class="card-body">
                    <h3 class="card-title"><a href="{{ url_for('qa.view_question', qid=item.id) }}">{{ item.title }}</a></h3>
                    <p class="card-text">{{ item.content[:100] }}...</p>
                    <div class="meta">
                        <span class="author">{{ item.author.display_name }}</span>
                        <span class="timestamp">{{ item.created_at.strftime('%Y-%m-%d %H:%M') }}</span>
                    </div>
                </div>
            </article>
        {% endfor %}
    </section>
{% endblock %}

The detail page shows the full question, an answer submission form, and a threaded list of answers with user information.

{% extends 'layout.html' %}
{% block head %}
    <link rel="stylesheet" href="{{ url_for('static', filename='css/discussion.css') }}">
{% endblock %}
{% block title %}{{ question.title }} - Details{% endblock %}
{% block body %}
    <article>
        <h2 class="page-heading">{{ question.title }}</h2>
        <div class="meta-info">
            <span>Posted by {{ question.author.display_name }}</span>
            <span>on {{ question.created_at.strftime('%Y-%m-%d %H:%M') }}</span>
        </div>
        <hr>
        <div class="question-body">{{ question.content }}</div>
        <hr>
        <h4>Answers ({{ question.comments|length }})</h4>
        <form action="{{ url_for('qa.add_comment') }}" method="post">
            <input type="hidden" name="qid" value="{{ question.id }}">
            <div class="form-wrapper">
                <div class="mb-3">
                    <input type="text" name="reply" class="form-input" placeholder="Write your answer..." required>
                </div>
                <div class="mb-3">
                    <button type="submit" class="btn-primary">Submit Answer</button>
                </div>
            </div>
        </form>
        <ul class="answer-list">
            {% for ans in question.comments %}
                <li class="answer-item">
                    <div class="user-block">
                        <img class="avatar" src="{{ url_for('static', filename='images/default_avatar.png') }}" alt="">
                        <span class="username">{{ ans.author.display_name }}</span>
                        <span class="date">{{ ans.created_at.strftime('%Y-%m-%d %H:%M') }}</span>
                    </div>
                    <p class="answer-text">{{ ans.content }}</p>
                </li>
            {% endfor %}
        </ul>
    </article>
{% endblock %}

Related Articles

Understanding Strong and Weak References in Java

Strong References Strong reference are the most prevalent type of object referencing in Java. When an object has a strong reference pointing to it, the garbage collector will not reclaim its memory. F...

Comprehensive Guide to SSTI Explained with Payload Bypass Techniques

Introduction Server-Side Template Injection (SSTI) is a vulnerability in web applications where user input is improper handled within the template engine and executed on the server. This exploit can r...

Implement Image Upload Functionality for Django Integrated TinyMCE Editor

Django’s Admin panel is highly user-friendly, and pairing it with TinyMCE, an effective rich text editor, simplifies content management significantly. Combining the two is particular useful for bloggi...

Leave a Comment

Anonymous

◎Feel free to join the discussion and share your thoughts.