Welcome back to our series on building a complete blog from scratch. In Part 1, we set up a robust development environment using AWS Cloud9, installed Django, and prepared our project for its future home on Heroku. With that solid foundation in place, it’s time to start building the core of our blog: the backend.
In this second installment, we will dive into the heart of Django. We’ll design the data structure for our blog posts using Django’s models, which is a clean and Pythonic way to define your database schema. Then, we will unlock one of Django’s most powerful features: the automatic admin interface. This will give us a ready-made control panel for creating, editing, and managing our content without writing a single line of frontend code. Let’s get started.
What We’ll Accomplish in Part 2
- Create a new Django “app” to organize our blog-related code.
- Define a
Postmodel to structure our blog articles. - Use Django migrations to create the corresponding database table.
- Set up the Django admin to manage our posts easily.
- Configure our project to use a local database for development.
This part focuses entirely on the backend logic and data management. By the end, you’ll have a functional system for content creation, setting the stage for building the public-facing pages in the next part.
Step 1: Create a Django App for the Blog
In Django, a “project” is the entire web application. A project is made up of one or more “apps.” An app is a self-contained module that handles a specific function. For instance, you might have separate apps for a blog, a user forum, and a photo gallery. This structure keeps your code organized and reusable.
Let’s create an app named blog to hold all the logic for our blog posts.
- Make sure your virtual environment from Part 1 is active. You should see
(venv)at the start of your terminal prompt. If not, activate it:source venv/bin/activate - In the terminal, run the
startappcommand:python manage.py startapp blog
This command creates a newblogdirectory in your project. If you look inside, you’ll find several files:models.py,views.py,admin.py, and others. Each file serves a specific purpose, which we will explore throughout this series. - Register the new app: For Django to recognize our new
blogapp, we need to add it to theINSTALLED_APPSlist in our project’s settings.# 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 our new app here
]
INSTALLED_APPS = [
‘django.contrib.admin’,
‘django.contrib.auth’,
‘django.contrib.contenttypes’,
‘django.contrib.sessions’,
‘django.contrib.messages’,
‘django.contrib.staticfiles’,
‘blog’, # Add our new app here
]
Now, Django knows about our blog app and will include its models and other components in our project.
Step 2: Define the Blog Post Model
A “model” is a Python class that represents a table in your database. Django’s Object-Relational Mapper (ORM) uses these models to generate database queries for you, so you rarely need to write raw SQL.
We need a model to represent a blog post. What information does a post need? At a minimum, it should have a title, content, a publication date, and a unique “slug” for the URL.
Open the blog/models.py file and add the following code:
# blog/models.py
from django.db import models
from django.utils import timezone
from django.contrib.auth.models import User
class Post(models.Model):
title = models.CharField(max_length=200)
slug = models.SlugField(max_length=200, unique_for_date='publish')
author = models.ForeignKey(User, on_delete=models.CASCADE, related_name='blog_posts')
content = models.TextField()
publish = models.DateTimeField(default=timezone.now)
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
class Meta:
ordering = ('-publish',)
def __str__(self):
return str(self.title)
Let’s break this down:
Post(models.Model): OurPostclass inherits from Django’sModelclass, giving it all the database functionality.title: ACharFieldis for short text strings, perfect for a title.slug: ASlugFieldis for storing URL-friendly text (e.g., “my-first-post”). We’ve made it unique for the publication date, which prevents two posts from having the same slug on the same day.author: AForeignKeycreates a many-to-one relationship. In this case, one user (Usermodel) can be the author of many posts.on_delete=models.CASCADEmeans if a user is deleted, their posts will be deleted too.content: ATextFieldis for long-form text, which is ideal for the body of our blog post.publish: ADateTimeFieldthat defaults to the current time, indicating when the post goes live.createdandupdated: These fields automatically track when a post is created (auto_now_add=True) and when it was last modified (auto_now=True).Metaclass: This inner class provides metadata for the model.ordering = ('-publish',)tells Django to sort posts in reverse chronological order by default.__str__method: This is a standard Python method that defines what to display when the object is converted to a string. It makes our posts more readable in the admin interface.
Step 3: Create and Apply Database Migrations
Now that we have a model, we need to tell Django to create the corresponding table in the database. This is done through a two-step process: makemigrations and migrate.
- Create the Migration File: Run the
makemigrationscommand. This command inspects your models and creates a “migration” file—a Python script that describes the changes needed to update the database schema.python manage.py makemigrations blog
You should see output like:Migrations for ‘blog’:
blog/migrations/0001_initial.py
– Create model Post - Apply the Migration: The
migratecommand reads the migration files and applies the changes to the database. By default, Django uses a lightweight file-based database called SQLite, which is perfect for local development.python manage.py migrate
This command will apply all unapplied migrations, including those for Django’s built-in features (like authentication and sessions) and the new migration for ourPostmodel.
Your database is now up to date with a Post table ready to store data.
Step 4: Set Up the Django Admin Interface
The Django admin is one of its killer features. It automatically creates a professional user interface for managing your site’s content. All we need to do is register our Post model with it.
- Create a Superuser: First, we need an administrator account to log in. Run the
createsuperusercommand and follow the prompts to create your username, email, and password.python manage.py createsuperuser - Register the
PostModel: Open theblog/admin.pyfile and tell the admin site about ourPostmodel.# blog/admin.py
from django.contrib import admin
from .models import Post
@admin.register(Post)
class PostAdmin(admin.ModelAdmin):
list_display = (‘title’, ‘slug’, ‘author’, ‘publish’)
list_filter = (‘publish’, ‘author’)
search_fields = (‘title’, ‘content’)
prepopulated_fields = {‘slug’: (‘title’,)}
date_hierarchy = ‘publish’
ordering = (‘-publish’,)
Here’s what this customPostAdminclass does:@admin.register(Post): This decorator registers ourPostmodel with the admin site.list_display: Customizes the columns shown on the post list page.list_filter: Adds a sidebar for filtering posts by date or author.search_fields: Adds a search bar to look for posts by title or content.prepopulated_fields: Automatically fills theslugfield based on thetitleas you type. This is incredibly convenient!
- Explore the Admin: Start the development server again:python manage.py runserver 0.0.0.0:8080
Now, open your site preview and navigate to the/admin/URL (e.g.,your-cloud9-url.com/admin/). Log in with the superuser credentials you created.
If you encounter the error “Origin checking failed – https://yourawsaddress.amazonaws.com does not match any trusted origins,” it means that Django’s CSRF (Cross-Site Request Forgery) protection is blocking the request due to an unrecognized origin. To resolve this, you need to add your Cloud9 workspace URL to the CSRF_TRUSTED_ORIGINS setting in your Django project.
- Open your
settings.pyfile in your Django project. - Locate or create the
CSRF_TRUSTED_ORIGINSlist. - Add your Cloud9 URL, including the
https://prefix, within the list. For example:CSRF_TRUSTED_ORIGINS = [‘https://yourawsaddress’] - Save the file and restart your Django development server.
This will inform Django to trust requests from your Cloud9 URL and resolve the error.
You will see the Django admin dashboard. Under “Blog,” you’ll find “Posts.” Click “Add post” and explore the form. You can create your first blog post right now! Notice how the slug is automatically generated as you type the title.
Once these steps have been followed, refresh your Django admin dashboard, and “Blog” along with “Posts” should appear.
Saving Your Work: Git Push for Part 2
Now that you’ve finished Part 2, you’ll want to save your progress and update your remote repository. Follow these steps to use Git for version control:
- Open your terminal and make sure you’re in your project directory.
- Add all your changes:git add .
- Commit the changes with a descriptive message:git commit -m “Finish Part 2: Add Post model and admin setup”
- Push your updates to the remote repository (replace
mainwith your branch name if different):git push origin main
This will back up your progress and make it easy to share or deploy later.
Conclusion and What’s Next
In this part, we built the entire data management layer for our blog. We designed a Post model, created the database structure with migrations, and set up a powerful admin interface for content creation. This backend system is the engine that will power our blog.
We are now perfectly positioned to build the frontend. In Part 3 of this series, we will focus on bringing our posts to the public. We will create the views and templates needed to display a list of all blog posts and a detail page for a single post. Get ready to write some HTML and see your blog come to life!





