Django Web Development for Beginners

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 django

Creating 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 runserver

Visit 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 blog

Now 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 migrate

Views (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/blog

Create 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 createsuperuser

Follow 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 title

Update 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_related and prefetch_related for 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:

  1. Set DEBUG = False in settings
  2. Configure allowed hosts
  3. Set up a production database (PostgreSQL recommended)
  4. Configure static files serving
  5. Set up a web server (Nginx) and WSGI server (Gunicorn)

External Resources:

Related Tutorials:

Last updated on