Views and Templates — Rendering the Article List

In this chapter, you will learn to use Django views to fetch data from the database and, together with templates, render the article card list on the blog homepage.


The role of views (View)

Views are Django's core logic layer, responsible for:

  • Receive user requests (requestparameter)
  • Querying the required data from the database
  • Passing data to templates to render HTML and returning it to the user.

Django views can be written in two ways:Function view (FBV)and class-based views (CBV).

In the first eight chapters, we prioritize FBV (Function-Based Views)—it is more intuitive and suitable for beginners to understand the request-handling process.


Function View Basics

A Django view function receivesrequestParameters and return valuesHttpResponseObject.

Example

# File path: blog/views.py
from django.shortcuts import render
from .models import Post

def index(request):
    """Blog home page: displays all articles"""
    # Query all articles from the database (in reverse chronological order)
    posts = Post.objects.all().order_by('-created_at')

    # The three parameters of render:
    # 1. request — request object
    # 2. Template path — blog/index.html
    # 3. context dictionary — data passed to the template
    context = {
        'posts': posts,
        'title': 'EXAMPLE Blog - Home'
    }
    return render(request, 'blog/index.html', context)

render()It is the most commonly used shortcut function in Django, combining: loading templates → injecting data → generating HTML → returning responses.


Django template syntax

Django template syntax is very similar to Vue3 template syntax, but there are some key differences.

PurposeDjango templatesVue3 template
Output variables{{ variable }}{{ variable }}
Loops{% for item in list %}...{% endfor %}v-for="item in list"
Conditions{% if cond %}...{% endif %}v-if="cond"
URL Generation{% url 'name' %}router-link to="name"
Inherit Layout{% extends 'base.html' %}Component Nesting

Key difference: Django templates use{% %}Wrapping control tags; Vue3 usesv-directives. In Django templates,Server-siderendering—HTML is generated and then sent to the browser; Vue3 performsBrowser-siderendering—the browser receives JS and then assembles the HTML.


Template inheritance: base.html

When multiple pages share the same navigation bar and footer, useTemplate inheritanceAvoid duplication.

The parent template defines blocks, and child templates fill in specific content.

Create the base.html parent template

Example

<!-- File path: blog/templates/blog/base.html -->
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>{% block title %}EXAMPLE Blog{% endblock %}</title>
    <style>
        * { margin: 0; padding: 0; box-sizing: border-box; }
        body {
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
            background: #f5f5f5;
            color: #333;
        }
        .navbar {
            display: flex; justify-content: space-between; align-items: center;
            padding: 16px 40px; background: #fff;
            box-shadow: 0 2px 8px rgba(0,0,0,0.1);
        }
        .navbar .logo { font-size: 22px; font-weight: bold; color: #2c3e50; text-decoration: none; }
        .navbar a { margin-left: 20px; text-decoration: none; color: #333; }
        .container { max-width: 960px; margin: 40px auto; padding: 0 20px; }
        .footer { text-align: center; padding: 20px; color: #999; border-top: 1px solid #eee; }
        .section-title { font-size: 24px; margin-bottom: 20px; }
    </style>
    {% block extra_head %}{% endblock %}
</head>
<body>
    <!-- Navigation bar -->
    <header class="navbar">
        <a href="/" class="logo">EXAMPLE Blog</a>
        <nav>
            <a href="/">Home</a>
            <a href="#">About</a>
        </nav>
    </header>

    <!-- Main content area (populated by child template) -->
    <main class="container">
        {% block content %}
        {% endblock %}
    </main>

    <!-- Footer -->
    <footer class="footer">
        <p>© 2024 EXAMPLE Blog. Powered by Django.</p>
    </footer>
</body>
</html>

Create the index.html child template

Example

<!-- File path: blog/templates/blog/index.html -->
<!-- extends must be at the very beginning -->
{% extends 'blog/base.html' %}

{% block title %}{{ title }}{% endblock %}

{% block content %}
<h2 class="section-title">Latest Articles</h2>

{% if posts %}
    <div class="article-grid">
        {% for post in posts %}
        <div class="article-card">
            <div class="card-content">
                <span class="card-category">{{ post.category.name }}</span>
                <h3>{{ post.title }}</h3>
                <p>{{ post.summary|truncatechars:80 }}</p>
                <span class="card-date">{{ post.created_at|date:"Y-m-d" }}</span>
            </div>
        </div>
        {% endfor %}
    </div>
{% else %}
    <p class="empty-tip">No articles yet, please stay tuned.</p>
{% endif %}
{% endblock %}

Several knowledge points used in templates:

SyntaxMeaning
{% extends 'blog/base.html' %}Inheriting the parent template must be written on the first line
{% block content %}...{% endblock %}Fill the content block defined in the parent template.
{{ post.category.name }}Accessing the properties of related objects through foreign keys
{{ post.summary|truncatechars:80 }}Filter: truncate to the first 80 characters
{{ post.created_at|date:"Y-m-d" }}Filter: format the date

In Django templates,FilterUse Pipe Symbol|Indicates, placed after a variable, used for formatted output. Common filters:date、truncatechars、length、default。


Adding styles to the article list

Append the following CSS to the style tag in base.html:

Example

/* Article card grid layout */
.article-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
    gap: 24px;
}

.article-card {
    background: #fff;
    border-radius: 12px;
    overflow: hidden;
    box-shadow: 0 2px 12px rgba(0,0,0,0.08);
    transition: transform 0.2s, box-shadow 0.2s;
}

.article-card:hover {
    transform: translateY(-4px);
    box-shadow: 0 8px 24px rgba(0,0,0,0.12);
}

.card-content {
    padding: 20px;
}

.card-category {
    display: inline-block;
    padding: 2px 10px;
    background: #E8EEF5;
    color: #3D71A6;
    border-radius: 12px;
    font-size: 12px;
    margin-bottom: 8px;
}

.article-card h3 {
    font-size: 18px;
    margin-bottom: 8px;
    color: #222;
}

.article-card p {
    font-size: 14px;
    color: #666;
    line-height: 1.6;
    margin-bottom: 12px;
}

.card-date {
    font-size: 12px;
    color: #999;
}

.empty-tip {
    text-align: center;
    color: #999;
    padding: 60px 0;
    font-size: 16px;
}

Hands-on: Verify the homepage effect

Make sure the following points are done:

  • Post and Category models are defined in models.py and have been migrated
  • At least 3-5 articles have been entered in the Admin backend (assigned to different categories).
  • The index function in views.py queries articles from the database and passes them to the template.
  • The / route in urls.py points to the index view

Start the server and visit the homepage. You should see the article card list dynamically rendered from database data.


Chapter summary

In this chapter, you have mastered the core coordination of Django views and templates: views.py queries data from the database and passes it into context, render() renders templates, base.html parent template defines layout + blocks, child templates extend and fill in content, Django template syntax ({{ }}, {% %}, filters).

The blog homepage is now database-driven, no longer hard-coded.

other extensions