1. Pengenalan Django
Django adalah web framework Python yang bersifat open-source dan baterai-include (semua fitur sudah tersedia). Dikembangkan pertama kali pada tahun 2005 oleh Adrian Holovaty dan Simon Willison, Django menjadi salah satu framework web paling populer dan banyak digunakan di dunia.
Django digunakan oleh banyak perusahaan besar seperti Instagram, Pinterest, Mozilla, Disqus, dan Bitbucket. Filosofi Django adalah "Don't Repeat Yourself" (DRY) dan "Batteries Included", artinya semua fitur yang dibutuhkan sudah tersedia built-in.
Mengapa Memilih Django?
| Keunggulan | Penjelasan |
|---|---|
| Batteries Included | ORM, admin panel, authentication, forms, semua built-in |
| Security | Proteksi bawaan terhadap CSRF, XSS, SQL Injection, dan clickjacking |
| ORM Kuat | Bekerja dengan database tanpa menulis SQL langsung |
| Admin Panel | Panel admin otomatis untuk mengelola data |
| Scalable | Bisa menangani traffic tinggi (Instagram menggunakan Django) |
| Dokumentasi | Dokumentasi lengkap dan komunitas besar |
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β DJANGO MVT PATTERN β β β β ββββββββββββ ββββββββββββ ββββββββββββ β β β β β β β β β β β TEMPLATE βββββββ VIEW ββββββΊβ MODEL β β β β (HTML) β β (Python) β β (Data) β β β β β β β β β β β ββββββ¬ββββββ ββββββ¬ββββββ ββββββ¬ββββββ β β β β β β β β βββββββ΄ββββββ βββββββ΄ββββββ β β β β URL β β DATABASE β β β β β Router β β (PostgreSQLβ β β β β β β /SQLite) β β β ββββββββββββ ββββββ β β β β β Request: User β URL β View β Model β DB β β Response: DB β Model β View β Template β User β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
2. Instalasi dan Setup Proyek
Mari kita mulai dengan menginstal Django dan membuat proyek pertama.
Membuat Virtual Environment
# Buat folder proyek mkdir belajar-django && cd belajar-django # Buat virtual environment python -m venv venv # Aktifkan virtual environment # Windows: venv\Scripts\activate # macOS/Linux: source venv/bin/activate # Instal Django pip install django # Verifikasi instalasi python -m django --version # Output: 5.0.6
Membuat Proyek dan Aplikasi
# Buat proyek Django baru django-admin startproject mysite . # Buat aplikasi di dalam proyek python manage.py startapp blog # Jalankan server development python manage.py runserver # Output: # Starting development server at http://127.0.0.1:8000/ # Buat migrasi database python manage.py migrate # Buat superuser untuk admin python manage.py createsuperuser
Struktur Proyek
mysite/
βββ manage.py β Utility commands
βββ mysite/ β Konfigurasi proyek
β βββ __init__.py
β βββ settings.py β Pengaturan proyek
β βββ urls.py β URL routing utama
β βββ asgi.py
β βββ wsgi.py
βββ blog/ β Aplikasi blog
βββ __init__.py
βββ admin.py β Konfigurasi admin
βββ apps.py β Konfigurasi aplikasi
βββ migrations/ β Database migrations
βββ models.py β Definisi model/data
βββ tests.py β Unit tests
βββ urls.py β URL routing aplikasi
βββ views.py β Logika tampilan
Konfigurasi settings.py
# mysite/settings.py β Tambahkan aplikasi 'blog'
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'blog', # β Tambahkan ini
]
# Bahasa dan zona waktu Indonesia
LANGUAGE_CODE = 'id-id'
TIME_ZONE = 'Asia/Jakarta'
USE_I18N = True
USE_TZ = True
# Bahasa Indonesia untuk template
LOCALE_PATHS = [BASE_DIR / 'locale']
Setiap aplikasi Django harus didaftarkan di INSTALLED_APPS pada settings.py. Tanpa itu, Django tidak akan mengenali model, template, dan URL dari aplikasi tersebut.
3. Memahami MVT Pattern
Django menggunakan pola arsitektur MVT (Model-View-Template) yang mirip dengan MVC (Model-View-Controller) tetapi dengan penamaan yang sedikit berbeda.
| MVC (Lain) | MVT (Django) | Fungsi |
|---|---|---|
| Model | Model | Struktur data dan logika database |
| View | Template | Tampilan HTML yang dikirim ke pengguna |
| Controller | View | Logika bisnis β memproses request dan mengembalikan response |
Alur Request dalam Django
# Alur request Django:
#
# 1. User mengakses URL (misal: /blog/artikel/1/)
# β
# 2. URL Dispatcher mencocokkan URL dengan pattern
# urls.py β path('artikel/<int:id>/', views.detail_artikel)
# β
# 3. View function dipanggil
# views.py β def detail_artikel(request, id)
# β
# 4. View mengambil data dari Model (database)
# models.py β Artikel.objects.get(id=id)
# β
# 5. View merender Template dengan data
# templates β render(request, 'detail.html', context)
# β
# 6. HTML dikembalikan ke browser user
# HttpResponse β <html>...</html>
4. Models: Mendefinisikan Data
Model adalah representasi dari data di database. Setiap model Django merepresentasikan satu tabel di database. Django ORM akan otomatis membuat tabel SQL berdasarkan definisi model.
Mendefinisikan Model
from django.db import models
from django.contrib.auth.models import User
from django.utils import timezone
class Kategori(models.Model):
nama = models.CharField(max_length=100)
slug = models.SlugField(unique=True)
deskripsi = models.TextField(blank=True)
class Meta:
verbose_name_plural = "Kategori"
ordering = ['nama']
def __str__(self):
return self.nama
class Artikel(models.Model):
STATUS_CHOICES = [
('draft', 'Draft'),
('published', 'Diterbitkan'),
]
judul = models.CharField(max_length=200)
slug = models.SlugField(unique=True)
penulis = models.ForeignKey(User, on_delete=models.CASCADE)
kategori = models.ForeignKey(Kategori, on_delete=models.SET_NULL, null=True)
konten = models.TextField()
gambar = models.ImageField(upload_to='artikel/', blank=True)
status = models.CharField(max_length=10, choices=STATUS_CHOICES, default='draft')
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
tags = models.ManyToManyField('Tag', blank=True)
class Meta:
ordering = ['-created_at']
verbose_name_plural = "Artikel"
def __str__(self):
return self.judul
def get_absolute_url(self):
return f'/blog/artikel/{self.slug}/'
class Tag(models.Model):
nama = models.CharField(max_length=50, unique=True)
class Meta:
verbose_name_plural = "Tag"
def __str__(self):
return self.nama
class Komentar(models.Model):
artikel = models.ForeignKey(Artikel, on_delete=models.CASCADE, related_name='komentar')
nama = models.CharField(max_length=100)
email = models.EmailField()
isi = models.TextField()
created_at = models.DateTimeField(auto_now_add=True)
aktif = models.BooleanField(default=True)
class Meta:
ordering = ['created_at']
def __str__(self):
return f'Komentar oleh {self.nama} pada {self.artikel}'
Menjalankan Migrasi
# Buat file migrasi berdasarkan model python manage.py makemigrations blog # Output: Migrations for 'blog': # blog/migrations/0001_initial.py # - Create model Kategori # - Create model Tag # - Create model Artikel # - Create model Komentar # Jalankan migrasi ke database python manage.py migrate # Output: Applying blog.0001_initial... OK # Cek status migrasi python manage.py showmigrations # SQL yang dihasilkan (opsional, untuk debugging) python manage.py sqlmigrate blog 0001
Tipe Field Django
| Field | Tipe SQL | Contoh |
|---|---|---|
CharField | VARCHAR | CharField(max_length=100) |
TextField | TEXT | TextField() |
IntegerField | INT | IntegerField(default=0) |
FloatField | FLOAT | FloatField() |
BooleanField | BOOLEAN | BooleanField(default=True) |
DateField | DATE | DateField(auto_now_add=True) |
EmailField | VARCHAR | EmailField() |
SlugField | VARCHAR | SlugField(unique=True) |
ImageField | VARCHAR | ImageField(upload_to='img/') |
ForeignKey | INT (FK) | ForeignKey(Model, on_delete=CASCADE) |
5. Views: Logika Aplikasi
View adalah fungsi atau class yang menerima HTTP request dan mengembalikan HTTP response. View berisi logika bisnis aplikasi β mengambil data dari model dan merender template.
Function-Based Views
from django.shortcuts import render, get_object_or_404, redirect
from django.http import HttpResponse, JsonResponse
from .models import Artikel, Kategori
from .forms import ArtikelForm, KomentarForm
# View sederhana β mengembalikan HTML
def beranda(request):
artikel_terbaru = Artikel.objects.filter(
status='published'
).order_by('-created_at')[:5]
kategori_list = Kategori.objects.all()
context = {
'artikel_list': artikel_terbaru,
'kategori_list': kategori_list,
'judul_halaman': 'Beranda Blog',
}
return render(request, 'blog/beranda.html', context)
# View detail artikel
def detail_artikel(request, slug):
artikel = get_object_or_404(Artikel, slug=slug, status='published')
komentar_list = artikel.komentar.filter(aktif=True)
form_komentar = KomentarForm()
context = {
'artikel': artikel,
'komentar_list': komentar_list,
'form_komentar': form_komentar,
}
return render(request, 'blog/detail.html', context)
# View berdasarkan kategori
def kategori_detail(request, slug):
kategori = get_object_or_404(Kategori, slug=slug)
artikel_list = kategori.artikel_set.filter(status='published')
context = {
'kategori': kategori,
'artikel_list': artikel_list,
}
return render(request, 'blog/kategori.html', context)
# View untuk membuat artikel baru
def buat_artikel(request):
if request.method == 'POST':
form = ArtikelForm(request.POST, request.FILES)
if form.is_valid():
artikel = form.save(commit=False)
artikel.penulis = request.user
artikel.save()
return redirect('blog:detail', slug=artikel.slug)
else:
form = ArtikelForm()
return render(request, 'blog/buat_artikel.html', {'form': form})
# View JSON API
def api_artikel(request):
artikel_list = list(
Artikel.objects.filter(status='published')
.values('judul', 'slug', 'created_at')[:10]
)
return JsonResponse({'artikel': artikel_list})
Class-Based Views
from django.views.generic import ListView, DetailView, CreateView
from django.urls import reverse_lazy
from .models import Artikel
from .forms import ArtikelForm
# ListView β menampilkan daftar
class ArtikelListView(ListView):
model = Artikel
template_name = 'blog/artikel_list.html'
context_object_name = 'artikel_list'
paginate_by = 10
def get_queryset(self):
return Artikel.objects.filter(status='published')
# DetailView β menampilkan detail
class ArtikelDetailView(DetailView):
model = Artikel
template_name = 'blog/detail.html'
context_object_name = 'artikel'
slug_field = 'slug'
# CreateView β form untuk membuat baru
class ArtikelCreateView(CreateView):
model = Artikel
form_class = ArtikelForm
template_name = 'blog/buat_artikel.html'
success_url = reverse_lazy('blog:beranda')
def form_valid(self, form):
form.instance.penulis = self.request.user
return super().form_valid(form)
6. Templates: Tampilan HTML
Template adalah file HTML yang dikombinasikan dengan Django Template Language (DTL) untuk menampilkan data secara dinamis. Template memisahkan presentasi dari logika bisnis.
Template Dasar
<!DOCTYPE html>
<html lang="id">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% block title %}Blog{% endblock %} | MyBlog</title>
<link rel="stylesheet" href="{% static 'css/style.css' %}">
{% block extra_css %}{% endblock %}
</head>
<body>
<nav class="navbar">
<a href="{% url 'blog:beranda' %}">MyBlog</a>
<ul>
{% for kategori in kategori_list %}
<li><a href="{% url 'blog:kategori' kategori.slug %}">{{ kategori.nama }}</a></li>
{% endfor %}
</ul>
</nav>
<main class="container">
{% block content %}{% endblock %}
</main>
<footer>
<p>© 2026 MyBlog. Semua hak dilindungi.</p>
</footer>
<script src="{% static 'js/app.js' %}"></script>
{% block extra_js %}{% endblock %}
</body>
</html>
Template Anak (Child Template)
{% extends 'base.html' %}
{% load static %}
{% block title %}{{ judul_halaman }}{% endblock %}
{% block content %}
<h1>{{ judul_halaman }}</h1>
<div class="artikel-grid">
{% for artikel in artikel_list %}
<article class="artikel-card">
{% if artikel.gambar %}
<img src="{{ artikel.gambar.url }}" alt="{{ artikel.judul }}">
{% endif %}
<h2><a href="{{ artikel.get_absolute_url }}">{{ artikel.judul }}</a></h2>
<p class="meta">
Oleh {{ artikel.penulis.username }} |
{{ artikel.created_at|date:"d M Y" }} |
{{ artikel.kategori.nama }}
</p>
<p>{{ artikel.konten|truncatewords:30 }}</p>
{% if artikel.tags.all %}
<div class="tags">
{% for tag in artikel.tags.all %}
<span class="tag">{{ tag.nama }}</span>
{% endfor %}
</div>
{% endif %}
</article>
{% empty %}
<p>Belum ada artikel yang diterbitkan.</p>
{% endfor %}
</div>
<!-- Pagination -->
{% if is_paginated %}
<div class="pagination">
{% if page_obj.has_previous %}
<a href="?page={{ page_obj.previous_page_number }}">β Sebelumnya</a>
{% endif %}
<span>Halaman {{ page_obj.number }} dari {{ page_obj.paginator.num_pages }}</span>
{% if page_obj.has_next %}
<a href="?page={{ page_obj.next_page_number }}">Selanjutnya β</a>
{% endif %}
</div>
{% endif %}
{% endblock %}
Template Filters dan Tags
| Filter/Tag | Fungsi | Contoh |
|---|---|---|
{{ var|upper }} | Ubah ke huruf besar | {{ nama|upper }} |
{{ var|date }} | Format tanggal | {{ tgl|date:"d M Y" }} |
{{ var|truncatewords }} | Potong teks | {{ isi|truncatewords:20 }} |
{{ var|default }} | Nilai default | {{ val|default:"N/A" }} |
{% if %} | Kondisional | {% if user.is_staff %}...{% endif %} |
{% for %} | Loop | {% for item in list %}...{% endfor %} |
{% url %} | Generate URL | {% url 'blog:detail' slug %} |
{% csrf_token %} | Security token | Di dalam setiap form POST |
7. Django Admin Panel
Salah satu fitur terbaik Django adalah admin panel otomatis yang bisa digunakan untuk mengelola data tanpa perlu membuat UI sendiri. Admin panel sangat berguna untuk CMS, manajemen konten, dan debugging.
from django.contrib import admin
from .models import Kategori, Artikel, Tag, Komentar
@admin.register(Kategori)
class KategoriAdmin(admin.ModelAdmin):
list_display = ['nama', 'slug']
prepopulated_fields = {'slug': ('nama',)}
search_fields = ['nama']
@admin.register(Tag)
class TagAdmin(admin.ModelAdmin):
list_display = ['nama']
search_fields = ['nama']
class KomentarInline(admin.TabularInline):
model = Komentar
extra = 0
readonly_fields = ['nama', 'email', 'isi', 'created_at']
@admin.register(Artikel)
class ArtikelAdmin(admin.ModelAdmin):
list_display = ['judul', 'penulis', 'kategori', 'status', 'created_at']
list_filter = ['status', 'kategori', 'created_at']
search_fields = ['judul', 'konten']
prepopulated_fields = {'slug': ('judul',)}
raw_id_fields = ['penulis']
date_hierarchy = 'created_at'
ordering = ['-created_at']
inlines = [KomentarInline]
# Custom actions
actions = ['publish_artikel', 'draft_artikel']
def publish_artikel(self, request, queryset):
queryset.update(status='published')
self.message_user(request, f'{queryset.count()} artikel diterbitkan.')
publish_artikel.short_description = "Terbitkan artikel yang dipilih"
def draft_artikel(self, request, queryset):
queryset.update(status='draft')
self.message_user(request, f'{queryset.count()} artikel dijadikan draft.')
draft_artikel.short_description = "Jadikan draft"
@admin.register(Komentar)
class KomentarAdmin(admin.ModelAdmin):
list_display = ['nama', 'artikel', 'created_at', 'aktif']
list_filter = ['aktif', 'created_at']
search_fields = ['nama', 'email', 'isi']
list_editable = ['aktif']
Akses admin panel di http://127.0.0.1:8000/admin/ dan login menggunakan superuser yang sudah dibuat. Admin panel bisa dikustomisasi dengan ModelAdmin untuk mengatur tampilan, filter, pencarian, dan aksi massal.
8. Forms: Input Pengguna
Django menyediakan sistem Form yang powerful untuk menangani input pengguna, validasi data, dan rendering HTML form secara otomatis.
Membuat Form
from django import forms
from .models import Artikel, Komentar
class ArtikelForm(forms.ModelForm):
class Meta:
model = Artikel
fields = ['judul', 'slug', 'kategori', 'konten', 'gambar', 'status', 'tags']
widgets = {
'judul': forms.TextInput(attrs={
'class': 'form-control',
'placeholder': 'Judul artikel...'
}),
'slug': forms.TextInput(attrs={
'class': 'form-control',
'placeholder': 'slug-url-artikel'
}),
'konten': forms.Textarea(attrs={
'class': 'form-control',
'rows': 15,
'placeholder': 'Tulis konten artikel di sini...'
}),
'status': forms.Select(attrs={'class': 'form-control'}),
'tags': forms.CheckboxSelectMultiple(),
}
def clean_judul(self):
judul = self.cleaned_data.get('judul')
if len(judul) < 10:
raise forms.ValidationError("Judul minimal 10 karakter!")
return judul
class KomentarForm(forms.ModelForm):
class Meta:
model = Komentar
fields = ['nama', 'email', 'isi']
widgets = {
'nama': forms.TextInput(attrs={
'class': 'form-control',
'placeholder': 'Nama kamu...'
}),
'email': forms.EmailInput(attrs={
'class': 'form-control',
'placeholder': 'email@kamu.com'
}),
'isi': forms.Textarea(attrs={
'class': 'form-control',
'rows': 4,
'placeholder': 'Tulis komentar...'
}),
}
Template untuk Form
{% extends 'base.html' %}
{% block title %}Buat Artikel Baru{% endblock %}
{% block content %}
<h1>Buat Artikel Baru</h1>
<form method="POST" enctype="multipart/form-data">
{% csrf_token %}
{% for field in form %}
<div class="form-group">
<label for="{{ field.id_for_label }}">{{ field.label }}</label>
{{ field }}
{% if field.help_text %}
<small class="help-text">{{ field.help_text }}</small>
{% endif %}
{% for error in field.errors %}
<span class="error">{{ error }}</span>
{% endfor %}
</div>
{% endfor %}
{% if form.non_field_errors %}
<div class="alert alert-danger">
{% for error in form.non_field_errors %}
<p>{{ error }}</p>
{% endfor %}
</div>
{% endif %}
<button type="submit" class="btn btn-primary">Simpan Artikel</button>
</form>
{% endblock %}
9. Django ORM: Query Database
ORM (Object-Relational Mapping) memungkinkan Anda berinteraksi dengan database menggunakan Python, tanpa perlu menulis SQL. Django ORM mendukung PostgreSQL, MySQL, SQLite, dan Oracle.
from blog.models import Artikel, Kategori, Tag
from django.db.models import Count, Q, Avg
# CREATE β Membuat data baru
kategori = Kategori.objects.create(nama="Python", slug="python")
artikel = Artikel.objects.create(
judul="Tutorial Django Pertama",
slug="tutorial-django-pertama",
penulis=user,
kategori=kategori,
konten="Ini konten artikel...",
status='published'
)
# READ β Membaca data
# Semua data
semua_artikel = Artikel.objects.all()
# Filter berdasarkan kondisi
artikel_python = Artikel.objects.filter(kategori__nama="Python")
artikel_draft = Artikel.objects.filter(status='draft')
# Get satu objek
artikel = Artikel.objects.get(id=1)
artikel = Artikel.objects.get(slug="tutorial-django")
# exclude() β kebalikan dari filter
artikel_bukan_draft = Artikel.objects.exclude(status='draft')
# order_by() β urutkan
terbaru = Artikel.objects.order_by('-created_at')
terlama = Artikel.objects.order_by('created_at')
# count() β hitung
total = Artikel.objects.count()
total_published = Artikel.objects.filter(status='published').count()
# first() dan last()
artikel_pertama = Artikel.objects.first()
artikel_terakhir = Artikel.objects.last()
# exists() β cek apakah ada
ada_artikel = Artikel.objects.filter(status='published').exists()
# UPDATE β Mengubah data
artikel = Artikel.objects.get(id=1)
artikel.judul = "Judul Baru"
artikel.save()
# Bulk update
Artikel.objects.filter(status='draft').update(status='published')
# DELETE β Menghapus data
artikel = Artikel.objects.get(id=1)
artikel.delete()
# Bulk delete
Artikel.objects.filter(status='draft').delete()
Complex Queries
from django.db.models import Count, Q, F, Avg
# Q objects β untuk query OR/NOT
artikel = Artikel.objects.filter(
Q(kategori__nama="Python") | Q(kategori__nama="Django")
)
# Annotasi β menambah field kalkulasi
kategori_dengan_jumlah = Kategori.objects.annotate(
jumlah_artikel=Count('artikel')
).order_by('-jumlah_artikel')
# Related lookups (join)
artikel_dengan_kategori = Artikel.objects.select_related('kategori', 'penulis')
artikel_dengan_tags = Artikel.objects.prefetch_related('tags')
# Aggregasi
total_artikel = Artikel.objects.aggregate(
total=Count('id'),
rata_rata_komentar=Avg('komentar__id')
)
# Pagination dari queryset
from django.core.paginator import Paginator
artikel_list = Artikel.objects.filter(status='published')
paginator = Paginator(artikel_list, 10) # 10 per halaman
page = paginator.get_page(1) # Halaman pertama
10. URL Routing
URL routing di Django menentukan URL mana yang akan diarahkan ke view function yang mana. URL patterns didefinisikan di file urls.py.
from django.urls import path
from . import views
app_name = 'blog' # Namespace URL
urlpatterns = [
# Beranda
path('', views.beranda, name='beranda'),
# Detail artikel berdasarkan slug
path('artikel/<slug:slug>/', views.detail_artikel, name='detail'),
# Kategori
path('kategori/<slug:slug>/', views.kategori_detail, name='kategori'),
# Buat artikel
path('baru/', views.buat_artikel, name='buat_artikel'),
# API endpoints
path('api/artikel/', views.api_artikel, name='api_artikel'),
]
from django.contrib import admin
from django.urls import path, include
from django.conf import settings
from django.conf.urls.static import static
urlpatterns = [
path('admin/', admin.site.urls),
path('blog/', include('blog.urls')),
path('accounts/', include('django.contrib.auth.urls')),
]
# Serve media files in development
if settings.DEBUG:
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
11. Quiz: Uji Pemahamanmu!
Setelah membaca tutorial di atas, jawablah 5 pertanyaan berikut untuk menguji pemahamanmu tentang Django: