Django Web Development: Van Concept tot Productie

Leer hoe je professionele web applicaties bouwt met Django. Van setup tot deployment - een complete gids voor het bouwen van schaalbare webapplicaties.

Waarom Django?

Django is een van de meest populaire Python web frameworks. Het volgt het "batteries included" principe, wat betekent dat het veel functionaliteit out-of-the-box biedt.

Voordelen van Django:

  • Snelle ontwikkeling: Veel functionaliteit is al ingebouwd
  • Veilig: Ingebouwde bescherming tegen veel voorkomende aanvallen
  • Schaalbaar: Gebruikt door grote bedrijven zoals Instagram en Spotify
  • DRY principe: Don't Repeat Yourself - minder code duplicatie
  • Admin interface: Automatisch gegenereerde beheerinterface

Django Project Setup

Laten we beginnen met het opzetten van een nieuw Django project:

Installatie:

# Maak een virtuele omgeving
python -m venv myproject_env

# Activeer de virtuele omgeving
# Windows:
myproject_env\Scripts\activate
# macOS/Linux:
source myproject_env/bin/activate

# Installeer Django
pip install django

# Maak een nieuw project
django-admin startproject myproject
cd myproject

# Start de development server
python manage.py runserver

Django Project Structuur

Een typisch Django project heeft de volgende structuur:

myproject/
    manage.py
    myproject/
        __init__.py
        settings.py
        urls.py
        wsgi.py
        asgi.py

Belangrijke bestanden:

  • manage.py: Command-line utility voor het project
  • settings.py: Project configuratie
  • urls.py: URL routing
  • wsgi.py: WSGI configuratie voor deployment

Je Eerste Django App

Django projecten bestaan uit apps. Elke app heeft een specifieke functionaliteit:

# Maak een nieuwe app
python manage.py startapp blog

# Voeg de app toe aan settings.py
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'blog',  # Nieuwe app
]

Models: Database Structuur

Models definiëren je database structuur in Python code:

# blog/models.py
from django.db import models
from django.contrib.auth.models import User

class Category(models.Model):
    name = models.CharField(max_length=100)
    slug = models.SlugField(unique=True)
    
    def __str__(self):
        return self.name

class Post(models.Model):
    title = models.CharField(max_length=200)
    slug = models.SlugField(unique=True)
    author = models.ForeignKey(User, on_delete=models.CASCADE)
    content = models.TextField()
    category = models.ForeignKey(Category, on_delete=models.CASCADE)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    published = models.BooleanField(default=False)
    
    class Meta:
        ordering = ['-created_at']
    
    def __str__(self):
        return self.title

Migrations:

# Maak migrations
python manage.py makemigrations

# Pas migrations toe
python manage.py migrate

# Maak een superuser
python manage.py createsuperuser

Views: Business Logic

Views bevatten de logica voor het verwerken van requests:

# blog/views.py
from django.shortcuts import render, get_object_or_404
from django.views.generic import ListView
from .models import Post, Category

class PostListView(ListView):
    model = Post
    queryset = Post.objects.filter(published=True)
    template_name = 'blog/post_list.html'
    context_object_name = 'posts'
    paginate_by = 10

def post_detail(request, slug):
    post = get_object_or_404(Post, slug=slug, published=True)
    return render(request, 'blog/post_detail.html', {'post': post})

def category_posts(request, category_slug):
    category = get_object_or_404(Category, slug=category_slug)
    posts = Post.objects.filter(category=category, published=True)
    return render(request, 'blog/category_posts.html', {
        'category': category,
        'posts': posts
    })

Templates: Frontend

Templates definiëren hoe je data wordt weergegeven:

Base Template:

<!-- blog/templates/blog/base.html -->
<!DOCTYPE html>
<html lang="nl">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>{% block title %}Mijn Blog{% endblock %}</title>
    <link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
    <nav class="navbar navbar-expand-lg navbar-dark bg-dark">
        <div class="container">
            <a class="navbar-brand" href="{% url 'blog:post_list' %}">Mijn Blog</a>
        </div>
    </nav>
    
    <main class="container mt-4">
        {% block content %}
        {% endblock %}
    </main>
    
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>

Post List Template:

<!-- blog/templates/blog/post_list.html -->
{% extends 'blog/base.html' %}

{% block title %}Blog Posts{% endblock %}

{% block content %}
<div class="row">
    <div class="col-md-8">
        <h1>Laatste Blog Posts</h1>
        
        {% for post in posts %}
        <div class="card mb-4">
            <div class="card-body">
                <h5 class="card-title">
                    <a href="{% url 'blog:post_detail' post.slug %}">{{ post.title }}</a>
                </h5>
                <p class="card-text">{{ post.content|truncatewords:50 }}</p>
                <small class="text-muted">
                    Door {{ post.author.username }} op {{ post.created_at|date:"d M Y" }}
                </small>
            </div>
        </div>
        {% empty %}
        <p>Nog geen blog posts beschikbaar.</p>
        {% endfor %}
        
        <!-- Pagination -->
        {% if is_paginated %}
        <nav aria-label="Page navigation">
            <ul class="pagination">
                {% if page_obj.has_previous %}
                <li class="page-item">
                    <a class="page-link" href="?page={{ page_obj.previous_page_number }}">Vorige</a>
                </li>
                {% endif %}
                
                <li class="page-item active">
                    <span class="page-link">{{ page_obj.number }}</span>
                </li>
                
                {% if page_obj.has_next %}
                <li class="page-item">
                    <a class="page-link" href="?page={{ page_obj.next_page_number }}">Volgende</a>
                </li>
                {% endif %}
            </ul>
        </nav>
        {% endif %}
    </div>
</div>
{% endblock %}

URL Routing

URL patterns koppelen URLs aan views:

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

app_name = 'blog'

urlpatterns = [
    path('', views.PostListView.as_view(), name='post_list'),
    path('post/<slug:slug>/', views.post_detail, name='post_detail'),
    path('category/<slug:category_slug>/', views.category_posts, name='category_posts'),
]

# myproject/urls.py
from django.contrib import admin
from django.urls import path, include

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

Django Admin

Django biedt een automatisch gegenereerde admin interface:

# blog/admin.py
from django.contrib import admin
from .models import Post, Category

@admin.register(Category)
class CategoryAdmin(admin.ModelAdmin):
    list_display = ['name', 'slug']
    prepopulated_fields = {'slug': ('name',)}

@admin.register(Post)
class PostAdmin(admin.ModelAdmin):
    list_display = ['title', 'author', 'category', 'published', 'created_at']
    list_filter = ['published', 'created_at', 'category']
    search_fields = ['title', 'content']
    prepopulated_fields = {'slug': ('title',)}
    date_hierarchy = 'created_at'
    ordering = ['-created_at']

Forms en Validatie

Django forms maken het gemakkelijk om user input te verwerken:

# blog/forms.py
from django import forms
from .models import Post

class PostForm(forms.ModelForm):
    class Meta:
        model = Post
        fields = ['title', 'content', 'category', 'published']
        widgets = {
            'title': forms.TextInput(attrs={'class': 'form-control'}),
            'content': forms.Textarea(attrs={'class': 'form-control', 'rows': 10}),
            'category': forms.Select(attrs={'class': 'form-select'}),
        }
    
    def clean_title(self):
        title = self.cleaned_data['title']
        if len(title) < 5:
            raise forms.ValidationError("Titel moet minimaal 5 karakters lang zijn.")
        return title

Testing

Django heeft uitgebreide testing ondersteuning:

# blog/tests.py
from django.test import TestCase
from django.contrib.auth.models import User
from django.urls import reverse
from .models import Post, Category

class PostModelTest(TestCase):
    def setUp(self):
        self.user = User.objects.create_user(
            username='testuser',
            password='testpass'
        )
        self.category = Category.objects.create(
            name='Test Category',
            slug='test-category'
        )
    
    def test_post_creation(self):
        post = Post.objects.create(
            title='Test Post',
            slug='test-post',
            author=self.user,
            content='Test content',
            category=self.category,
            published=True
        )
        self.assertEqual(post.title, 'Test Post')
        self.assertTrue(post.published)
    
    def test_post_str_representation(self):
        post = Post.objects.create(
            title='Test Post',
            slug='test-post',
            author=self.user,
            content='Test content',
            category=self.category
        )
        self.assertEqual(str(post), 'Test Post')

class PostViewTest(TestCase):
    def setUp(self):
        self.user = User.objects.create_user(
            username='testuser',
            password='testpass'
        )
        self.category = Category.objects.create(
            name='Test Category',
            slug='test-category'
        )
        self.post = Post.objects.create(
            title='Test Post',
            slug='test-post',
            author=self.user,
            content='Test content',
            category=self.category,
            published=True
        )
    
    def test_post_list_view(self):
        response = self.client.get(reverse('blog:post_list'))
        self.assertEqual(response.status_code, 200)
        self.assertContains(response, 'Test Post')
    
    def test_post_detail_view(self):
        response = self.client.get(
            reverse('blog:post_detail', kwargs={'slug': 'test-post'})
        )
        self.assertEqual(response.status_code, 200)
        self.assertContains(response, 'Test Post')

# Tests uitvoeren
# python manage.py test

Deployment

Voor productie deployment zijn er verschillende opties:

Production Settings:

# settings/production.py
from .base import *

DEBUG = False
ALLOWED_HOSTS = ['jouwdomain.com', 'www.jouwdomain.com']

# Database
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': 'jouw_db_naam',
        'USER': 'jouw_db_user',
        'PASSWORD': 'jouw_db_password',
        'HOST': 'localhost',
        'PORT': '5432',
    }
}

# Static files
STATIC_ROOT = '/var/www/jouwproject/static/'
MEDIA_ROOT = '/var/www/jouwproject/media/'

# Security
SECURE_SSL_REDIRECT = True
SECURE_BROWSER_XSS_FILTER = True
SECURE_CONTENT_TYPE_NOSNIFF = True

Requirements.txt:

Django==4.2.0
psycopg2-binary==2.9.5
gunicorn==20.1.0
whitenoise==6.4.0
Pillow==9.5.0

Best Practices

  • Gebruik virtuele omgevingen: Isoleer project dependencies
  • Splits settings: Verschillende settings voor development/production
  • Gebruik environment variables: Voor gevoelige informatie
  • Schrijf tests: Zorg voor betrouwbare code
  • Gebruik migrations: Versie beheer voor database schema
  • Cache implementeren: Voor betere performance
  • Logging configureren: Voor debugging en monitoring

Conclusie

Django is een krachtig framework dat je helpt om snel professionele web applicaties te bouwen. Door zijn "batteries included" filosofie kun je je focussen op je applicatie logica in plaats van boilerplate code.

Belangrijke voordelen van Django:

  • Snelle ontwikkeling met minder code
  • Ingebouwde veiligheidsfeatures
  • Schaalbaarheid voor grote applicaties
  • Uitgebreide documentatie en community
  • Flexibiliteit en uitbreidbaarheid

Wil je Django leren in een gestructureerde omgeving? Bekijk onze Django Web Development cursus voor praktijkgerichte training!