Web Framework Python
3 Weeks - Complete Guide
Web framework Python yang powerful, cepat, dan scalable
# Install Django
pip install django
# Cek versi
django-admin --version
pip install uv
uv pip install django
# Buat venv
python -m venv myenv
# Activate
source myenv/bin/activate # Linux/Mac
myenv\Scripts\activate # Windows
# Install Django
pip install django
# 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
python manage.py runserver
# Atau dengan port spesifik
python manage.py runserver 8080
python manage.py startapp blog
blog/
├── __init__.py
├── admin.py
├── apps.py
├── migrations/
├── models.py
├── tests.py
└── views.py
# settings.py
INSTALLED_APPS = [
...
'blog',
]
# 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
# Buat migration
python manage.py makemigrations
# Lihat SQL yang akan dijalankan
python manage.py sqlmigrate blog 0001
# Jalankan migration
python manage.py migrate
# Rollback ke migration tertentu
python manage.py migrate blog 0001
# Cek status
python manage.py showmigrations
# Hapus semua migration
python manage.py migrate blog zero
python manage.py createsuperuser
# 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')
# Buka: http://127.0.0.1:8000/admin
# 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})
# 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'),
]
# 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')),
]
templates/
└── blog/
├── base.html
├── post_list.html
└── post_detail.html
# templates/base.html
{% load static %}
<!DOCTYPE html>
<html>
<head>
<title>{% block title %}{% endblock %}</title>
</head>
<body>
{% block content %}{% endblock %}
</body>
</html>
# 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 %}
# 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')
Post.objects.filter(title__contains='Django')
Post.objects.filter(created_at__year=2024)
Post.objects.filter(published=False)
# blog/forms.py
from django import forms
from .models import Post
class PostForm(forms.ModelForm):
class Meta:
model = Post
fields = ['title', 'content', 'published']
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})
blog/
└── static/
├── css/
│ └── style.css
└── images/
└── logo.png
{% load static %}
<link rel="stylesheet" href="{% static 'css/style.css' %}">
<img src="{% static 'images/logo.png' %}">
# models.py
image = models.ImageField(upload_to='images/')
# settings.py
MEDIA_URL = '/media/'
MEDIA_ROOT = BASE_DIR / 'media'
# settings.py
INSTALLED_APPS = [
...
'django.contrib.auth',
'django.contrib.contenttypes',
]
# urls.py
path('accounts/', include('django.contrib.auth.urls'))
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'),
from django.contrib.auth.decorators import login_required
@login_required
def dashboard(request):
...
# 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'
path('', PostListView.as_view(), name='post_list'),
path('post/<int:pk>/', PostDetailView.as_view(), name='post_detail'),
# Install
pip install djangorestframework
# settings.py
INSTALLED_APPS = [
...
'rest_framework',
]
from rest_framework import serializers
from .models import Post
class PostSerializer(serializers.ModelSerializer):
class Meta:
model = Post
fields = ['id', 'title', 'content', 'created_at']
from rest_framework import generics
from .models import Post
from .serializers import PostSerializer
class PostListCreate(generics.ListCreateAPIView):
queryset = Post.objects.all()
serializer_class = PostSerializer
# 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')
python manage.py test
# Dengan coverage
pip install coverage
coverage run manage.py test
coverage report
# settings.py
DEBUG = False
ALLOWED_HOSTS = ['yourdomain.com']
# Secrets
# Gunakan environment variables!
pip install gunicorn
gunicorn myproject.wsgi
# Dockerfile
FROM python:3.11
WORKDIR /app
COPY requirements.txt .
RUN pip install -r requirements.txt
COPY . .
CMD ["gunicorn", "myproject.wsgi"]
# .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')
# 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
# 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 %}
Next Steps
1. Practice: buat blog app
2. Pelajari Django REST Framework
3. Explore: Django Channels, Celery
4. Build project portfolio
# 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'})
# 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
# comments/urls.py
router = NestedDefaultRouter(parent_router, r'posts')
router.register(r'comments', CommentViewSet)
# URLs: /api/posts/{post_id}/comments/{id}/
# 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
# 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()
@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 | Override save() |
|---|---|
| Loose coupling | Tightly coupled |
| Reusable di app lain | Specific ke model |
| Hard to trace | Easier to follow |
| Testing butuh mocking | Testing langsung |
Apa itu migrate di Django?
Apa fungsi views.py?
Apa itu ORM?