Django is a "batteries-included" framework that comes with built-in scaffolding provided by the startproject
command and the general concepts of apps. But, actually, Django provides incredible flexibility if desired around structure.
Consider how to architect a single page website: what's the simplest way to do it? Off the top of my head, at least 5 approaches come to mind. Because if you really think about it, apps are not mandatory at all but rather an organizational convenience that help reason about the codebase. Ditto for independent views files. We don't even need templates for a simple site when we can return strings in a view. Pretty much a urls.py
file is the only "mandatory" part of the exercise.
Setup
On your command line, create a new directory called five
and make a new Django project. We'll be using Pipenv here and calling our top-level directory config
.
$ pipenv install django==3.0.7
$ pipenv shell
(five) $ django-admin startproject config .
(five) $ python manage.py migrate
(five) $ python manage.py runserver
Navigate over to the Django homepage at http://127.0.0.1:8000/ to confirm everything is working.
1. App + TemplateView
The "traditional" approach would be to create a dedicated app for static pages and use TemplateView, a generic class-based view provided by Django.
First, create the app which can be called whatever we choose. In this case, it'll be pages
. Make sure you've stopped the local server by typing Control+c
.
(five) $ python manage.py startapp pages
Add the new app to our INSTALLED_APPS
setting.
# config/settings.py
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'pages.apps.PagesConfig', # new
]
Update the config/urls.py
file by importing include
and adding a path for the pages app.
# config/urls.py
from django.contrib import admin
from django.urls import path, include # new
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('pages.urls')), # new
]
Ok, now what? We need a urls.py
file within our app, a views file, and a template file.
Let's start with the pages/urls.py
file. First, create it by using the touch
command if you're on a Mac.
(five) $ touch pages/urls.py
The code includes a single path at home1/
, a HomePageView
we haven't created yet, and the URL name of home
.
# pages/urls.py
from django.urls import path
from .views import HomePageView
urlpatterns = [
path('home1/', HomePageView.as_view(), name='home1'),
]
The view imports TemplateView
and points to a template called home.html
.
# pages/views.py
from django.views.generic import TemplateView
class HomePageView(TemplateView):
template_name = 'pages/home.html'
And finally we need a template. Create a templates
directory within the pages
app, then a new directory called pages
, and finally the template called home.html
.
<!- pages/templates/pages/home.html ->
<h1>Hello World</h1>
Start up the local server again with python manage.py runserver
and navigate to http://127.0.0.1:8000/home1/.
This approach works well for large projects where you want a dedicated pages
app for static pages. But since this article is for educational purposes, let's see 4 more ways we can achieve the same outcome.
2. Function-Based View
The first thing we could do is swap out the generic class-based TemplateView
for a function-based view. Django allows for either using function-based or class-based views.
We'll import render
at the top of pages/views.py
and add a new view, home_page_view2
, which requests the same template home.html
.
# pages/views.py
from django.shortcuts import render # new
from django.views.generic import TemplateView
class HomePageView(TemplateView):
template_name = 'pages/home.html'
def home_page_view2(request): # new
return render(request, 'pages/home.html')
This new view will have its own path, home2/
, in the pages/urls.py
file.
# pages/urls.py
from django.urls import path
from .views import HomePageView, home_page_view2 # new
urlpatterns = [
path('home1/', HomePageView.as_view(), name='home1'),
path('home2/', home_page_view2, name='home2'), # new
]
Make sure the server is still running and navigate to http://127.0.0.1:8000/home2/ to see the new version of our homepage in action.
3. No Template!
A third approach would be to remove the template entirely, since all we're displaying is a short amount of text. We can create a new view that does this by importing HttpResponse
and hard-coding the string Hello World
to be returned.
# pages/views.py
from django.shortcuts import render, HttpResponse # new
from django.views.generic import TemplateView
class HomePageView(TemplateView):
template_name = 'pages/home.html'
def home_page_view2(request):
return render(request, 'pages/home.html')
def home_page_view3(request): # new
return HttpResponse('Hello World')
# pages/urls.py
from django.urls import path
from .views import HomePageView, home_page_view2, home_page_view3 # new
urlpatterns = [
path('home1/', HomePageView.as_view(), name='home1'),
path('home2/', home_page_view2, name='home2'),
path('home3/', home_page_view3, name='home3'), # new
]
Navigate to http://127.0.0.1:8000/home3/ in your web browser to see this third version in action.
4. No App!
We don't have to use apps, actually, if we don't want to. It's possible to create a "Hello World" page solely within our project-level config
directory.
Here is all the code we need in just a single file:
# config/urls.py
from django.contrib import admin
from django.urls import path, include
from django.views.generic import TemplateView # new
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('pages.urls')),
path('home4/', TemplateView.as_view(template_name='pages/home.html'), name='home4'), # new
]
We've imported TemplateView
at the top, create a new URL path, pointed to the existed template within the path, and provided an updated URL name of home4
.
Navigate to http://127.0.0.1:8000/home4/ to see this in action!
5. Models
A fifth approach would be to use the database to display our "Hello World" message. That involves adding a models.py
file and leveraging the built-in DetailView.
Create a new file called pages/models.py
. We'll only add a single field, text
, to the model called Post
. Adding an optional __str__
method means the model will display nicely within the Django admin.
# pages/models.py
from django.db import models
class Post(models.Model):
text = models.TextField()
def __str__(self):
return self.text[:50]
Since we made a change to our database models, it's time for a migrations file and then to invoke migrate
to update the database.
(five) $ python manage.py makemigrations
(five) $ python manage.py migrate
We'll want a basic pages/admin.py
file to see our pages
app within the admin so update pages/admin.py
as follows:
# pages/admin.py
from django.contrib import admin
from .models import Post
admin.site.register(Post)
Create a superuser account and start up the server again.
(five) $ python manage.py createsuperuser
(five) $ python manage.py runserver
Log in to the admin at http://127.0.0.1:8000/admin/ and click on the link for Posts
under Pages
.
Click on "+Add" next to it. Add our text which will be "Hello World" and click the "Save" button in the bottom right.
Success! We've added our first post to the database.
To display this post we'll need a urls file, a views file, and a template file. Start with pages/urls.py
where we'll import a new view called HomeDetailView
and add a path for it.
# pages/urls.py
from django.urls import path
from .views import HomePageView, home_page_view2, home_page_view3, HomeDetailView # new
urlpatterns = [
path('home1/', HomePageView.as_view(), name='home1'),
path('home2/', home_page_view2, name='home2'),
path('home3/', home_page_view3, name='home3'),
path('home/<int:pk>/', HomeDetailView.as_view(), name='post_detail'), # new
]
The pages/views.py
file needs to import DetailView
at the top and our Post
model. Then create HomeDetailView
which references our Post
model and a new pages/home5.html
template.
# pages/views.py
from django.shortcuts import render, HttpResponse
from django.views.generic import TemplateView, DetailView # new
from .models import Post # new
class HomePageView(TemplateView):
template_name = 'pages/home.html'
def home_page_view2(request):
return render(request, 'pages/home.html')
def home_page_view3(request):
return HttpResponse('Hello, World!)
class HomeDetailView(DetailView): # new
model = Post
template_name = 'pages/home5.html'
The last step is to create a new template called home5.html
that will display the text
field from our model.
<!-- pages/templates/home5.html -->
{{ post.text }}
Make sure the local server is running python manage.py runserver
and navigate to http://127.0.0.1:8000/home/1/ to see our 5th approach in action.
And there it is!
Conclusion
If you understand these five approaches you'll be well on your way to understanding all that Django has to offer. And you can start exploring additional ways to tackle this arbitrary challenge, for example, taking a look at base view class, get_context_data, HTTPRequest and likely many others.