1.Overview
As the internet has evolved , the line between website and mobile apps has blurred. we can use Django to build a single project that serves a dynamic website as well as a set of mobile apps.
Django is Python's most popular web framework, a set of tools designed for building interactive web applications. We will learn how to use Django to build a project called Learning Log, an online journal system that lets us keep track of information .
We will write a specification for this project, and then define models for the data tha app will work with.We will use Django's admin system to enter some initial data, and then write views and templates so Django can build the site's pages.
2.Setting Up a Project
1).Writing a Spec
We'll write a web app called Learning Log that allows users to log the topics they're interested in and make journal entries as they learn about each topic. The Learning Log home page will
describe the site and invite users to either register or log in. Once logged in, a user can create new topics, add new entries, and read and edit existing entries.
2).Creating a Virtual Environment
bash
maxwellpan@192 learning_log % python3 -m venv ll_env
maxwellpan@192 learning_log % ls -ltr
total 0
drwxr-xr-x 6 maxwellpan staff 192 Apr 17 18:50 ll_env
maxwellpan@192 learning_log % cd ll_env
maxwellpan@192 ll_env % ls -ltr
total 8
drwxr-xr-x 3 maxwellpan staff 96 Apr 17 18:50 include
drwxr-xr-x 3 maxwellpan staff 96 Apr 17 18:50 lib
-rw-r--r-- 1 maxwellpan staff 372 Apr 17 18:50 pyvenv.cfg
drwxr-xr-x 12 maxwellpan staff 384 Apr 17 18:50 bin
maxwellpan@192 ll_env %
3).Activating the Virtual Environment
bash
maxwellpan@192 bin % pwd
/Users/maxwellpan/selflearn/py_practise_learn/python_crash_course/chapter18/learning_log/ll_env/bin
maxwellpan@192 bin % ls -ltr
total 72
lrwxr-xr-x 1 maxwellpan staff 65 Apr 17 18:50 python3.12 -> /Library/Frameworks/Python.framework/Versions/3.12/bin/python3.12
lrwxr-xr-x 1 maxwellpan staff 10 Apr 17 18:50 python -> python3.12
lrwxr-xr-x 1 maxwellpan staff 10 Apr 17 18:50 python3 -> python3.12
-rwxr-xr-x 1 maxwellpan staff 315 Apr 17 18:50 pip
-rwxr-xr-x 1 maxwellpan staff 315 Apr 17 18:50 pip3
-rwxr-xr-x 1 maxwellpan staff 315 Apr 17 18:50 pip3.12
-rw-rw-r-- 1 maxwellpan staff 2272 Apr 17 18:50 activate.fish
-rw-rw-r-- 1 maxwellpan staff 993 Apr 17 18:50 activate.csh
-rw-rw-r-- 1 maxwellpan staff 9033 Apr 17 18:50 Activate.ps1
-rw-rw-r-- 1 maxwellpan staff 2182 Apr 17 18:50 activate
maxwellpan@192 bin % source activate
(ll_env) maxwellpan@192 bin %
To stop using a virtual environment, enter deactivate:
bash
maxwellpan@192 bin % source activate
(ll_env) maxwellpan@192 bin % deactive
zsh: command not found: deactive
(ll_env) maxwellpan@192 bin % pwd
/Users/maxwellpan/selflearn/py_practise_learn/python_crash_course/chapter18/learning_log/ll_env/bin
(ll_env) maxwellpan@192 bin %
4)Installing Django
bash
maxwellpan@192 learning_log % pwd
/Users/maxwellpan/selflearn/py_practise_learn/python_crash_course/chapter18/learning_log
maxwellpan@192 learning_log % ls -ltr
total 0
drwxr-xr-x 6 maxwellpan staff 192 Apr 17 18:50 ll_env
maxwellpan@192 learning_log % cd ll_env
maxwellpan@192 ll_env % cd bin
maxwellpan@192 bin % source activate
(ll_env) maxwellpan@192 bin % pip3 install --upgrade pip
Requirement already satisfied: pip in /Users/maxwellpan/selflearn/py_practise_learn/python_crash_course/chapter18/learning_log/ll_env/lib/python3.12/site-packages (24.0)
(ll_env) maxwellpan@192 bin % pip3 install django
Collecting django
Downloading Django-5.0.4-py3-none-any.whl.metadata (4.1 kB)
Collecting asgiref<4,>=3.7.0 (from django)
Downloading asgiref-3.8.1-py3-none-any.whl.metadata (9.3 kB)
Collecting sqlparse>=0.3.1 (from django)
Downloading sqlparse-0.5.0-py3-none-any.whl.metadata (3.9 kB)
Downloading Django-5.0.4-py3-none-any.whl (8.2 MB)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 8.2/8.2 MB 8.8 MB/s eta 0:00:00
Downloading asgiref-3.8.1-py3-none-any.whl (23 kB)
Downloading sqlparse-0.5.0-py3-none-any.whl (43 kB)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 44.0/44.0 kB 3.5 MB/s eta 0:00:00
Installing collected packages: sqlparse, asgiref, django
Successfully installed asgiref-3.8.1 django-5.0.4 sqlparse-0.5.0
(ll_env) maxwellpan@192 bin %
5)Creating a Project in Django
python
(ll_env) maxwellpan@192 learning_log % pwd
/Users/maxwellpan/selflearn/py_practise_learn/python_crash_course/chapter18/learning_log
(ll_env) maxwellpan@192 learning_log % django-admin startproject ll_project .
(ll_env) maxwellpan@192 learning_log % ls
ll_env ll_project manage.py
(ll_env) maxwellpan@192 learning_log % ls ll_project
__init__.py asgi.py settings.py urls.py wsgi.py
(ll_env) maxwellpan@192 learning_log %
6).Creating the Database
bash
(ll_env) maxwellpan@192 learning_log % python3 manage.py migrate
Operations to perform:
Apply all migrations: admin, auth, contenttypes, sessions
Running migrations:
Applying contenttypes.0001_initial... OK
Applying auth.0001_initial... OK
Applying admin.0001_initial... OK
Applying admin.0002_logentry_remove_auto_add... OK
Applying admin.0003_logentry_add_action_flag_choices... OK
Applying contenttypes.0002_remove_content_type_name... OK
Applying auth.0002_alter_permission_name_max_length... OK
Applying auth.0003_alter_user_email_max_length... OK
Applying auth.0004_alter_user_username_opts... OK
Applying auth.0005_alter_user_last_login_null... OK
Applying auth.0006_require_contenttypes_0002... OK
Applying auth.0007_alter_validators_add_error_messages... OK
Applying auth.0008_alter_user_username_max_length... OK
Applying auth.0009_alter_user_last_name_max_length... OK
Applying auth.0010_alter_group_name_max_length... OK
Applying auth.0011_update_proxy_permissions... OK
Applying auth.0012_alter_user_first_name_max_length... OK
Applying sessions.0001_initial... OK
(ll_env) maxwellpan@192 learning_log % ls
db.sqlite3 ll_env ll_project manage.py
(ll_env) maxwellpan@192 learning_log %
7).Viewing the Project
bash
(ll_env) maxwellpan@192 learning_log % python3 manage.py runserver
Watching for file changes with StatReloader
Performing system checks...
System check identified no issues (0 silenced).
April 17, 2024 - 11:03:28
Django version 5.0.4, using settings 'll_project.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.
Note:
If you receive the error message "That port is already in use," tell Django to use a dif-
ferent port by entering python manage.py runserver 8001 and then cycling through
higher numbers until you find an open port.
3.Starting an App
A Django project is organized as a group of individual apps that work together to make the project work as a whole. For now, we'll create one app to do most of our project's work. We'll add another app in Chapter 19 to manage user accounts. You should leave the development server running in the terminal window you opened earlier. Open a new terminal window (or tab) and navigate to the directory that contains manage.py. Activate the virtual environment,and then run the startapp command:
bash
(ll_env) maxwellpan@192 learning_log % ls -ltr
total 264
drwxr-xr-x 6 maxwellpan staff 192 Apr 17 18:50 ll_env
-rwxr-xr-x 1 maxwellpan staff 666 Apr 17 19:00 manage.py
drwxr-xr-x 8 maxwellpan staff 256 Apr 17 19:01 ll_project
-rw-r--r-- 1 maxwellpan staff 131072 Apr 17 19:01 db.sqlite3
(ll_env) maxwellpan@192 learning_log % source ll_env/bin/activate
(ll_env) maxwellpan@192 learning_log % python3 manage.py startapp learning_logs
(ll_env) maxwellpan@192 learning_log % ls
db.sqlite3 learning_logs ll_env ll_project manage.py
(ll_env) maxwellpan@192 learning_log % ls learning_logs
__init__.py admin.py apps.py migrations models.py tests.py views.py
(ll_env) maxwellpan@192 learning_log %
The command startapp appname tells Django to create the infrastructure needed to build an app. When you look in the project directory now, you'll see a new folder called learning_logs . Use the ls command to see what Django has created . The most important files are models.py, admin.py, and views.py. We'll use models.py to define the data we want to manage in our
app. We'll look at admin.py and views.py a little later.
1) Defining Models
bash
(ll_env) maxwellpan@192 learning_log % cd learning_logs
(ll_env) maxwellpan@192 learning_logs % ls -ltr
total 40
-rw-r--r-- 1 maxwellpan staff 0 Apr 17 19:15 __init__.py
-rw-r--r-- 1 maxwellpan staff 63 Apr 17 19:15 views.py
-rw-r--r-- 1 maxwellpan staff 63 Apr 17 19:15 admin.py
-rw-r--r-- 1 maxwellpan staff 157 Apr 17 19:15 apps.py
-rw-r--r-- 1 maxwellpan staff 57 Apr 17 19:15 models.py
-rw-r--r-- 1 maxwellpan staff 60 Apr 17 19:15 tests.py
drwxr-xr-x 3 maxwellpan staff 96 Apr 17 19:15 migrations
(ll_env) maxwellpan@192 learning_logs % cat models.py
from django.db import models
# Create your models here.
(ll_env) maxwellpan@192 learning_logs % pwd
/Users/maxwellpan/selflearn/py_practise_learn/python_crash_course/chapter18/learning_log/learning_logs
(ll_env) maxwellpan@192 learning_logs %
python
from django.db import models
# Create your models here.
class Topic(models.Model):
"""A topic the user is learning about."""
text = models.CharField(max_length=200)
data_added = models.DateTimeField(auto_now_add=True)
def __str__(self):
"""Return a string representation of the model."""
return self.text
2)Activating Models
python
# Application definition
INSTALLED_APPS = [
# My apps.
'learning_logs',
# Default django apps.
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
]
python
(ll_env) maxwellpan@192 learning_log % pwd
/Users/maxwellpan/selflearn/py_practise_learn/python_crash_course/chapter18/learning_log
(ll_env) maxwellpan@192 learning_log % ls -ltr
total 264
drwxr-xr-x 6 maxwellpan staff 192 Apr 17 18:50 ll_env
-rwxr-xr-x 1 maxwellpan staff 666 Apr 17 19:00 manage.py
drwxr-xr-x 8 maxwellpan staff 256 Apr 17 19:01 ll_project
-rw-r--r-- 1 maxwellpan staff 131072 Apr 17 19:01 db.sqlite3
drwxr-xr-x 9 maxwellpan staff 288 Apr 17 19:15 learning_logs
(ll_env) maxwellpan@192 learning_log % python3 manage.py makemigrations learning_logs
Migrations for 'learning_logs':
learning_logs/migrations/0001_initial.py
- Create model Topic
(ll_env) maxwellpan@192 learning_log % python3 manage.py migrate
Operations to perform:
Apply all migrations: admin, auth, contenttypes, learning_logs, sessions
Running migrations:
Applying learning_logs.0001_initial... OK
(ll_env) maxwellpan@192 learning_log %
3) The Django Admin Site
Django makes it easy to work with your models through its admin site. Django's
admin site is only meant to be used by the site's administrators; it's not meant for
regular users. In this section, we'll set up the admin site and use it to add some
topics through the Topic model.
4)Setting Up a Superuser
python
(ll_env) maxwellpan@192 learning_log % python3 manage.py createsuperuser
Username (leave blank to use 'maxwellpan'): ll_admin
Email address: psmaxwell@sina.com
Password:
Password (again):
Superuser created successfully.
(ll_env) maxwellpan@192 learning_log % pwd
/Users/maxwellpan/selflearn/py_practise_learn/python_crash_course/chapter18/learning_log
(ll_env) maxwellpan@192 learning_log %
5)Registering a Model with the Admin Site
python
from django.contrib import admin
# Register your models here.
from .models import Topic
admin.site.register(Topic)
Now use the superuser account to access the admin site. Go to http://localhost:8000/admin/ and enter the username and password for the superuser you just created.
Note:
If you see a message in your browser that the web page is not available, make sure you still have the Django server running in a terminal window. If you don't, activate a virtual environment and reissue the command python manage.py runserver. If you're having trouble viewing your project at any point in the development process, closing any open terminals and reissuing the runserver command is a good first troubleshooting step.
6) Adding Topics
7) Defining the Entry Model
python
class Entry(models.Model):
"""Something specific learned about a topic."""
topic = models.ForeignKey(Topic, on_delete=models.CASCADE)
text = models.TextField()
date_added = models.DateTimeField(auto_now_add=True)
class Meta:
verbose_name_plural = 'entries'
def __str__(self):
"""Return a simple string representing the entry."""
return f"{self.text[:50]}..."
8) Migrating the Entry Model
python
(ll_env) maxwellpan@192 learning_log % python3 manage.py makemigrations learning_logs
Migrations for 'learning_logs':
learning_logs/migrations/0002_entry.py
- Create model Entry
(ll_env) maxwellpan@192 learning_log % python3 manage.py migrate
Operations to perform:
Apply all migrations: admin, auth, contenttypes, learning_logs, sessions
Running migrations:
Applying learning_logs.0002_entry... OK
(ll_env) maxwellpan@192 learning_log %
9) Registering Entry with the Admin Site
python
from django.contrib import admin
# Register your models here.
from .models import Topic, Entry
admin.site.register(Topic)
admin.site.register(Entry)
python
(ll_env) maxwellpan@192 learning_log % python3 manage.py runserver
Watching for file changes with StatReloader
Performing system checks...
System check identified no issues (0 silenced).
April 17, 2024 - 12:40:58
Django version 5.0.4, using settings 'll_project.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.
python
http://localhost:8000/admin/
10) The Django Shell
python
(ll_env) maxwellpan@192 learning_log % ls -ltr
total 392
drwxr-xr-x 6 maxwellpan staff 192 Apr 17 18:50 ll_env
-rwxr-xr-x 1 maxwellpan staff 666 Apr 17 19:00 manage.py
drwxr-xr-x 8 maxwellpan staff 256 Apr 17 19:01 ll_project
drwxr-xr-x 10 maxwellpan staff 320 Apr 17 19:53 learning_logs
-rw-r--r-- 1 maxwellpan staff 143360 Apr 17 20:50 db.sqlite3
(ll_env) maxwellpan@192 learning_log % python3 manage.py shell
Python 3.12.2 (v3.12.2:6abddd9f6a, Feb 6 2024, 17:02:06) [Clang 13.0.0 (clang-1300.0.29.30)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> from learning_logs.models import Topic
>>> Topic.objects.all()
<QuerySet [<Topic: Chess>, <Topic: Rock Climbing>]>
>>>
>>> topics = Topic.objects.all()
>>> for topic in topics:
... print(topic.id, topic)
...
1 Chess
2 Rock Climbing
>>>
>>> t = Topic.objects.get(id=1)
>>> t.text
'Chess'
>>> t.date_added
Traceback (most recent call last):
File "<console>", line 1, in <module>
AttributeError: 'Topic' object has no attribute 'date_added'. Did you mean: 'data_added'?
>>> t.date_added
Traceback (most recent call last):
File "<console>", line 1, in <module>
AttributeError: 'Topic' object has no attribute 'date_added'. Did you mean: 'data_added'?
>>> t.entry_set.all()
<QuerySet [<Entry: The opening is the first part of the game, roughly...>]>
>>>
4. Making Pages: The Learning Log Home Page
1) Mapping a URL
Users request pages by entering URLs into a browser and clicking links, so we'll need to decide what URLs are needed. The home page URL is first: it's the base URL people use to access the project. At the moment, the base URL, http://localhost:8000/, returns the default Django site that lets us know the project was set up correctly. We'll change this by mapping the base URL to Learning Log's home page.
python
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('learning_logs.urls')),
]
python
"""Define URL patterns for learning_logs"""
from django.urls import path
from . import views
app_name = 'learning_logs'
urlpatterns = [
path('', views.index, name='index'),
]
2) Writing a View
python
from django.shortcuts import render
# Create your views here.
def index(request):
"""The home page for Learning Log."""
return render(request, 'learning_logs/index.html')
3) Writing a Template
5.Building Additional Pages
1) Template Inheritance
1.1 The Parent Template
python
<p>
<a href="{% url 'learning_logs:index' %}">Learning Log</a>>
</p>
{% block content %}{ % endblock content %}
1.2 The Child Template
python
{% extends 'learning_logs/base.html' %}
{% block content %}
<p>Learning Log helps you keep track of your learning, for any topic you're interested in.</p>
{% endblock content %}
2)The Topics Page
2.1 The Topics URL Pattern
python
"""Define URL patterns for learning_logs"""
from django.urls import path
from . import views
app_name = 'learning_logs'
urlpatterns = [
path('', views.index, name='index'),
# Page that shows all topics.
path('topics/',views.topics, name='topics')
]
2.2 The Topics View
python
from django.shortcuts import render
from .models import Topic
# Create your views here.
def index(request):
"""The home page for Learning Log."""
return render(request, 'learning_logs/index.html')
def topics(request):
"""Show all topics."""
topics = Topic.objects.order_by('date_added')
context = {'topics': topics}
return render(request, 'learning_logs/topics.html', context)
2.3 The Topics Template
topics.html
python
{% extends 'learning_logs/base.html' %}
{% block content %}
<p>Topics</p>
<ul>
{% for topic in topics %}
<li>
<a href="{% url 'learning_logs:topic' topic.id %}">
{{ topic.text }}</a>
</li>
{% empty %}
<li>No topics have been added yet.</li>
{% endfor %}
</ul>
{% endblock content %}
base.html
python
<p>
<a href="{% url 'learning_logs:index' %}">Learning Log</a> -
<a href="{% url 'learning_logs:topics' %}">Topics</a>
</p>
{% block content %}{% endblock content %}
3) Individual Topic Pages
learning_logs/urls.py
python
"""Defines URL patterns for learning_logs."""
from django.urls import path
from . import views
app_name = 'learning_logs'
urlpatterns = [
# Home page
path('', views.index, name='index'),
# Page that shows all topics.
path('topics/', views.topics, name='topics'),
# Detail page for a single topic.
path('topics/<int:topic_id>/', views.topic, name='topic'),
]
4) The Topic View
python
from django.shortcuts import render
from .models import Topic
def index(request):
"""The home page for Learning Log."""
return render(request, 'learning_logs/index.html')
def topics(request):
"""Show all topics."""
topics = Topic.objects.order_by('date_added')
context = {'topics': topics}
return render(request, 'learning_logs/topics.html', context)
def topic(request, topic_id):
"""Show a single topic and all its entries."""
topic = Topic.objects.get(id=topic_id)
entries = topic.entry_set.order_by('-date_added')
context = {'topic': topic, 'entries': entries}
return render(request, 'learning_logs/topic.html', context)
- The Topic Template
topic.html
python
{% extends 'learning_logs/base.html' %}
{% block content %}
<p>Topic: {{ topic.text }}</p>
<p>Entries:</p>
<ul>
{% for entry in entries %}
<li>
<p>{{ entry.date_added|date:'M d, Y H:i' }}</p>
<p>{{ entry.text|linebreaks }}</p>
</li>
{% empty %}
<li>There are no entries for this topic yet.</li>
{% endfor %}
</ul>
{% endblock content %}
topics.html
python
{% extends 'learning_logs/base.html' %}
{% block content %}
<p>Topics</p>
<ul>
{% for topic in topics %}
<li>
<a href="{% url 'learning_logs:topic' topic.id %}">
{{ topic.text }}</a>
</li>
{% empty %}
<li>No topics have been added yet.</li>
{% endfor %}
</ul>
{% endblock content %}
NOTE: There's a subtle but important difference between topic.id and topic_id. The expres-
sion topic.id examines a topic and retrieves the value of the corresponding ID. The variable topic_id is a reference to that ID in the code. If you run into errors when working with IDs, make sure you're using these expressions in the appropriate ways.
6.Summary
We learned how to starting building a simple web app using the Django framework. We saw a brief project specification, installed Django to a virtual environment, set up a project, and checked that the project was set up correctly. We set up an app and defined models to rep-
resent the data for your app. We learned about databases and how Django helps us migrate our database after we make a change to our models. We created a superuser for the admin site, and we used the admin site to enter some initial data.