AI Tutor Gemini

Week 2: Django Models & Admin

Page
Style
Download Notes
Month 1: The Foundations (Weeks 1-4)
Module 1

Week 2: Django Models & Admin

Week 2: Django Models & Admin

Professional Django Developer Bootcamp

Learning Objectives

By the end of this week, students will be able to:

  1. Design and implement Django models with various field types
  2. Understand Django's ORM and database relationships
  3. Create and apply database migrations
  4. Configure and customize Django's admin interface
  5. Connect local Git repository to GitHub
  6. Build a complete blog application with Post and Comment models
  7. Manage database records through the admin panel

Day 1: Introduction to Django Models & ORM

Session 1: Understanding Django's ORM (2 hours)

What is an ORM?

Object-Relational Mapping (ORM) bridges the gap between Python objects and database tables:

  1. Without ORM: Write raw SQL queries
  2. With ORM: Use Python methods and attributes
# Raw SQL (what we avoid)
cursor.execute("SELECT * FROM blog_post WHERE published=True")

# Django ORM (what we use)
Post.objects.filter(published=True)

Django Model Basics

A Django model is a Python class that represents a database table:

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)
    
    def __str__(self):
        return self.title

This creates a database table with columns for title, content, and created_at.

Key Model Field Types:

# Text fields
title = models.CharField(max_length=100)      # Short text
content = models.TextField()                   # Long text
slug = models.SlugField(max_length=100)       # URL-friendly text

# Number fields
views = models.IntegerField(default=0)        # Whole numbers
rating = models.FloatField()                  # Decimal numbers

# Date/Time fields
created_at = models.DateTimeField(auto_now_add=True)  # Set once
updated_at = models.DateTimeField(auto_now=True)      # Update always
publish_date = models.DateField()                     # Date only

# Boolean fields
published = models.BooleanField(default=False)

# Choice fields
STATUS_CHOICES = [
    ('draft', 'Draft'),
    ('published', 'Published'),
]
status = models.CharField(max_length=10, choices=STATUS_CHOICES, default='draft')

Recommended Videos:

  1. Django Models Explained (15 min)
  2. Django ORM Tutorial (22 min)

Session 2: Creating Your First Models (2 hours)

Let's create a new Django project for our blog:

# Create new project
django-admin startproject blog_project
cd blog_project

# Create blog app
python manage.py startapp blog

# Activate virtual environment (if not already)
source django_bootcamp_env/bin/activate  # macOS/Linux
# or
django_bootcamp_env\Scripts\activate     # Windows

Step 1: Configure Settings

In blog_project/settings.py:

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'blog',  # Add your blog app
]

Step 2: Create Blog Models

In blog/models.py:

from django.db import models
from django.contrib.auth.models import User
from django.urls import reverse
from django.utils import timezone

class Post(models.Model):
    """Blog post model"""
    STATUS_CHOICES = [
        ('draft', 'Draft'),
        ('published', 'Published'),
    ]
    
    title = models.CharField(max_length=200)
    slug = models.SlugField(max_length=200, unique=True)
    author = models.ForeignKey(User, on_delete=models.CASCADE, related_name='posts')
    content = models.TextField()
    excerpt = models.TextField(max_length=300, blank=True, help_text="Brief description")
    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)
    published_at = models.DateTimeField(null=True, blank=True)
    
    class Meta:
        ordering = ['-created_at']  # Newest first
        verbose_name = 'Blog Post'
        verbose_name_plural = 'Blog Posts'
    
    def __str__(self):
        return self.title
    
    def get_absolute_url(self):
        return reverse('blog:post_detail', kwargs={'slug': self.slug})
    
    def save(self, *args, **kwargs):
        # Auto-set published_at when status changes to published
        if self.status == 'published' and not self.published_at:
            self.published_at = timezone.now()
        super().save(*args, **kwargs)

class Comment(models.Model):
    """Comment model for blog posts"""
    post = models.ForeignKey(Post, on_delete=models.CASCADE, related_name='comments')
    name = models.CharField(max_length=100)
    email = models.EmailField()
    content = models.TextField()
    created_at = models.DateTimeField(auto_now_add=True)
    is_approved = models.BooleanField(default=False)
    
    class Meta:
        ordering = ['created_at']
    
    def __str__(self):
        return f'Comment by {self.name} on {self.post.title}'

Understanding Model Relationships:

# ForeignKey: Many-to-One relationship
author = models.ForeignKey(User, on_delete=models.CASCADE)
# Many posts can belong to one user

# related_name allows reverse lookups
user.posts.all()  # Get all posts by this user

Recommended Videos:

  1. Django Model Fields Explained (18 min)
  2. Django ForeignKey Relationships (12 min)

Day 2: Database Migrations

Session 3: Understanding Django Migrations (2 hours)

What are Migrations?

Migrations are Django's way of propagating changes to your models into your database schema:

Model Changes → Migration Files → Database Schema Updates

Migration Commands:

# Create migration files for model changes
python manage.py makemigrations

# Apply migrations to database
python manage.py migrate

# View migration status
python manage.py showmigrations

# View SQL that will be executed
python manage.py sqlmigrate blog 0001

Step 1: Create Initial Migration

# Create migrations for our blog models
python manage.py makemigrations blog

# You'll see output like:
# Migrations for 'blog':
#   blog/migrations/0001_initial.py
#     - Create model Post
#     - Create model Comment

Step 2: Apply Migrations

# Apply all pending migrations
python manage.py migrate

# You'll see output confirming table creation:
# Operations to perform:
#   Apply all migrations: admin, auth, contenttypes, sessions, blog
# Running migrations:
#   Applying blog.0001_initial... OK

Migration File Structure:

Django creates migration files in blog/migrations/0001_initial.py:

from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion

class Migration(migrations.Migration):
    initial = True
    
    dependencies = [
        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
    ]
    
    operations = [
        migrations.CreateModel(
            name='Post',
            fields=[
                ('id', models.BigAutoField(auto_created=True, primary_key=True)),
                ('title', models.CharField(max_length=200)),
                ('content', models.TextField()),
                # ... more fields
            ],
        ),
        # ... more operations
    ]

Common Migration Scenarios:

# After adding a new field to a model
python manage.py makemigrations
python manage.py migrate

# After changing a field type
python manage.py makemigrations
python manage.py migrate

# View what migrations will do without applying them
python manage.py migrate --plan

Recommended Videos:

  1. Django Migrations Explained (16 min)
  2. Django Migration Best Practices (20 min)

Day 3: Django Admin Interface

Session 4: Setting Up Django Admin (2 hours)

Create Superuser Account:

# Create an admin user
python manage.py createsuperuser

# Follow the prompts:
# Username: admin
# Email address: admin@example.com
# Password: (choose a secure password)

Step 1: Register Models in Admin

In blog/admin.py:

from django.contrib import admin
from .models import Post, Comment

# Basic registration
admin.site.register(Post)
admin.site.register(Comment)

Step 2: Start Server and Access Admin

# Run development server
python manage.py runserver

# Visit: http://127.0.0.1:8000/admin/
# Login with your superuser credentials

Step 3: Customize Admin Interface

Enhanced blog/admin.py:

from django.contrib import admin
from .models import Post, Comment

@admin.register(Post)
class PostAdmin(admin.ModelAdmin):
    list_display = ['title', 'author', 'status', 'created_at']
    list_filter = ['status', 'created_at', 'author']
    search_fields = ['title', 'content']
    prepopulated_fields = {'slug': ('title',)}
    date_hierarchy = 'created_at'
    ordering = ['-created_at']
    list_editable = ['status']
    
    # Organize form fields
    fieldsets = (
        ('Post Content', {
            'fields': ('title', 'slug', 'content', 'excerpt')
        }),
        ('Metadata', {
            'fields': ('author', 'status', 'published_at'),
            'classes': ('collapse',)
        }),
    )
    
    # Filter by current user for non-superusers
    def get_queryset(self, request):
        qs = super().get_queryset(request)
        if request.user.is_superuser:
            return qs
        return qs.filter(author=request.user)

@admin.register(Comment)
class CommentAdmin(admin.ModelAdmin):
    list_display = ['name', 'post', 'created_at', 'is_approved']
    list_filter = ['is_approved', 'created_at']
    search_fields = ['name', 'email', 'content']
    date_hierarchy = 'created_at'
    list_editable = ['is_approved']
    readonly_fields = ['created_at']
    
    # Custom action to approve comments
    actions = ['approve_comments']
    
    def approve_comments(self, request, queryset):
        queryset.update(is_approved=True)
        self.message_user(request, f"Approved {queryset.count()} comments.")
    approve_comments.short_description = "Approve selected comments"

# Customize admin site headers
admin.site.site_header = "Blog Administration"
admin.site.site_title = "Blog Admin Portal"
admin.site.index_title = "Welcome to Blog Administration"

Admin Interface Features:

  1. list_display: Columns shown in the list view
  2. list_filter: Filter sidebar options
  3. search_fields: Fields searchable via search box
  4. prepopulated_fields: Auto-populate slug from title
  5. list_editable: Edit fields directly in list view
  6. fieldsets: Organize form fields into sections

Recommended Videos:

  1. Django Admin Customization (25 min)
  2. Django Admin Best Practices (18 min)

Day 4: GitHub Integration & Advanced Git

Session 5: Connecting to GitHub (2 hours)

Step 1: Create GitHub Repository

  1. Go to GitHub.com and sign in
  2. Click "New repository"
  3. Name it django-blog-bootcamp
  4. Keep it public for the course
  5. Don't initialize with README (we have local files)

Step 2: Connect Local Repository to GitHub

# Initialize Git if you haven't already
git init

# Add all files to staging
git add .

# Create initial commit
git commit -m "Initial blog project setup

- Create Django blog project with Post and Comment models
- Configure admin interface with custom settings
- Set up database migrations
- Add comprehensive model fields and relationships"

# Add GitHub remote repository
git remote add origin https://github.com/YOUR_USERNAME/django-blog-bootcamp.git

# Push to GitHub (first time)
git push -u origin main

Step 3: Verify GitHub Connection

# Check remote repositories
git remote -v

# You should see:
# origin  https://github.com/YOUR_USERNAME/django-blog-bootcamp.git (fetch)
# origin  https://github.com/YOUR_USERNAME/django-blog-bootcamp.git (push)

GitHub Best Practices:

  1. Meaningful commit messages: Describe what and why
  2. Regular commits: Don't wait until everything is perfect
  3. Ignore sensitive files: Use .gitignore

Create .gitignore file:

# Python
__pycache__/
*.py[cod]
*$py.class
*.so
.Python
env/
venv/
ENV/

# Django
*.log
local_settings.py
db.sqlite3
media/

# IDE
.vscode/
.idea/
*.swp
*.swo
*~

# OS
.DS_Store
Thumbs.db

Recommended Videos:

  1. Git and GitHub for Beginners (40 min)
  2. GitHub Workflow for Django Projects (15 min)

Day 5: Blog Application Development

Project 2: Complete Personal Blog

Step 1: Create Blog Views

In blog/views.py:

from django.shortcuts import render, get_object_or_404
from django.core.paginator import Paginator
from .models import Post, Comment

def post_list(request):
    """Display list of published posts"""
    posts = Post.objects.filter(status='published').order_by('-published_at')
    
    # Pagination - 5 posts per page
    paginator = Paginator(posts, 5)
    page_number = request.GET.get('page')
    posts = paginator.get_page(page_number)
    
    context = {
        'posts': posts,
        'title': 'Latest Blog Posts'
    }
    return render(request, 'blog/post_list.html', context)

def post_detail(request, slug):
    """Display individual post with comments"""
    post = get_object_or_404(Post, slug=slug, status='published')
    comments = post.comments.filter(is_approved=True)
    
    context = {
        'post': post,
        'comments': comments,
        'title': post.title
    }
    return render(request, 'blog/post_detail.html', context)

def about(request):
    """About the blog"""
    return render(request, 'blog/about.html', {'title': 'About'})

Step 2: Configure URLs

Create blog/urls.py:

from django.urls import path
from . import views

app_name = 'blog'

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

Update blog_project/urls.py:

from django.contrib import admin
from django.urls import path, include

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

Step 3: Create Templates

Create directory structure:

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

Base template (blog/templates/blog/base.html):

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>{% block title %}My Django Blog{% endblock %}</title>
    <style>
        * { margin: 0; padding: 0; box-sizing: border-box; }
        body { 
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; 
            line-height: 1.6; 
            color: #333; 
            background-color: #f4f4f4; 
        }
        .container { 
            max-width: 800px; 
            margin: 0 auto; 
            padding: 20px; 
            background: white; 
            min-height: 100vh; 
        }
        header { 
            background: #2c3e50; 
            color: white; 
            padding: 1rem; 
            margin-bottom: 2rem; 
            border-radius: 5px; 
        }
        nav a { 
            color: white; 
            text-decoration: none; 
            margin-right: 1rem; 
            padding: 0.5rem; 
            border-radius: 3px; 
            transition: background 0.3s; 
        }
        nav a:hover { 
            background: rgba(255, 255, 255, 0.2); 
        }
        h1 { color: #2c3e50; margin-bottom: 1rem; }
        h2 { color: #34495e; margin: 1.5rem 0 1rem; }
        .post-meta { 
            color: #7f8c8d; 
            font-size: 0.9em; 
            margin-bottom: 1rem; 
        }
        .post-excerpt { 
            color: #555; 
            margin-bottom: 1rem; 
        }
        .post-link { 
            color: #3498db; 
            text-decoration: none; 
            font-weight: bold; 
        }
        .post-link:hover { 
            text-decoration: underline; 
        }
        .comment { 
            background: #ecf0f1; 
            padding: 1rem; 
            margin: 1rem 0; 
            border-radius: 5px; 
            border-left: 4px solid #3498db; 
        }
        .pagination { 
            text-align: center; 
            margin: 2rem 0; 
        }
        .pagination a { 
            color: #3498db; 
            padding: 0.5rem 1rem; 
            text-decoration: none; 
            margin: 0 0.25rem; 
            border-radius: 3px; 
        }
        .pagination a:hover { 
            background: #3498db; 
            color: white; 
        }
    </style>
</head>
<body>
    <div class="container">
        <header>
            <h1>My Django Blog</h1>
            <nav>
                <a href="{% url 'blog:post_list' %}">Home</a>
                <a href="{% url 'blog:about' %}">About</a>
                <a href="{% url 'admin:index' %}">Admin</a>
            </nav>
        </header>
        
        <main>
            {% block content %}
            {% endblock %}
        </main>
    </div>
</body>
</html>

Post list template (blog/templates/blog/post_list.html):

{% extends 'blog/base.html' %}

{% block title %}{{ title }} - My Django Blog{% endblock %}

{% block content %}
<h1>{{ title }}</h1>

{% for post in posts %}
    <article style="border-bottom: 1px solid #eee; margin-bottom: 2rem; padding-bottom: 1rem;">
        <h2>
            <a href="{{ post.get_absolute_url }}" class="post-link">
                {{ post.title }}
            </a>
        </h2>
        
        <div class="post-meta">
            By {{ post.author.get_full_name|default:post.author.username }} on 
            {{ post.published_at|date:"F d, Y" }}
            • {{ post.comments.count }} comment{{ post.comments.count|pluralize }}
        </div>
        
        <div class="post-excerpt">
            {% if post.excerpt %}
                {{ post.excerpt }}
            {% else %}
                {{ post.content|truncatewords:30 }}
            {% endif %}
        </div>
        
        <a href="{{ post.get_absolute_url }}" class="post-link">Read more →</a>
    </article>
{% empty %}
    <p>No blog posts yet. Check back soon!</p>
{% endfor %}

<!-- Pagination -->
{% if posts.has_other_pages %}
    <div class="pagination">
        {% if posts.has_previous %}
            <a href="?page=1">« First</a>
            <a href="?page={{ posts.previous_page_number }}">‹ Previous</a>
        {% endif %}
        
        <span>Page {{ posts.number }} of {{ posts.paginator.num_pages }}</span>
        
        {% if posts.has_next %}
            <a href="?page={{ posts.next_page_number }}">Next ›</a>
            <a href="?page={{ posts.paginator.num_pages }}">Last »</a>
        {% endif %}
    </div>
{% endif %}
{% endblock %}

Post detail template (blog/templates/blog/post_detail.html):

{% extends 'blog/base.html' %}

{% block title %}{{ post.title }} - My Django Blog{% endblock %}

{% block content %}
<article>
    <h1>{{ post.title }}</h1>
    
    <div class="post-meta">
        By {{ post.author.get_full_name|default:post.author.username }} on 
        {{ post.published_at|date:"F d, Y" }}
        {% if post.updated_at > post.created_at %}
            (Updated: {{ post.updated_at|date:"F d, Y" }})
        {% endif %}
    </div>
    
    <div style="margin: 2rem 0; line-height: 1.8;">
        {{ post.content|linebreaks }}
    </div>
</article>

<hr style="margin: 3rem 0;">

<section>
    <h2>Comments ({{ comments.count }})</h2>
    
    {% for comment in comments %}
        <div class="comment">
            <h4>{{ comment.name }}</h4>
            <small class="post-meta">{{ comment.created_at|date:"F d, Y \a\t g:i A" }}</small>
            <p>{{ comment.content|linebreaks }}</p>
        </div>
    {% empty %}
        <p>No comments yet. Be the first to comment!</p>
    {% endfor %}
</section>

<p style="margin-top: 2rem;">
    <a href="{% url 'blog:post_list' %}" class="post-link">← Back to all posts</a>
</p>
{% endblock %}

Step 4: Add Sample Data via Admin

  1. Run the server: python manage.py runserver
  2. Go to admin: http://127.0.0.1:8000/admin/
  3. Create 2-3 blog posts with status "Published"
  4. Add some comments to the posts
  5. Test your blog at http://127.0.0.1:8000/

Recommended Videos:

  1. Django Templates Deep Dive (30 min)
  2. Django Pagination Tutorial (15 min)

Week 2 Assignment

Requirements:

  1. ✅ Create a Django blog project with Post and Comment models
  2. ✅ Implement proper model relationships using ForeignKey
  3. ✅ Create and apply database migrations
  4. ✅ Set up and customize Django admin interface
  5. ✅ Connect your project to a public GitHub repository
  6. ✅ Build a functional blog with post list and detail views
  7. ✅ Add pagination to the post list
  8. ✅ Style your templates with CSS

Submission Checklist:

  1. [ ] Push your complete blog project to GitHub
  2. [ ] Include at least 3 sample blog posts via admin
  3. [ ] Demonstrate working admin interface
  4. [ ] Show functional blog frontend
  5. [ ] Include meaningful commit messages
  6. [ ] Create a README.md with setup instructions

Bonus Challenges:

  1. Add a search functionality for blog posts
  2. Create a contact form model
  3. Add categories or tags to blog posts
  4. Implement a comment form on the frontend
  5. Add user profile pages

GitHub Repository Structure:

django-blog-bootcamp/
├── blog_project/
│   ├── blog_project/
│   ├── blog/
│   ├── manage.py
│   └── db.sqlite3
├── .gitignore
├── requirements.txt
└── README.md

Sample README.md:

# Django Blog Bootcamp - Week 2

A personal blog application built with Django as part of the Professional Django Developer Bootcamp.

## Features
- Blog post management
- Comment system
- Django admin interface
- Responsive design
- Pagination

## Setup Instructions
1. Clone the repository
2. Create virtual environment: `python -m venv venv`
3. Activate virtual environment
4. Install dependencies: `pip install -r requirements.txt`
5. Run migrations: `python manage.py migrate`
6. Create superuser: `python manage.py createsuperuser`
7. Start server: `python manage.py runserver`

## Technologies Used
- Django 4.x
- PostgreSQL (production)
- SQLite (development)
- HTML/CSS
- Git/GitHub

Week 2 Summary

Excellent progress! You've now mastered Django's data layer and built a complete blog application:

Django Models: Created complex models with various field types

Database Relationships: Implemented ForeignKey relationships

Django ORM: Learned to query and manipulate data with Python

Migrations: Managed database schema changes safely

Admin Interface: Customized Django's powerful admin panel

GitHub Integration: Connected your project to version control

Complete Blog: Built a functional blog with posts and comments

Key Concepts Mastered:

  1. Model field types and options
  2. Database relationships and queries
  3. Migration workflow and best practices
  4. Admin interface customization
  5. Template inheritance and pagination
  6. Git workflow with GitHub

Next week, we'll enhance your blog with advanced features like URL routing, template tags and filters, and implement a commenting system that users can interact with from the frontend. You'll learn about Django's powerful view system and template language in depth!

Before Week 3:

  1. Ensure your blog is pushed to GitHub
  2. Practice creating and editing posts through the admin
  3. Experiment with different admin customizations
  4. Try adding more sample data to test pagination