Home > Django
Slide 1 / 22
🎯

Django

Web Framework Python

3 Weeks - Complete Guide

Web framework Python yang powerful, cepat, dan scalable

Overview

Tujuan Pembelajaran

Kenapa Django?

Minggu 1 - Setup

Install Django

Via pip

# Install Django
pip install django

# Cek versi
django-admin --version

Via uv (recommended)

pip install uv
uv pip install django

Virtual Environment

# Buat venv
python -m venv myenv

# Activate
source myenv/bin/activate  # Linux/Mac
myenv\Scripts\activate     # Windows

# Install Django
pip install django
Minggu 1

Buat Project

# Buat project baru
django-admin startproject myproject

# Masuk ke folder
cd myproject

# Struktur project
myproject/
├── manage.py
└── myproject/
    ├── __init__.py
    ├── settings.py
    ├── urls.py
    ├── asgi.py
    └── wsgi.py

Run Server

python manage.py runserver

# Atau dengan port spesifik
python manage.py runserver 8080
Buka http://127.0.0.1:8000 untuk melihat project!
Minggu 1

Django Apps

Buat App

python manage.py startapp blog

Struktur App

blog/
├── __init__.py
├── admin.py
├── apps.py
├── migrations/
├── models.py
├── tests.py
└── views.py

Register App

# settings.py
INSTALLED_APPS = [
    ...
    'blog',
]
Minggu 1

Models

Models adalah representasi database dalam Python
# blog/models.py
from django.db import models

class Post(models.Model):
    title = models.CharField(max_length=200)
    content = models.TextField()
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    published = models.BooleanField(default=False)

    def __str__(self):
        return self.title

Field Types

Minggu 1

Migration

# Buat migration
python manage.py makemigrations

# Lihat SQL yang akan dijalankan
python manage.py sqlmigrate blog 0001

# Jalankan migration
python manage.py migrate

Undo Migration

# Rollback ke migration tertentu
python manage.py migrate blog 0001

# Cek status
python manage.py showmigrations

Reset Database

# Hapus semua migration
python manage.py migrate blog zero
Minggu 1

Django Admin

Create Superuser

python manage.py createsuperuser

Register Models

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

@admin.register(Post)
class PostAdmin(admin.ModelAdmin):
    list_display = ('title', 'published', 'created_at')
    list_filter = ('published', 'created_at')
    search_fields = ('title', 'content')

Akses Admin

# Buka: http://127.0.0.1:8000/admin
Minggu 1

Views

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

def post_list(request):
    posts = Post.objects.filter(published=True)
    return render(request, 'blog/post_list.html', {'posts': posts})

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

Function vs Class-Based

Minggu 1

URL Routing

App URLs

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

app_name = 'blog'

urlpatterns = [
    path('', views.post_list, name='post_list'),
    path('post//', views.post_detail, name='post_detail'),
]

Project URLs

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

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

Templates

Template Structure

templates/
└── blog/
    ├── base.html
    ├── post_list.html
    └── post_detail.html

Base Template

# templates/base.html
{% load static %}
<!DOCTYPE html>
<html>
<head>
    <title>{% block title %}{% endblock %}</title>
</head>
<body>
    {% block content %}{% endblock %}
</body>
</html>

Extend Template

# templates/blog/post_list.html
{% extends 'base.html' %}

{% block title %}Blog - Post List{% endblock %}

{% block content %}
    {% for post in posts %}
        <h2>{{ post.title }}</h2>
    {% endfor %}
{% endblock %}
Minggu 2

Django ORM

# Create
Post.objects.create(title="Hello", content="Content")

# Read
Post.objects.all()
Post.objects.get(pk=1)
Post.objects.filter(published=True)
Post.objects.exclude(title="Test")

# Update
post = Post.objects.get(pk=1)
post.title = "New Title"
post.save()

# Delete
post.delete()

# Chaining
Post.objects.filter(published=True).order_by('-created_at')

Lookups

Post.objects.filter(title__contains='Django')
Post.objects.filter(created_at__year=2024)
Post.objects.filter(published=False)
Minggu 2

Forms

Django Forms

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

class PostForm(forms.ModelForm):
    class Meta:
        model = Post
        fields = ['title', 'content', 'published']

View dengan Form

from .forms import PostForm

def post_create(request):
    if request.method == 'POST':
        form = PostForm(request.POST)
        if form.is_valid():
            form.save()
            return redirect('blog:post_list')
    else:
        form = PostForm()
    return render(request, 'form.html', {'form': form})
Minggu 2

Static Files

Struktur

blog/
└── static/
    ├── css/
    │   └── style.css
    └── images/
        └── logo.png

Di Template

{% load static %}

<link rel="stylesheet" href="{% static 'css/style.css' %}">
<img src="{% static 'images/logo.png' %}">

Media Files

# models.py
image = models.ImageField(upload_to='images/')

# settings.py
MEDIA_URL = '/media/'
MEDIA_ROOT = BASE_DIR / 'media'
Minggu 2

Authentication

Setup

# settings.py
INSTALLED_APPS = [
    ...
    'django.contrib.auth',
    'django.contrib.contenttypes',
]

# urls.py
path('accounts/', include('django.contrib.auth.urls'))

Login View

from django.contrib.auth import views as auth_views

path('login/', auth_views.LoginView.as_view(), name='login'),
path('logout/', auth_views.LogoutView.as_view(), name='logout'),

Restrict View

from django.contrib.auth.decorators import login_required

@login_required
def dashboard(request):
    ...
Minggu 2

Class-Based Views

# blog/views.py
from django.views.generic import ListView, DetailView
from .models import Post

class PostListView(ListView):
    model = Post
    template_name = 'blog/post_list.html'
    context_object_name = 'posts'
    ordering = ['-created_at']

class PostDetailView(DetailView):
    model = Post
    template_name = 'blog/post_detail.html'

URLs untuk CBV

path('', PostListView.as_view(), name='post_list'),
path('post/<int:pk>/', PostDetailView.as_view(), name='post_detail'),

CRUD dengan CBV

Minggu 3

Django REST Framework

# Install
pip install djangorestframework

# settings.py
INSTALLED_APPS = [
    ...
    'rest_framework',
]

Serializers

from rest_framework import serializers
from .models import Post

class PostSerializer(serializers.ModelSerializer):
    class Meta:
        model = Post
        fields = ['id', 'title', 'content', 'created_at']

API Views

from rest_framework import generics
from .models import Post
from .serializers import PostSerializer

class PostListCreate(generics.ListCreateAPIView):
    queryset = Post.objects.all()
    serializer_class = PostSerializer
Minggu 3

Testing

Write Test

# blog/tests.py
from django.test import TestCase
from .models import Post

class PostModelTest(TestCase):
    def test_str(self):
        post = Post(title='Test', content='Content')
        self.assertEqual(str(post), 'Test')

Run Tests

python manage.py test

# Dengan coverage
pip install coverage
coverage run manage.py test
coverage report
Minggu 3

Deployment

Production Settings

# settings.py
DEBUG = False
ALLOWED_HOSTS = ['yourdomain.com']

# Secrets
# Gunakan environment variables!

Gunicorn

pip install gunicorn
gunicorn myproject.wsgi

Docker

# Dockerfile
FROM python:3.11
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .
CMD ["gunicorn", "myproject.wsgi"]
Best Practices

Best Practices

Environment Variables

# .env
SECRET_KEY=your-secret-key
DEBUG=False
DATABASE_URL=postgres://...

# settings.py
from dotenv import load_dotenv
load_dotenv()
SECRET_KEY = os.getenv('SECRET_KEY')
Cheat Sheet

Commands

# Project & App
django-admin startproject myproject
python manage.py startapp blog

# Database
python manage.py makemigrations
python manage.py migrate

# Run
python manage.py runserver
python manage.py createsuperuser

# Testing
python manage.py test

Quick References

# ORM
Post.objects.all()
Post.objects.filter()
Post.objects.get(pk=1)

# URL
{% url 'blog:post_detail' pk=post.pk %}

# Template
{% for %}{% endfor %}
{% if %}{% endif %}
{% block %}{% endblock %}

🎉 Selesai!

Next Steps

1. Practice: buat blog app

2. Pelajari Django REST Framework

3. Explore: Django Channels, Celery

4. Build project portfolio

Advanced

Django REST Framework - Viewsets & Routers

Viewsets

# blog/viewsets.py
from rest_framework import viewsets, permissions
from .models import Post
from .serializers import PostSerializer

class PostViewSet(viewsets.ModelViewSet):
    queryset = Post.objects.all()
    serializer_class = PostSerializer
    permission_classes = [permissions.IsAuthenticatedOrReadOnly]

    # Custom actions
    @action(detail=True, methods=['post'])
    def publish(self, request, pk=None):
        post = self.get_object()
        post.published = True
        post.save()
        return Response({'status': 'published'})

Routers

# blog/urls.py
from rest_framework.routers import DefaultRouter
from .viewsets import PostViewSet

router = DefaultRouter()
router.register(r'posts', PostViewSet)

urlpatterns = [
    path('api/', include(router.urls)),
]

# Generated URLs:
# GET    /api/posts/           -> list
# POST   /api/posts/           -> create
# GET    /api/posts/{id}/      -> retrieve
# PUT    /api/posts/{id}/      -> update
# DELETE /api/posts/{id}/      -> destroy
# POST   /api/posts/{id}/publish/ -> custom action

Nested Routers

# comments/urls.py
router = NestedDefaultRouter(parent_router, r'posts')
router.register(r'comments', CommentViewSet)

# URLs: /api/posts/{post_id}/comments/{id}/
Benefits: Viewsets + Routers = CRUD dengan kode minimal!
Advanced

Django Signals

Common Signals

# post_save - setelah model.save()
# pre_save - sebelum model.save()
# post_delete - setelah model.delete()
# pre_delete - sebelum model.delete()
# m2m_changed - ManyToMany changed
# pre_init, post_init - model instantiation

Contoh: Auto Create Profile

# accounts/signals.py
from django.db.models.signals import post_save
from django.contrib.auth import get_user_model
from django.dispatch import receiver

User = get_user_model()

@receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
    if created:
        Profile.objects.create(user=instance)

@receiver(post_save, sender=User)
def save_user_profile(sender, instance, **kwargs):
    if hasattr(instance, 'profile'):
        instance.profile.save()

Contoh: Cache Invalidation

@receiver(post_save, sender=Post)
def invalidate_post_cache(sender, instance, **kwargs):
    cache.delete(f'post_{instance.pk}')
    cache.delete('all_posts')

@receiver(post_delete, sender=Post)
def invalidate_post_cache_delete(sender, instance, **kwargs):
    cache.delete(f'post_{instance.pk}')
    cache.delete('all_posts')

Signals vs Overriding save()

SignalsOverride save()
Loose couplingTightly coupled
Reusable di app lainSpecific ke model
Hard to traceEasier to follow
Testing butuh mockingTesting langsung
Best Practice: Gunakan signals untuk cross-app concerns, override save() untuk model-specific logic!

Kuis: Django

Apa itu migrate di Django?

Apa fungsi views.py?

Apa itu ORM?