Setting Up Your First App

Let's build a real Django app from scratch. We'll create a blog application with posts, comments, and user authentication.

Step 1: Create the Project

Bash
pip install django
django-admin startproject myblog
cd myblog
python manage.py startapp posts

Step 2: Configure the App

Python
# myblog/settings.py — add 'posts' to INSTALLED_APPS
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'posts',
]

Step 3: Define Models

Python
# posts/models.py
from django.db import models
from django.contrib.auth.models import User
from django.utils import timezone


class Post(models.Model):
    title = models.CharField(max_length=200)
    slug = models.SlugField(unique=True)
    author = models.ForeignKey(User, on_delete=models.CASCADE, related_name='posts')
    body = models.TextField()
    created = models.DateTimeField(default=timezone.now)
    updated = models.DateTimeField(auto_now=True)
    published = models.BooleanField(default=False)
    tags = models.ManyToManyField('Tag', blank=True, related_name='posts')

    class Meta:
        ordering = ['-created']

    def __str__(self):
        return self.title

    def get_absolute_url(self):
        return f'/posts/{self.slug}/'


class Comment(models.Model):
    post = models.ForeignKey(Post, on_delete=models.CASCADE, related_name='comments')
    author = models.ForeignKey(User, on_delete=models.CASCADE)
    body = models.TextField()
    created = models.DateTimeField(auto_now_add=True)
    active = models.BooleanField(default=True)

    class Meta:
        ordering = ['created']

    def __str__(self):
        return f'Comment by {self.author} on {self.post}'


class Tag(models.Model):
    name = models.CharField(max_length=50, unique=True)

    def __str__(self):
        return self.name

Step 4: Create and Run Migrations

Bash
python manage.py makemigrations posts
python manage.py migrate

This creates the database tables:

Table Columns
posts_post id, title, slug, author_id, body, created, updated, published
posts_comment id, post_id, author_id, body, created, active
posts_tag id, name
posts_post_tags post_id, tag_id (junction table)

Step 5: Register with Admin

Python
# posts/admin.py
from django.contrib import admin
from .models import Post, Comment, Tag


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


@admin.register(Comment)
class CommentAdmin(admin.ModelAdmin):
    list_display = ['author', 'post', 'created', 'active']
    list_filter = ['active', 'created']
    actions = ['approve_comments']

    def approve_comments(self, request, queryset):
        queryset.update(active=True)


@admin.register(Tag)
class TagAdmin(admin.ModelAdmin):
    list_display = ['name']
    search_fields = ['name']

Step 6: Create Superuser and Test

Bash
python manage.py createsuperuser
# Enter username, email, password

python manage.py runserver
# Visit http://127.0.0.1:8000/admin/

💡 Tip: The related_name parameter on ForeignKey lets you do post.comments.all() to get all comments for a post. Choose descriptive names.

Next: URLs and Views