In the previous parts of this series, you successfully deployed your Django application to Heroku. You’ve configured your environment, pushed your code, and can access the Django admin interface at yourapp.herokuapp.com/admin. However, visiting your main URL likely presents you with a discouraging “Not Found” page. This is a common and expected step in the process. Your app is running, but you haven’t told Django what to show at the root URL or any other custom paths.
This guide, Part 4, will walk you through the final steps to bring your website to life. We will create the necessary views, templates, and URL patterns to display a homepage, a blog page, an about page, and a contact page. By the end, you’ll have a functional, multi-page website accessible to anyone on the internet.
Why Do I See a “Not Found” Error?
The “Not Found” error appears because Django’s URL dispatcher, defined in your urls.py file, doesn’t have an entry for the root path (/). Out of the box, a new Django project only defines the path for the /admin/ site. When a user visits your main domain, Django looks for a matching URL pattern, finds nothing, and returns a 404 error.
Our mission is to create these URL patterns and connect them to views that render HTML pages. We’ll build out a few essential pages to transform your project into a proper website.
Step 1: Create a ‘main’ App for Core Pages
It’s a Django best practice to organize different functionalities into separate apps. While you might have a blog app for your posts, general pages like the homepage, about, and contact sections fit well into a dedicated main or core app.
In your Cloud9 terminal, within your project’s root directory (where manage.py is located), create a new app:
python manage.py startapp main
Now, register this new app with your project. Open your settings.py file and add 'main' to the INSTALLED_APPS list:
# settings.py
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'main', # Add your new app here
# ... any other apps you have
]
Setting up Dual Database Configuration (Local & Production)
To ensure your project works seamlessly both locally and in production (such as Heroku), you’ll want to configure Django to use SQLite by default for local development, and switch automatically to a cloud database when the DATABASE_URL environment variable is detected.
Open your settings.py and update your database settings as follows:
# settings.py
import os
import dj_database_url
DATABASE_URL = os.environ.get('DATABASE_URL')
if DATABASE_URL:
# For production (Heroku), use the database specified in DATABASE_URL
DATABASES = {
'default': dj_database_url.config(default=DATABASE_URL)
}
else:
# For local development, default to SQLite
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
}
}
Note: Make sure you have the dj-database-url package installed:
pip install dj-database-url
And add it to your requirements.txt:
pip freeze > requirements.txt
If you can not access the /admin/ site,
Summary Table
| Problem | Cause | Fixed By |
|---|---|---|
| Bad Request (400) | Wrong ALLOWED_HOSTS / CSRF_TRUSTED_ORIGINS | Wildcard settings (*.herokuapp.com, etc.) |
| 500 error on /admin/login/ | Django refuses to serve admin static files | –insecure –nostatic |
| Broken/missing CSS | collectstatic copied files incorrectly | –link flag (symlinks instead of copy) |
| Can’t access from Cloud9 preview |
The Magic Local Development Command (Explained Piece by Piece)
Bash
python manage.py collectstatic --noinput --clear --link && \
python manage.py runserver --insecure --nostatic 0.0.0.0:8080
Let’s break it down:
Part 1: collectstatic –noinput –clear –link
Bash
python manage.py collectstatic --noinput --clear --link
This command copies all static files (CSS, JS, images) from your apps (including Django’s own admin app) into one folder that Django can serve.
| Flag | What it does | Why we need it in Cloud9 |
|---|---|---|
| –noinput | Skips the “Are you sure?” prompt | So the command runs without waiting for you to type yes |
| –clear | Deletes the old staticfiles/ folder before collecting new files | Prevents old/broken files from lingering |
| –link | Creates symbolic links instead of copying files | This is the real hero. Copying can break internal paths in Django’s admin files on Linux/Cloud9. Symlinks keep the original paths intact → styles load correctly |
Without –link, you often get broken admin CSS even after collectstatic!
Part 2: runserver –insecure –nostatic 0.0.0.0:8080
Bash
python manage.py runserver --insecure --nostatic 0.0.0.0:8080
| Flag | What it does | Why we need it in Django 4.2 + Cloud9 |
|---|---|---|
| –insecure | Serves static files even when DEBUG=False | We usually run with DEBUG=True, but Django still sometimes refuses |
| –nostatic | Disables the built-in static file warning/serving changes in Django 4.2+ | Django 4.2+ refuses to serve static files by default in some cases → 500 error |
| 0.0.0.0:8080 | Makes the server accessible from outside the container (required for Cloud9 preview) | Cloud9 can’t show 127.0.0.1:8000 — it needs port 8080 + 0.0.0.0 |
Important: Starting with Django 4.2, using just runserver is no longer enough for a perfectly styled admin in many environments. You must combine –insecure and –nostatic.
Testing Your Database Setup
- Local Development:
When you run your app locally withpython manage.py runserver, Django will use SQLite by default. You can verify this in the Django shell:python manage.py shell
>>> from django.conf import settings
>>> settings.DATABASES
# Output should show the SQLite configuration - Production (Heroku):
On Heroku, after you add a database add-on (like Heroku Postgres), theDATABASE_URLwill be set automatically in your app’s environment. Django will pick up this variable and use the cloud database. You can confirm this through the Heroku CLI:heroku config:get DATABASE_URL -a your-app-name
git add .
git commit -m “Allow local sql3 database for development”
git push heroku main
Step 2: Define the Views
Views are Python functions or classes that take a web request and return a web response. In our case, the response will be the HTML content of our pages.
Open the main/views.py file and add the following code. We’ll start by defining simple functions for our homepage, about page, and contact page.
# main/views.py
from django.shortcuts import render
def home(request):
"""
View for the homepage.
"""
return render(request, 'main/home.html')
def about(request):
"""
View for the about page.
"""
return render(request, 'main/about.html')
def contact(request):
"""
View for the contact page.
"""
return render(request, 'main/contact.html')
# We'll assume you have a separate 'blog' app for this view.
# If not, you can place this in 'main/views.py' for now.
# from django.shortcuts import render (already imported)
# def blog(request):
# """
# View for the blog listing page.
# """
# # In a real app, you would fetch blog posts from the database here
# # posts = Post.objects.all()
# # context = {'posts': posts}
# return render(request, 'blog/blog_list.html')
Each function uses Django’s render shortcut, which takes the request object and the path to an HTML template, then returns an HttpResponse object with that rendered template.
Step 3: Create the HTML Templates
Our views are now looking for HTML files that don’t exist yet. Let’s create them. Inside your main app directory, create a new folder named templates, and inside that, another folder named main.
Your directory structure should look like this:
main/
├── templates/
│ └── main/
│ ├── home.html
│ ├── about.html
│ └── contact.html
└── views.py
...
Now, create the HTML files with some basic content.
main/templates/main/home.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Welcome to My Website</title>
</head>
<body>
<h1>Homepage</h1>
<p>Welcome! Your Django site is now live on Heroku.</p>
</body>
</html>
main/templates/main/about.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>About Us</title>
</head>
<body>
<h1>About Page</h1>
<p>This is the page that tells visitors about you or your project.</p>
</body>
</html>
main/templates/main/contact.html:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Contact Us</title>
</head>
<body>
<h1>Contact Page</h1>
<p>Here's how to get in touch.</p>
</body>
</html>
Step 4: Connect the URLs
The final piece of the puzzle is to connect URLs to the views we created. This requires two steps: creating a urls.py for our main app and including it in the project’s main urls.py file.
First, create a new file named urls.py inside your main app directory.
main/urls.py:
from django.urls import path
from . import views
urlpatterns = [
path('', views.home, name='home'),
path('about/', views.about, name='about'),
path('contact/', views.contact, name='contact'),
]
This file defines the URL patterns for the main app. Notice the empty string '' for the homepage view.
Next, we need to tell our project-level URL configuration to look at this new file. Open your project’s urls.py file (the one in the same directory as settings.py).
myproject/urls.py:
from django.contrib import admin
from django.urls import path, include # Make sure 'include' is imported
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('main.urls')), # Include the URLs from the main app
# path('blog/', include('blog.urls')), # You would add this for your blog app
]
By using include('main.urls') with an empty path '', we are telling Django to send any request that matches the root (yourapp.herokuapp.com/) to the main/urls.py file for further processing. There, the empty path '' matches views.home, successfully routing the user to your homepage.
Step 5: Deploy the Changes
You’ve made significant changes to your codebase. It’s time to push them to Heroku so they become live.
Run the following commands in your terminal:
# Stage all your new and modified files
git add .
# Commit the changes with a descriptive message
git commit -m "Add main app with home, about, and contact pages"
# Push the changes to Heroku
git push heroku main
Heroku will automatically rebuild your application with the new code. Once the deployment is complete, navigate to https://yourapp.herokuapp.com/.
You should now see your “Homepage” content instead of the “Not Found” error! You can also test your other pages by visiting https://yourapp.herokuapp.com/about/ and https://yourapp.herokuapp.com/contact/.
Conclusion and Next Steps
Congratulations! You have successfully bridged the gap between a backend-only application and a functional website with live pages. By creating a dedicated app for your core pages, defining views, building templates, and mapping the URL patterns, you have established the fundamental structure of your Django site.
From here, you can continue to build out your site’s functionality:
- Create a Base Template: Avoid repeating the
<html>,<head>, and<body>structure on every page by using Django’s template inheritance. - Style Your Pages: Integrate CSS to make your site visually appealing. Remember to configure
whitenoiseto serve your static files. - Build Out the Blog: If you have a
blogapp, follow the same pattern to create views and templates for listing posts and viewing individual post details.
You’re no longer stuck at the admin login. Your project is now a real website on the internet, ready for you to share and build upon.






Leave a Reply