Building a Web Application with Django: A Practical Guide

Django is a high-level web framework that facilitates the developmant of interactive websites by handling web requests, database operations, and user management. This guide details the creation of a Learning Log application—an online journal system for tracking knowledge on various subjects.

Project Initialization

To begin, establish a project with a clear specification and a dedicated virtual environment to isolate dependencies.

Project Specification

Define the application's goals and functionality. The Learning Log will allow users to register, log in, create topics, and add log entries related to those topics.

Virtual Environment Setup

Create an isolated Python environment to manage project-specific packages.

python -m venv learning_log_env

Activate the virtual environment:

source learning_log_env/bin/activate

On Windows, use:

learning_log_env\Scripts\activate

Installing Django

With the environment active, install Django.

pip install django

Creating the Django Project

Generate the project structure.

django-admin startproject learning_log_project .

The dot ensures proper directory configuration for deployment.

Database Setup

Django uses a database to store project information. Initialize it with:

python manage.py migrate

This command creates a SQLite database file, db.sqlite3, suitable for development.

Runing the Development Server

Start the server to verify the project.

python manage.py runserver

Visit http://127.0.0.1:8000/ in a browser. If port 8000 is busy, specify another, e.g., runserver 8001.

Creating an Application

Django projects consist of applications. Create one named log_entries.

python manage.py startapp log_entries

This creates directories and files including models.py, admin.py, and views.py.

Defining Data Models

Models define the data structure. In log_entries/models.py:

from django.db import models

class Subject(models.Model):
    """A topic the user is learning about."""
    name = models.CharField(max_length=200)
    created_at = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return self.name

class JournalEntry(models.Model):
    """An entry about a specific subject."""
    subject = models.ForeignKey(Subject, on_delete=models.CASCADE)
    content = models.TextField()
    created_at = models.DateTimeField(auto_now_add=True)

    class Meta:
        verbose_name_plural = 'journal_entries'

    def __str__(self):
        return self.content[:50] + '...'

Activating Models

Add the application to the project's INSTALLED_APPS in learning_log_project/settings.py.

INSTALLED_APPS = [
    'log_entries',
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
]

Create and apply database migrations.

python manage.py makemigrations log_entries
python manage.py migrate

Django Admin Interface

Register models with the admin site in log_entries/admin.py.

from django.contrib import admin
from .models import Subject, JournalEntry

admin.site.register(Subject)
admin.site.register(JournalEntry)

Create a superuser to access the admin site.

python manage.py createsuperuser

Interactive Django Shell

Use the Django shell to interact with data.

python manage.py shell

Example queries:

from log_entries.models import Subject
Subject.objects.all()
for s in Subject.objects.all():
    print(s.id, s.name)

Creating Web Pages

The process involves defining URLs, writing views, and creating templates.

URL Configuration

First, include the app's URLs in the project's main urls.py.

from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include('log_entries.urls')),
]

Create log_entries/urls.py:

from django.urls import path
from . import views

app_name = 'log_entries'
urlpatterns = [
    path('', views.homepage, name='home'),
]

View Functions

Define the view in log_entries/views.py.

from django.shortcuts import render

def homepage(request):
    """Render the application's homepage."""
    return render(request, 'log_entries/home.html')

Templates

Create a template at log_entries/templates/log_entries/home.html.

<!DOCTYPE html>
<html>
<head>
    <title>Learning Log</title>
</head>
<body>
    <h1>Learning Log</h1>
    <p>Track your learning on any topic.</p>
</body>
</html>

Building Additional Pages

Extend the application to list subjects and show entries for a specific subject.

Template Inheritance

Create a base template log_entries/templates/log_entries/base.html.

<!DOCTYPE html>
<html>
<head>
    <title>Learning Log</title>
</head>
<body>
    <p><a href="{% url 'log_entries:home' %}">Home</a></p>
    {% block main_content %}{% endblock %}
</body>
</html>

Child templates extend this base.

Listing All Subjects

Update urls.py:

urlpatterns = [
    path('', views.homepage, name='home'),
    path('subjects/', views.subject_list, name='subject_list'),
]

Create the view:

def subject_list(request):
    """Display all subjects."""
    subjects = Subject.objects.order_by('created_at')
    context = {'subjects': subjects}
    return render(request, 'log_entries/subjects.html', context)

Create the template subjects.html:

{% extends 'log_entries/base.html' %}
{% block main_content %}
    <h2>Subjects</h2>
    <ul>
        {% for subject in subjects %}
            <li>{{ subject.name }}</li>
        {% empty %}
            <li>No subjects have been added yet.</li>
        {% endfor %}
    </ul>
{% endblock %}

Displaying Entries for a Subject

Add a URL pattern for a specific subject.

path('subjects/<int:subject_id>/', views.subject_detail, name='subject_detail'),

Create the view:

def subject_detail(request, subject_id):
    """Show a single subject and all its entries."""
    subject = Subject.objects.get(id=subject_id)
    entries = subject.journalentry_set.order_by('created_at')
    context = {'subject': subject, 'entries': entries}
    return render(request, 'log_entries/subject_detail.html', context)

Create the template subject_detail.html:

{% extends 'log_entries/base.html' %}
{% block main_content %}
    <h2>{{ subject.name }}</h2>
    <p>Entries:</p>
    <ul>
        {% for entry in entries %}
            <li>
                <p>{{ entry.created_at|date:'M d, Y H:i' }}</p>
                <p>{{ entry.content|linebreaks }}</p>
            </li>
        {% empty %}
            <li>No entries for this subject yet.</li>
        {% endfor %}
    </ul>
{% endblock %}

Update the subjects.html template to link to each subject's detail page.

<li>
    <a href="{% url 'log_entries:subject_detail' subject.id %}">{{ subject.name }}</a>
</li>

User Data Input

Create forms to allow users to add new subjects and entries.

Form for Adding a Subject

Create log_entries/forms.py.

from django import forms
from .models import Subject, JournalEntry

class SubjectForm(forms.ModelForm):
    class Meta:
        model = Subject
        fields = ['name']
        labels = {'name': ''}

class EntryForm(forms.ModelForm):
    class Meta:
        model = JournalEntry
        fields = ['content']
        labels = {'content': ''}
        widgets = {'content': forms.Textarea(attrs={'cols': 80})}

View for New Subject

Add a URL and view.

# urls.py
path('new_subject/', views.new_subject, name='new_subject'),
# views.py
from django.shortcuts import render, redirect
from .forms import SubjectForm

def new_subject(request):
    """Add a new subject."""
    if request.method != 'POST':
        form = SubjectForm()
    else:
        form = SubjectForm(data=request.POST)
        if form.is_valid():
            form.save()
            return redirect('log_entries:subject_list')
    context = {'form': form}
    return render(request, 'log_entries/new_subject.html', context)

Create the template new_subject.html.

{% extends 'log_entries/base.html' %}
{% block main_content %}
    <h2>Add a new subject</h2>
    <form action="{% url 'log_entries:new_subject' %}" method="post">
        {% csrf_token %}
        {{ form.as_p }}
        <button name="submit">Add Subject</button>
    </form>
{% endblock %}

Add a link to this page from the subject list template.

View for New Entry

Add a URL and view for creating entries linked to a subject.

# urls.py
path('new_entry/<int:subject_id>/', views.new_entry, name='new_entry'),
# views.py
def new_entry(request, subject_id):
    """Add a new entry for a particular subject."""
    subject = Subject.objects.get(id=subject_id)
    if request.method != 'POST':
        form = EntryForm()
    else:
        form = EntryForm(data=request.POST)
        if form.is_valid():
            new_entry_obj = form.save(commit=False)
            new_entry_obj.subject = subject
            new_entry_obj.save()
            return redirect('log_entries:subject_detail', subject_id=subject_id)
    context = {'subject': subject, 'form': form}
    return render(request, 'log_entries/new_entry.html', context)

Create the template new_entry.html and add a link from the subject detail page.

Editing an Existing Entry

Add functionality to edit entries.

# urls.py
path('edit_entry/<int:entry_id>/', views.edit_entry, name='edit_entry'),
# views.py
def edit_entry(request, entry_id):
    """Edit an existing entry."""
    entry = JournalEntry.objects.get(id=entry_id)
    subject = entry.subject
    if request.method != 'POST':
        form = EntryForm(instance=entry)
    else:
        form = EntryForm(instance=entry, data=request.POST)
        if form.is_valid():
            form.save()
            return redirect('log_entries:subject_detail', subject_id=subject.id)
    context = {'entry': entry, 'subject': subject, 'form': form}
    return render(request, 'log_entries/edit_entry.html', context)

Create the template edit_entry.html and add an edit link next to each entry in the subject detail page.

User Account Management

Create a separate app for user accounts.

python manage.py startapp user_accounts

Add it to INSTALLED_APPS and include its URLs in the project's main urls.py.

path('accounts/', include('user_accounts.urls')),

Login Page

Create user_accounts/urls.py to use Django's built-in authentication views.

from django.urls import path, include

app_name = 'user_accounts'
urlpatterns = [
    path('', include('django.contrib.auth.urls')),
]

Create a login template at user_accounts/templates/registration/login.html.

{% extends 'log_entries/base.html' %}
{% block main_content %}
    <h2>Log in</h2>
    {% if form.errors %}
        <p>Your username and password didn't match. Please try again.</p>
    {% endif %}
    <form method="post" action="{% url 'user_accounts:login' %}">
        {% csrf_token %}
        {{ form.as_p }}
        <button name="submit">Log in</button>
        <input type="hidden" name="next" value="{% url 'log_entries:home' %}" />
    </form>
{% endblock %}

Update the base template to show login/logout links conditionally.

<p>
    <a href="{% url 'log_entries:home' %}">Home</a> -
    <a href="{% url 'log_entries:subject_list' %}">Subjects</a> -
    {% if user.is_authenticated %}
        Hello, {{ user.username }}.
        <a href="{% url 'user_accounts:logout' %}">Log out</a>
    {% else %}
        <a href="{% url 'user_accounts:login' %}">Log in</a>
    {% endif %}
</p>

Create a logout confirmation template at user_accounts/templates/registration/logged_out.html.

Registration Page

Create a custom view for registration in user_accounts/views.py.

from django.shortcuts import render, redirect
from django.contrib.auth import login
from django.contrib.auth.forms import UserCreationForm

def register(request):
    """Register a new user."""
    if request.method != 'POST':
        form = UserCreationForm()
    else:
        form = UserCreationForm(data=request.POST)
        if form.is_valid():
            new_user = form.save()
            login(request, new_user)
            return redirect('log_entries:home')
    context = {'form': form}
    return render(request, 'registration/register.html', context)

Add a URL pattern for registration.

# user_accounts/urls.py
from django.urls import path
from . import views

urlpatterns = [
    path('', include('django.contrib.auth.urls')),
    path('register/', views.register, name='register'),
]

Create the registration template register.html in the registration directory and add a registration link to the base template for non-authenticated users.

Restricting Access

Use the @login_required decorator to protect views.

from django.contrib.auth.decorators import login_required

@login_required
def subject_list(request):
    # ... view code

Apply this decorator to all views that should require login, except the home page and registration pages.

In settings.py, set the login URL.

LOGIN_URL = 'user_accounts:login'

Associating Data with Users

Modify the Subject model to include an owner.

from django.contrib.auth.models import User

class Subject(models.Model):
    name = models.CharField(max_length=200)
    created_at = models.DateTimeField(auto_now_add=True)
    owner = models.ForeignKey(User, on_delete=models.CASCADE)
    # ...

Run migrations, providing a default user ID for existing subjects during the migration process.

Modify views to filter subjects by the current user.

@login_required
def subject_list(request):
    subjects = Subject.objects.filter(owner=request.user).order_by('created_at')
    context = {'subjects': subjects}
    return render(request, 'log_entries/subjects.html', context)

Protect the subject detail and edit entry views to ensure users can only access their own data.

from django.http import Http404

@login_required
def subject_detail(request, subject_id):
    subject = Subject.objects.get(id=subject_id)
    if subject.owner != request.user:
        raise Http404
    # ...

Update the new_subject view to assign the owner automatically.

@login_required
def new_subject(request):
    if request.method != 'POST':
        form = SubjectForm()
    else:
        form = SubjectForm(data=request.POST)
        if form.is_valid():
            new_subject_obj = form.save(commit=False)
            new_subject_obj.owner = request.user
            new_subject_obj.save()
            return redirect('log_entries:subject_list')
    context = {'form': form}
    return render(request, 'log_entries/new_subject.html', context)

Styling with Bootstrap

Install django-bootstrap4.

pip install django-bootstrap4

Add it to INSTALLED_APPS.

INSTALLED_APPS = [
    # ...
    'bootstrap4',
    # ...
]

Update the base template and all other templates to use Bootstrap classes for responsive design and improved aesthetics. Load Bootstrap tags and use its components like navbars, jumbotrons, cards, and form styling.

Refactor templates to use Bootstrap's grid system and utility classes, replacing basic HTML with styled components.

Tags: Django web development python Bootstrap database

Posted on Tue, 04 Aug 2026 16:26:49 +0000 by Suchy