Django Templates and Frontend Integration
50 minDjango templates provide a powerful template language for creating dynamic HTML pages with inheritance, filters, and tags. The template system separates presentation logic from business logic, making it easier to maintain and update your application's UI. Templates use a simple syntax that's easy to learn but powerful enough for complex scenarios.
Template inheritance allows you to create base templates and extend them for consistent layouts across your application. This DRY principle reduces code duplication and makes it easier to maintain consistent styling and structure. Child templates can override specific blocks defined in parent templates, providing flexibility while maintaining consistency.
Django's template language includes built-in filters for common operations and custom template tags for complex logic. Filters transform template variables (e.g., {{ name|upper }}), while tags perform more complex operations like loops and conditionals. Understanding the difference between filters and tags, and when to use each, is crucial for effective template development.
Integrating Django with modern frontend frameworks and handling static files properly is essential for production applications. Django's static files system handles CSS, JavaScript, and images, with support for compression and caching in production. Understanding STATIC_URL, STATIC_ROOT, and collectstatic is crucial for deploying Django applications.
Template context processors provide global context variables available to all templates, reducing the need to pass common data in every view. Custom template tags and filters allow you to extend Django's template language with application-specific functionality. These features enable you to build complex, maintainable templates while keeping business logic in views.
Django's template system includes security features like automatic HTML escaping to prevent XSS attacks. The {% csrf_token %} tag provides CSRF protection for forms. Understanding these security features and using them correctly is essential for building secure web applications.
Key Concepts
- Django templates use inheritance for consistent layouts.
- Template filters transform variables (e.g., {{ name|upper }}).
- Template tags perform complex operations (loops, conditionals).
- Static files (CSS, JS, images) are handled separately from templates.
- Template context processors provide global context variables.
Learning Objectives
Master
- Creating base templates and extending them
- Using template filters and tags effectively
- Handling static files in development and production
- Creating custom template tags and filters
Develop
- Understanding template inheritance patterns
- Designing maintainable template structures
- Integrating Django with modern frontend frameworks
Tips
- Use {% load static %} at the top of templates that use static files.
- Use {% extends %} and {% block %} for template inheritance.
- Use {% include %} for reusable template components.
- Use {% url %} tag for URL resolution instead of hardcoding paths.
Common Pitfalls
- Not using template inheritance, causing code duplication.
- Putting business logic in templates instead of views.
- Not escaping user input, causing XSS vulnerabilities.
- Forgetting to run collectstatic in production, causing missing static files.
Summary
- Django templates provide powerful template language with inheritance.
- Template inheritance enables consistent layouts across applications.
- Filters and tags extend template functionality.
- Proper static file handling is essential for production.
Exercise
Create comprehensive Django templates for the blog application with proper inheritance, responsive design, and modern UI components.
<!-- base.html - Base template -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% block title %}Django Blog{% endblock %}</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="{% static 'css/style.css' %}">
{% block extra_css %}{% endblock %}
</head>
<body>
<!-- Navigation -->
<nav class="navbar navbar-expand-lg navbar-dark bg-dark">
<div class="container">
<a class="navbar-brand" href="{% url 'blog:post_list' %}">Django Blog</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav me-auto">
<li class="nav-item">
<a class="nav-link" href="{% url 'blog:post_list' %}">Home</a>
</li>
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-bs-toggle="dropdown">
Categories
</a>
<ul class="dropdown-menu">
{% for category in categories %}
<li><a class="dropdown-item" href="{% url 'blog:category_posts' category.slug %}">{{ category.name }}</a></li>
{% endfor %}
</ul>
</li>
</ul>
<form class="d-flex" method="get" action="{% url 'blog:post_search' %}">
<input class="form-control me-2" type="search" name="q" placeholder="Search posts..." value="{{ query }}">
<button class="btn btn-outline-light" type="submit">Search</button>
</form>
<ul class="navbar-nav ms-3">
{% if user.is_authenticated %}
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" id="userDropdown" role="button" data-bs-toggle="dropdown">
{{ user.username }}
</a>
<ul class="dropdown-menu">
<li><a class="dropdown-item" href="{% url 'blog:post_create' %}">New Post</a></li>
<li><a class="dropdown-item" href="{% url 'logout' %}">Logout</a></li>
</ul>
</li>
{% else %}
<li class="nav-item">
<a class="nav-link" href="{% url 'login' %}">Login</a>
</li>
{% endif %}
</ul>
</div>
</div>
</nav>
<!-- Main Content -->
<main class="container my-4">
{% if messages %}
{% for message in messages %}
<div class="alert alert-{{ message.tags }} alert-dismissible fade show" role="alert">
{{ message }}
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
</div>
{% endfor %}
{% endif %}
{% block content %}{% endblock %}
</main>
<!-- Footer -->
<footer class="bg-dark text-light py-4 mt-5">
<div class="container text-center">
<p>© 2024 Django Blog. All rights reserved.</p>
</div>
</footer>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
{% block extra_js %}{% endblock %}
</body>
</html>
<!-- post_list.html - Post list template -->
{% extends 'blog/base.html' %}
{% load static %}
{% block title %}Blog Posts{% endblock %}
{% block content %}
<div class="row">
<div class="col-lg-8">
<h1 class="mb-4">Blog Posts</h1>
{% if query %}
<div class="alert alert-info">
Search results for: "{{ query }}"
<a href="{% url 'blog:post_list' %}" class="float-end">Clear search</a>
</div>
{% endif %}
{% if posts %}
{% for post in posts %}
<article class="card mb-4">
{% if post.featured_image %}
<img src="{{ post.featured_image.url }}" class="card-img-top" alt="{{ post.title }}">
{% endif %}
<div class="card-body">
<h2 class="card-title">
<a href="{{ post.get_absolute_url }}" class="text-decoration-none">{{ post.title }}</a>
</h2>
<p class="card-text text-muted">
By {{ post.author.username }} | {{ post.published_at|date:"F j, Y" }} | {{ post.category.name }}
</p>
<p class="card-text">{{ post.excerpt|default:post.content|truncatewords:30 }}</p>
<div class="d-flex justify-content-between align-items-center">
<a href="{{ post.get_absolute_url }}" class="btn btn-primary">Read More</a>
<small class="text-muted">{{ post.views }} views</small>
</div>
</div>
</article>
{% endfor %}
<!-- Pagination -->
{% if is_paginated %}
<nav aria-label="Page navigation">
<ul class="pagination justify-content-center">
{% if page_obj.has_previous %}
<li class="page-item">
<a class="page-link" href="?page=1">First</a>
</li>
<li class="page-item">
<a class="page-link" href="?page={{ page_obj.previous_page_number }}">Previous</a>
</li>
{% endif %}
<li class="page-item active">
<span class="page-link">{{ page_obj.number }} of {{ page_obj.paginator.num_pages }}</span>
</li>
{% if page_obj.has_next %}
<li class="page-item">
<a class="page-link" href="?page={{ page_obj.next_page_number }}">Next</a>
</li>
<li class="page-item">
<a class="page-link" href="?page={{ page_obj.paginator.num_pages }}">Last</a>
</li>
{% endif %}
</ul>
</nav>
{% endif %}
{% else %}
<div class="alert alert-warning">
No posts found. {% if query %}Try a different search term.{% endif %}
</div>
{% endif %}
</div>
<!-- Sidebar -->
<div class="col-lg-4">
<div class="card">
<div class="card-header">
<h5>Categories</h5>
</div>
<div class="card-body">
<ul class="list-unstyled">
{% for category in categories %}
<li class="mb-2">
<a href="{% url 'blog:category_posts' category.slug %}" class="text-decoration-none">
{{ category.name }} ({{ category.posts.count }})
</a>
</li>
{% endfor %}
</ul>
</div>
</div>
</div>
</div>
{% endblock %}
<!-- post_detail.html - Post detail template -->
{% extends 'blog/base.html' %}
{% load static %}
{% block title %}{{ post.title }}{% endblock %}
{% block content %}
<article class="blog-post">
<header class="mb-4">
<h1 class="display-4">{{ post.title }}</h1>
<div class="text-muted mb-3">
<span>By {{ post.author.username }}</span> |
<span>{{ post.published_at|date:"F j, Y" }}</span> |
<span>Category: <a href="{% url 'blog:category_posts' post.category.slug %}">{{ post.category.name }}</a></span> |
<span>{{ post.views }} views</span>
</div>
{% if post.tags.all %}
<div class="mb-3">
{% for tag in post.tags.all %}
<span class="badge bg-secondary me-1">{{ tag.name }}</span>
{% endfor %}
</div>
{% endif %}
</header>
{% if post.featured_image %}
<img src="{{ post.featured_image.url }}" class="img-fluid rounded mb-4" alt="{{ post.title }}">
{% endif %}
<div class="blog-content">
{{ post.content|linebreaks }}
</div>
{% if user == post.author %}
<div class="mt-4">
<a href="{% url 'blog:post_update' post.slug %}" class="btn btn-primary me-2">Edit Post</a>
<a href="{% url 'blog:post_delete' post.slug %}" class="btn btn-danger">Delete Post</a>
</div>
{% endif %}
</article>
<!-- Comments Section -->
<section class="comments mt-5">
<h3>Comments ({{ comments.count }})</h3>
{% if user.is_authenticated %}
<div class="card mb-4">
<div class="card-body">
<h5 class="card-title">Add a Comment</h5>
<form method="post">
{% csrf_token %}
{{ comment_form.as_p }}
<button type="submit" class="btn btn-primary">Submit Comment</button>
</form>
</div>
</div>
{% else %}
<div class="alert alert-info">
Please <a href="{% url 'login' %}">login</a> to leave a comment.
</div>
{% endif %}
{% if comments %}
{% for comment in comments %}
<div class="card mb-3">
<div class="card-body">
<div class="d-flex justify-content-between">
<h6 class="card-subtitle mb-2 text-muted">{{ comment.author_name }}</h6>
<small class="text-muted">{{ comment.created_at|date:"F j, Y g:i a" }}</small>
</div>
<p class="card-text">{{ comment.content|linebreaks }}</p>
</div>
</div>
{% endfor %}
{% else %}
<p class="text-muted">No comments yet. Be the first to comment!</p>
{% endif %}
</section>
{% endblock %}
Exercise Tips
- Use {% load static %} before using {% static %} tag for static files.
- Create reusable template components with {% include %} for headers, footers, etc.
- Use template filters for common transformations: {{ post.published_at|date:'F j, Y' }}.
- Implement template caching for expensive operations: {% cache 300 expensive_content %}.