Django
01 / 03

Models, Views & URLs

Models, Views & URLs

Project Setup

pip install django djangorestframework
django-admin startproject myproject .
python manage.py startapp users

# Run dev server
python manage.py runserver

# Migrations
python manage.py makemigrations
python manage.py migrate
python manage.py createsuperuser

# Shell
python manage.py shell

Models

# users/models.py
from django.db import models
from django.contrib.auth.models import AbstractUser

class User(AbstractUser):
    bio = models.TextField(blank=True)
    avatar = models.ImageField(upload_to='avatars/', null=True, blank=True)
    created_at = models.DateTimeField(auto_now_add=True)

class Category(models.Model):
    name = models.CharField(max_length=100, unique=True)
    slug = models.SlugField(unique=True)

    class Meta:
        verbose_name_plural = 'categories'
        ordering = ['name']

    def __str__(self):
        return self.name

class Post(models.Model):
    STATUS_DRAFT = 'draft'
    STATUS_PUBLISHED = 'published'
    STATUS_CHOICES = [
        (STATUS_DRAFT, 'Draft'),
        (STATUS_PUBLISHED, 'Published'),
    ]

    title = models.CharField(max_length=200)
    slug = models.SlugField(unique=True)
    author = models.ForeignKey(User, on_delete=models.CASCADE, related_name='posts')
    category = models.ForeignKey(Category, on_delete=models.SET_NULL, null=True)
    tags = models.ManyToManyField('Tag', blank=True)
    body = models.TextField()
    status = models.CharField(max_length=20, choices=STATUS_CHOICES, default=STATUS_DRAFT)
    view_count = models.PositiveIntegerField(default=0)
    published_at = models.DateTimeField(null=True, blank=True)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    class Meta:
        ordering = ['-created_at']
        indexes = [models.Index(fields=['status', '-created_at'])]

    def __str__(self):
        return self.title

# Field types reference
# CharField, TextField, IntegerField, FloatField, DecimalField
# BooleanField, DateField, DateTimeField, EmailField, URLField
# ForeignKey, OneToOneField, ManyToManyField
# ImageField, FileField, JSONField
# on_delete: CASCADE, SET_NULL, SET_DEFAULT, PROTECT, DO_NOTHING

ORM Queries

from users.models import Post, User

# Create
post = Post.objects.create(title='Hello', author=user, slug='hello')
post = Post(title='Hello', author=user)
post.save()

# Read
Post.objects.all()
Post.objects.filter(status='published')
Post.objects.filter(author__username='alice')     # traverse FK
Post.objects.filter(title__icontains='hello')     # field lookup
Post.objects.filter(created_at__gte=datetime(2024, 1, 1))
Post.objects.exclude(status='draft')
Post.objects.get(id=1)                            # raises DoesNotExist if not found
Post.objects.get_or_create(slug='hello', defaults={'title': 'Hello'})

# Chaining
Post.objects.filter(status='published').order_by('-created_at')[:10]
Post.objects.select_related('author', 'category')  # JOIN (ForeignKey)
Post.objects.prefetch_related('tags')              # separate query (M2M)

# Aggregation
from django.db.models import Count, Avg, Sum, Max, Q

Post.objects.filter(status='published').count()
Post.objects.aggregate(avg_views=Avg('view_count'))

Post.objects.values('author__username').annotate(post_count=Count('id'))

# Q objects — complex queries
Post.objects.filter(Q(status='published') | Q(author=user))
Post.objects.filter(Q(status='published') & ~Q(category=None))

# Update
Post.objects.filter(author=user).update(status='published')
# post.save() — always triggers signals; .update() does not

# Delete
Post.objects.filter(status='draft', created_at__lt=cutoff).delete()

Views & URLs

# views.py (function-based)
from django.shortcuts import render, get_object_or_404, redirect
from django.http import JsonResponse
from django.contrib.auth.decorators import login_required

def post_list(request):
    posts = Post.objects.filter(status='published').select_related('author')
    return render(request, 'posts/list.html', {'posts': posts})

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

@login_required
def create_post(request):
    if request.method == 'POST':
        form = PostForm(request.POST)
        if form.is_valid():
            post = form.save(commit=False)
            post.author = request.user
            post.save()
            return redirect('post_detail', slug=post.slug)
    else:
        form = PostForm()
    return render(request, 'posts/form.html', {'form': form})

# urls.py
from django.urls import path, include
from . import views

urlpatterns = [
    path('', views.post_list, name='post_list'),
    path('<slug:slug>/', views.post_detail, name='post_detail'),
    path('create/', views.create_post, name='create_post'),
]

# myproject/urls.py
urlpatterns = [
    path('admin/', admin.site.urls),
    path('posts/', include('posts.urls')),
    path('api/', include('api.urls')),
]

Keep your own version of these notes — editable, searchable, and organised by your stack.

Start free