Master Django project structure, models, views, and templates with hands-on web development
Welcome to Django - Python's most popular web framework! Django powers some of the world's largest websites including Instagram, Pinterest, and Mozilla.
Why Django?
What You'll Build:
By the end of this course, you'll create a complete e-commerce platform with user authentication, product catalog, shopping cart, and payment processing.
Enterprise Django Environment:
Our Django development environment runs in an isolated container with PostgreSQL database, Redis caching, and production-grade configurations.
Django follows the MVT (Model-View-Template) pattern - a variation of MVC:
Model: Data layer
View: Business logic layer
Template: Presentation layer
URL Configuration: Routes requests to appropriate views
Django projects follow a standardized structure for maintainability:
1myproject/
2āāā myproject/ # Main project directory
3ā āāā __init__.py
4ā āāā settings.py # Project configuration
5ā āāā urls.py # Root URL configuration
6ā āāā wsgi.py # WSGI deployment interface
7ā āāā asgi.py # ASGI deployment interface
8āāā myapp/ # Django application
9ā āāā __init__.py
10ā āāā admin.py # Admin interface configuration
11ā āāā apps.py # App configuration
12ā āāā models.py # Database models
13ā āāā views.py # View functions/classes
14ā āāā urls.py # App URL patterns
15ā āāā tests.py # Unit tests
16ā āāā migrations/ # Database migration files
17āāā templates/ # HTML templates
18āāā static/ # Static files (CSS, JS, images)
19āāā manage.py # Django management scriptBest Practices:
Django's ORM (Object-Relational Mapping) lets you work with databases using Python objects:
Model Definition:
1from django.db import models
2from django.contrib.auth.models import User
3
4class Product(models.Model):
5 name = models.CharField(max_length=200)
6 description = models.TextField()
7 price = models.DecimalField(max_digits=10, decimal_places=2)
8 created_at = models.DateTimeField(auto_now_add=True)
9
10 def __str__(self):
11 return self.name
12
13 class Meta:
14 ordering = ['-created_at']Key Features:
Database Operations:
Product.objects.create(name="Laptop", price=999.99)Product.objects.all(), Product.objects.get(id=1)product.price = 899.99; product.save()product.delete()Build a fully functional blog application demonstrating Django MVT architecture, models, views, templates, and URL routing. This example showcases production-ready Django development patterns.
1# š COMPLETE DJANGO BLOG APPLICATION
2# This demonstrates Django MVT architecture with real-world patterns
3
4# models.py - Data Layer
5from django.db import models
6from django.contrib.auth.models import User
7from django.urls import reverse
8from django.utils import timezone
9
10class Category(models.Model):
11 """Blog post categories for organization"""
12 name = models.CharField(max_length=100, unique=True)
13 slug = models.SlugField(max_length=100, unique=True)
14 description = models.TextField(blank=True)
15 created_at = models.DateTimeField(auto_now_add=True)
16
17 class Meta:
18 verbose_name_plural = "Categories"
19 ordering = ['name']
20
21 def __str__(self):
22 return self.name
23
24 def get_absolute_url(self):
25 return reverse('blog:category', kwargs={'slug': self.slug})
26
27class Post(models.Model):
28 """Main blog post model with full functionality"""
29 STATUS_CHOICES = [
30 ('draft', 'Draft'),
31 ('published', 'Published'),
32 ]
33
34 title = models.CharField(max_length=200)
35 slug = models.SlugField(max_length=200, unique=True)
36 author = models.ForeignKey(User, on_delete=models.CASCADE, related_name='blog_posts')
37 category = models.ForeignKey(Category, on_delete=models.SET_NULL, null=True, blank=True)
38 content = models.TextField()
39 excerpt = models.TextField(max_length=300, blank=True)
40 status = models.CharField(max_length=10, choices=STATUS_CHOICES, default='draft')
41 created_at = models.DateTimeField(auto_now_add=True)
42 updated_at = models.DateTimeField(auto_now=True)
43 published_at = models.DateTimeField(blank=True, null=True)
44
45 # SEO and social media fields
46 meta_description = models.CharField(max_length=160, blank=True)
47 featured_image = models.ImageField(upload_to='blog/images/', blank=True, null=True)
48
49 class Meta:
50 ordering = ['-published_at', '-created_at']
51 indexes = [
52 models.Index(fields=['-published_at']),
53 models.Index(fields=['status']),
54 ]
55
56 def __str__(self):
57 return self.title
58
59 def save(self, *args, **kwargs):
60 """Auto-set published_at when status changes to published"""
61 if self.status == 'published' and not self.published_at:
62 self.published_at = timezone.now()
63 super().save(*args, **kwargs)
64
65 def get_absolute_url(self):
66 return reverse('blog:post_detail', kwargs={'slug': self.slug})
67
68 @property
69 def reading_time(self):
70 """Calculate estimated reading time"""
71 words = len(self.content.split())
72 return max(1, words // 200) # Assume 200 words per minute
73
74class Comment(models.Model):
75 """User comments on blog posts"""
76 post = models.ForeignKey(Post, on_delete=models.CASCADE, related_name='comments')
77 name = models.CharField(max_length=100)
78 email = models.EmailField()
79 content = models.TextField()
80 created_at = models.DateTimeField(auto_now_add=True)
81 is_approved = models.BooleanField(default=False)
82
83 class Meta:
84 ordering = ['created_at']
85
86 def __str__(self):
87 return f'Comment by {self.name} on {self.post.title}'
88
89# views.py - Business Logic Layer
90from django.shortcuts import render, get_object_or_404
91from django.views.generic import ListView, DetailView
92from django.db.models import Q, Count
93from django.core.paginator import Paginator
94from django.http import JsonResponse
95from django.contrib import messages
96
97class PostListView(ListView):
98 """Display paginated list of published blog posts"""
99 model = Post
100 template_name = 'blog/post_list.html'
101 context_object_name = 'posts'
102 paginate_by = 10
103
104 def get_queryset(self):
105 queryset = Post.objects.filter(status='published').select_related('author', 'category')
106
107 # Search functionality
108 search_query = self.request.GET.get('search')
109 if search_query:
110 queryset = queryset.filter(
111 Q(title__icontains=search_query) |
112 Q(content__icontains=search_query)
113 )
114
115 # Category filtering
116 category_slug = self.request.GET.get('category')
117 if category_slug:
118 queryset = queryset.filter(category__slug=category_slug)
119
120 return queryset
121
122 def get_context_data(self, **kwargs):
123 context = super().get_context_data(**kwargs)
124 context['categories'] = Category.objects.annotate(
125 post_count=Count('post', filter=Q(post__status='published'))
126 )
127 context['search_query'] = self.request.GET.get('search', '')
128 return context
129
130class PostDetailView(DetailView):
131 """Display individual blog post with comments"""
132 model = Post
133 template_name = 'blog/post_detail.html'
134 context_object_name = 'post'
135 slug_field = 'slug'
136
137 def get_queryset(self):
138 return Post.objects.filter(status='published').select_related('author', 'category')
139
140 def get_context_data(self, **kwargs):
141 context = super().get_context_data(**kwargs)
142 # Get approved comments
143 context['comments'] = self.object.comments.filter(is_approved=True)
144 # Related posts
145 context['related_posts'] = Post.objects.filter(
146 category=self.object.category,
147 status='published'
148 ).exclude(id=self.object.id)[:3]
149 return context
150
151def category_posts(request, slug):
152 """Display posts filtered by category"""
153 category = get_object_or_404(Category, slug=slug)
154 posts = Post.objects.filter(
155 category=category,
156 status='published'
157 ).select_related('author')
158
159 paginator = Paginator(posts, 10)
160 page_number = request.GET.get('page')
161 page_obj = paginator.get_page(page_number)
162
163 context = {
164 'category': category,
165 'posts': page_obj,
166 'page_obj': page_obj
167 }
168 return render(request, 'blog/category_posts.html', context)
169
170# urls.py - URL Routing
171from django.urls import path
172from . import views
173
174app_name = 'blog'
175
176urlpatterns = [
177 path('', views.PostListView.as_view(), name='post_list'),
178 path('post/<slug:slug>/', views.PostDetailView.as_view(), name='post_detail'),
179 path('category/<slug:slug>/', views.category_posts, name='category'),
180]
181
182# admin.py - Django Admin Configuration
183from django.contrib import admin
184from .models import Category, Post, Comment
185
186@admin.register(Category)
187class CategoryAdmin(admin.ModelAdmin):
188 list_display = ['name', 'slug', 'created_at']
189 prepopulated_fields = {'slug': ('name',)}
190 search_fields = ['name']
191
192@admin.register(Post)
193class PostAdmin(admin.ModelAdmin):
194 list_display = ['title', 'author', 'category', 'status', 'created_at', 'published_at']
195 list_filter = ['status', 'category', 'created_at']
196 search_fields = ['title', 'content']
197 prepopulated_fields = {'slug': ('title',)}
198 date_hierarchy = 'created_at'
199
200 fieldsets = (
201 ('Content', {
202 'fields': ('title', 'slug', 'author', 'category', 'content', 'excerpt')
203 }),
204 ('Publishing', {
205 'fields': ('status', 'published_at')
206 }),
207 ('SEO', {
208 'fields': ('meta_description', 'featured_image'),
209 'classes': ('collapse',)
210 }),
211 )
212
213@admin.register(Comment)
214class CommentAdmin(admin.ModelAdmin):
215 list_display = ['name', 'post', 'created_at', 'is_approved']
216 list_filter = ['is_approved', 'created_at']
217 search_fields = ['name', 'email', 'content']
218 actions = ['approve_comments']
219
220 def approve_comments(self, request, queryset):
221 queryset.update(is_approved=True)
222 messages.success(request, f'{queryset.count()} comments approved.')
223 approve_comments.short_description = "Approve selected comments"
224
225# Template Example: blog/post_list.html
226template_content = '''
227<!-- Base template with Django template language -->
228{% extends 'base.html' %}
229{% load static %}
230
231{% block title %}Blog Posts{% endblock %}
232
233{% block content %}
234<div class="blog-container">
235 <header class="blog-header">
236 <h1>Latest Blog Posts</h1>
237
238 <!-- Search Form -->
239 <form method="get" class="search-form">
240 <input type="text" name="search"
241 value="{{ search_query }}"
242 placeholder="Search posts...">
243 <button type="submit">Search</button>
244 </form>
245 </header>
246
247 <!-- Categories Sidebar -->
248 <aside class="categories">
249 <h3>Categories</h3>
250 <ul>
251 {% for category in categories %}
252 <li>
253 <a href="{% url 'blog:category' category.slug %}">
254 {{ category.name }} ({{ category.post_count }})
255 </a>
256 </li>
257 {% endfor %}
258 </ul>
259 </aside>
260
261 <!-- Posts Grid -->
262 <main class="posts-grid">
263 {% for post in posts %}
264 <article class="post-card">
265 {% if post.featured_image %}
266 <img src="{{ post.featured_image.url }}" alt="{{ post.title }}">
267 {% endif %}
268
269 <div class="post-content">
270 <h2>
271 <a href="{{ post.get_absolute_url }}">{{ post.title }}</a>
272 </h2>
273
274 <div class="post-meta">
275 <span>By {{ post.author.get_full_name|default:post.author.username }}</span>
276 <span>{{ post.published_at|date:"M d, Y" }}</span>
277 <span>{{ post.reading_time }} min read</span>
278 {% if post.category %}
279 <span>in {{ post.category.name }}</span>
280 {% endif %}
281 </div>
282
283 <p>{{ post.excerpt|default:post.content|truncatewords:30 }}</p>
284
285 <a href="{{ post.get_absolute_url }}" class="read-more">
286 Read More ā
287 </a>
288 </div>
289 </article>
290 {% empty %}
291 <p>No posts found.</p>
292 {% endfor %}
293 </main>
294
295 <!-- Pagination -->
296 {% if is_paginated %}
297 <nav class="pagination">
298 <span class="pagination-info">
299 Page {{ page_obj.number }} of {{ page_obj.paginator.num_pages }}
300 </span>
301
302 {% if page_obj.has_previous %}
303 <a href="?page={{ page_obj.previous_page_number }}">ā Previous</a>
304 {% endif %}
305
306 {% if page_obj.has_next %}
307 <a href="?page={{ page_obj.next_page_number }}">Next ā</a>
308 {% endif %}
309 </nav>
310 {% endif %}
311</div>
312{% endblock %}
313'''
314
315print("š Django Blog Application Complete!")
316print("\nā
Features Implemented:")
317print("⢠MVT Architecture with proper separation")
318print("⢠Category-based organization")
319print("⢠Search functionality")
320print("⢠Pagination with performance optimization")
321print("⢠SEO-friendly URLs and meta fields")
322print("⢠Comment system with moderation")
323print("⢠Django Admin integration")
324print("⢠Template inheritance")
325print("⢠Database relationships and indexes")
326print("⢠Reading time calculation")
327print("\nš Production Ready Features:")
328print("⢠select_related() for query optimization")
329print("⢠Database indexes for performance")
330print("⢠Proper error handling")
331print("⢠Security best practices")
332print("⢠Responsive template structure")š Django Architecture Breakdown:
1. Models Layer (Data):
Category: Organizes blog posts with SEO-friendly slugsPost: Main content model with publishing workflowComment: User engagement with moderation system2. Views Layer (Business Logic):
PostListView: Class-based view with pagination and searchPostDetailView: Individual post display with related contentcategory_posts: Function-based view for category filteringselect_related() prevents N+1 queries3. Templates Layer (Presentation):
{% extends %}{% url %} tag4. Admin Integration:
5. URL Configuration:
app_name6. Production Features:
7. Security & Best Practices:
This example demonstrates professional Django development patterns used in production applications!
Your progress is saved in this browser, on this device only.