Django Web Development for Beginners
Django is a high-level Python web framework that enables rapid development of secure and maintainable websites. Built by experienced developers, Django takes care of much of the hassle of web development, so you can focus on writing your app without needing to reinvent the wheel.
What is Django?
Django follows the Model-View-Template (MVT) architectural pattern and includes everything you need to build web applications out of the box. It’s often described as a “batteries-included” framework because it comes with authentication, admin panels, ORM, and many other features built-in.
Why Choose Django?
- Rapid Development: Build complex applications quickly
- Secure: Built-in protection against common security threats
- Scalable: Used by major companies like Instagram and Spotify
- Versatile: Can build anything from simple APIs to complex web applications
- Great Documentation: Comprehensive guides and tutorials
Installing Django
Before you start, make sure you have Python 3.8 or higher installed. It’s recommended to use a virtual environment for Django projects.
# Create a virtual environment
python -m venv django_env
# Activate the virtual environment
# On Windows:
django_env\Scripts\activate
# On macOS/Linux:
source django_env/bin/activate
# Install Django
pip install djangoCreating Your First Django Project
Let’s create a simple blog application to understand Django’s core concepts.
Step 1: Create a New Project
# Create a new Django project
django-admin startproject myblog
# Navigate into the project directory
cd myblog
# Create the database tables
python manage.py migrate
# Start the development server
python manage.py runserverVisit http://127.0.0.1:8000/ in your browser. You should see the Django welcome page.
Step 2: Create an App
In Django, a project is made up of multiple apps. Each app handles a specific functionality.
# Create a blog app
python manage.py startapp blogNow open myblog/settings.py and add your new app to the INSTALLED_APPS list:
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'blog', # Add this line
]Understanding Django’s MVT Pattern
Models (Database Layer)
Models define the structure of your data. Let’s create a simple blog post model.
Edit blog/models.py:
from django.db import models
from django.contrib.auth.models import User
from django.utils import timezone
class Post(models.Model):
title = models.CharField(max_length=200)
content = models.TextField()
author = models.ForeignKey(User, on_delete=models.CASCADE)
created_at = models.DateTimeField(default=timezone.now)
updated_at = models.DateTimeField(auto_now=True)
published = models.BooleanField(default=False)
def __str__(self):
return self.title
class Meta:
ordering = ['-created_at']Now create and apply the database migrations:
python manage.py makemigrations
python manage.py migrateViews (Logic Layer)
Views handle the business logic and process user requests. Create views to display blog posts.
Edit blog/views.py:
from django.shortcuts import render, get_object_or_404
from .models import Post
def post_list(request):
"""View to display all published blog posts"""
posts = Post.objects.filter(published=True)
return render(request, 'blog/post_list.html', {'posts': posts})
def post_detail(request, pk):
"""View to display a single blog post"""
post = get_object_or_404(Post, pk=pk, published=True)
return render(request, 'blog/post_detail.html', {'post': post})
def post_create(request):
"""View to create a new blog post"""
if request.method == 'POST':
title = request.POST.get('title')
content = request.POST.get('content')
if title and content:
post = Post.objects.create(
title=title,
content=content,
author=request.user
)
return redirect('post_detail', pk=post.pk)
return render(request, 'blog/post_create.html')Templates (Presentation Layer)
Templates are HTML files with Django template language for dynamic content.
Create the necessary directories and files:
mkdir -p blog/templates/blogCreate blog/templates/blog/base.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% block title %}My Blog{% endblock %}</title>
<style>
body { font-family: Arial, sans-serif; max-width: 800px; margin: 0 auto; padding: 20px; }
.post { border-bottom: 1px solid #ddd; padding: 20px 0; }
.post h2 { margin: 0 0 10px 0; }
.post-meta { color: #666; font-size: 0.9em; margin-bottom: 10px; }
.btn { background: #007cba; color: white; padding: 8px 16px; text-decoration: none; border-radius: 4px; }
</style>
</head>
<body>
<header>
<h1><a href="{% url 'post_list' %}">My Blog</a></h1>
<nav>
<a href="{% url 'post_list' %}">Home</a> |
<a href="{% url 'post_create' %}">New Post</a>
</nav>
</header>
<main>
{% block content %}
{% endblock %}
</main>
</body>
</html>Create blog/templates/blog/post_list.html:
{% extends 'blog/base.html' %}
{% block title %}Blog Posts - My Blog{% endblock %}
{% block content %}
<h2>Latest Blog Posts</h2>
{% if posts %}
{% for post in posts %}
<div class="post">
<h2><a href="{% url 'post_detail' post.pk %}">{{ post.title }}</a></h2>
<div class="post-meta">
By {{ post.author.username }} on {{ post.created_at|date:"F d, Y" }}
</div>
<p>{{ post.content|truncatewords:30 }}</p>
</div>
{% endfor %}
{% else %}
<p>No blog posts available.</p>
{% endif %}
{% endblock %}Create blog/templates/blog/post_detail.html:
{% extends 'blog/base.html' %}
{% block title %}{{ post.title }} - My Blog{% endblock %}
{% block content %}
<div class="post">
<h2>{{ post.title }}</h2>
<div class="post-meta">
By {{ post.author.username }} on {{ post.created_at|date:"F d, Y" }}
{% if post.updated_at != post.created_at %}
(Updated: {{ post.updated_at|date:"F d, Y" }})
{% endif %}
</div>
<div>
{{ post.content|linebreaks }}
</div>
</div>
<p>
<a href="{% url 'post_list' %}" class="btn">← Back to Posts</a>
</p>
{% endblock %}Create blog/templates/blog/post_create.html:
{% extends 'blog/base.html' %}
{% block title %}Create Post - My Blog{% endblock %}
{% block content %}
<h2>Create New Blog Post</h2>
<form method="post">
{% csrf_token %}
<div>
<label for="title">Title:</label><br>
<input type="text" id="title" name="title" required style="width: 100%; padding: 8px;">
</div>
<br>
<div>
<label for="content">Content:</label><br>
<textarea id="content" name="content" required rows="10" style="width: 100%; padding: 8px;"></textarea>
</div>
<br>
<button type="submit" class="btn">Publish Post</button>
<a href="{% url 'post_list' %}" class="btn" style="background: #666;">Cancel</a>
</form>
{% endblock %}URL Configuration
Now we need to map URLs to our views. Create blog/urls.py:
from django.urls import path
from . import views
urlpatterns = [
path('', views.post_list, name='post_list'),
path('post/<int:pk>/', views.post_detail, name='post_detail'),
path('post/new/', views.post_create, name='post_create'),
]Update myblog/urls.py to include the blog app’s URLs:
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('blog.urls')), # Include blog URLs
]Django Admin Interface
Django automatically creates an admin interface for managing your data. Let’s set it up.
First, create a superuser account:
python manage.py createsuperuserFollow the prompts to create your admin account.
Now register your models with the admin. Edit blog/admin.py:
from django.contrib import admin
from .models import Post
@admin.register(Post)
class PostAdmin(admin.ModelAdmin):
list_display = ['title', 'author', 'created_at', 'published']
list_filter = ['published', 'created_at', 'author']
search_fields = ['title', 'content']
list_editable = ['published']
date_hierarchy = 'created_at'
ordering = ['-created_at']Visit http://127.0.0.1:8000/admin/ and log in with your superuser account. You’ll see a professional admin interface where you can manage blog posts.
Adding Forms for Better User Input
While the simple form above works, Django provides powerful form handling capabilities.
Create blog/forms.py:
from django import forms
from .models import Post
class PostForm(forms.ModelForm):
class Meta:
model = Post
fields = ['title', 'content', 'published']
widgets = {
'title': forms.TextInput(attrs={'class': 'form-control'}),
'content': forms.Textarea(attrs={'class': 'form-control', 'rows': 10}),
}
def clean_title(self):
title = self.cleaned_data.get('title')
if len(title) < 5:
raise forms.ValidationError("Title must be at least 5 characters long.")
return titleUpdate blog/views.py to use the form:
from django.shortcuts import render, get_object_or_404, redirect
from .models import Post
from .forms import PostForm
def post_create(request):
"""View to create a new blog post using Django forms"""
if request.method == 'POST':
form = PostForm(request.POST)
if form.is_valid():
post = form.save(commit=False)
post.author = request.user
post.save()
return redirect('post_detail', pk=post.pk)
else:
form = PostForm()
return render(request, 'blog/post_create_form.html', {'form': form})Create blog/templates/blog/post_create_form.html:
{% extends 'blog/base.html' %}
{% block title %}Create Post - My Blog{% endblock %}
{% block content %}
<h2>Create New Blog Post</h2>
<form method="post">
{% csrf_token %}
{% if form.non_field_errors %}
<div style="color: red;">
{% for error in form.non_field_errors %}
<p>{{ error }}</p>
{% endfor %}
</div>
{% endif %}
<div>
<label for="{{ form.title.id_for_label }}">Title:</label>
{{ form.title }}
{% if form.title.errors %}
<div style="color: red;">{{ form.title.errors }}</div>
{% endif %}
</div>
<br>
<div>
<label for="{{ form.content.id_for_label }}">Content:</label>
{{ form.content }}
{% if form.content.errors %}
<div style="color: red;">{{ form.content.errors }}</div>
{% endif %}
</div>
<br>
<div>
{{ form.published }}
<label for="{{ form.published.id_for_label }}">Publish immediately</label>
</div>
<br>
<button type="submit" class="btn">Create Post</button>
<a href="{% url 'post_list' %}" class="btn" style="background: #666;">Cancel</a>
</form>
{% endblock %}Add the new URL pattern to blog/urls.py:
path('post/new-form/', views.post_create_form, name='post_create_form'),User Authentication
Django has built-in authentication. Let’s add login/logout functionality.
Update myblog/urls.py:
from django.contrib import admin
from django.urls import path, include
from django.contrib.auth import views as auth_views
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('blog.urls')),
path('accounts/login/', auth_views.LoginView.as_view(), name='login'),
path('accounts/logout/', auth_views.LogoutView.as_view(), name='logout'),
]Create login template registration/login.html (create templates/registration directory):
{% extends 'blog/base.html' %}
{% block title %}Login - My Blog{% endblock %}
{% block content %}
<h2>Login</h2>
<form method="post">
{% csrf_token %}
{{ form.as_p }}
<button type="submit" class="btn">Login</button>
</form>
<p>Don't have an account? <a href="/admin/register/">Create one here</a></p>
{% endblock %}Update settings to redirect after login:
# In myblog/settings.py
LOGIN_REDIRECT_URL = '/'
LOGOUT_REDIRECT_URL = '/'Static Files
For CSS, JavaScript, and images, Django uses static files.
Update myblog/settings.py:
STATIC_URL = '/static/'
STATICFILES_DIRS = [BASE_DIR / 'static']Create a static directory in your project root and add a CSS file.
Best Practices
Security
- Always use
{% csrf_token %}in forms - Validate user input using Django forms
- Use Django’s built-in authentication
- Keep Django updated to the latest version
Performance
- Use
select_relatedandprefetch_relatedfor database queries - Implement caching for frequently accessed data
- Use Django’s built-in pagination for large lists
Code Organization
- Keep apps focused on single responsibility
- Use class-based views for complex logic
- Separate business logic from views
- Write tests for your applications
Deploying Django
For production, you’ll need to:
- Set
DEBUG = Falsein settings - Configure allowed hosts
- Set up a production database (PostgreSQL recommended)
- Configure static files serving
- Set up a web server (Nginx) and WSGI server (Gunicorn)
External Resources:
Related Tutorials: