Introduction to Backend Development With Django
Welcome to the (Django)l !: Introduction to Backend Programming with Django
Prerequisites to this Workshop¶
You need the following installed:
- Browser
- VsCode
- Docker
The starting repo for this workshop: https://github.com/CodersforLearning/django-workshop-winter-2024
What you will be building as part of this workshop?¶
You're launching a new startup called "Snapstagram" - a social media app that showcases images through posts.
Below is the schematics
erDiagram
user {
uuid user_id PK
string username
string email
string password
datetime created_at
}
post {
uuid post_id PK
uuid user_id FK
string content
boolean is_draft
datetime created_at
datetime updated_at
}
comment {
uuid comment_id PK
uuid post_id FK
uuid user_id FK
string content
uuid parent_comment_id
datetime created_at
datetime updated_at
}
image {
uuid image_id PK
uuid post_id FK
string url
}
user ||--o{ post : creates
user ||--o{ comment : creates
post ||--o{ image : contains
post ||--o{ comment : has
comment ||--o{ comment : "replies to"
¶
erDiagram
user {
uuid user_id PK
string username
string email
string password
datetime created_at
}
post {
uuid post_id PK
uuid user_id FK
string content
boolean is_draft
datetime created_at
datetime updated_at
}
comment {
uuid comment_id PK
uuid post_id FK
uuid user_id FK
string content
uuid parent_comment_id
datetime created_at
datetime updated_at
}
image {
uuid image_id PK
uuid post_id FK
string url
}
user ||--o{ post : creates
user ||--o{ comment : creates
post ||--o{ image : contains
post ||--o{ comment : has
comment ||--o{ comment : "replies to"What are APIs and REST-APIs?¶
Application Programming Interface
Analogy
Who interacts with the user interface? - the user Who interacts with the application programming interface ? - the application program (eg. the browser)
Representational State Transfer Application Programming Interface
-
backend architectural pattern that follows the GET/POST/PUT/PATCH/DELETE
-
Can be represented in Swagger/ Open API specification
Swagger/ Open API specification

In terms of using RESTful APIs, there are some naming and implementation conventions used to accurately label the endpoint with what it does.
CRUD to HTTP Verb Matching for JSON standard communications with REST-APIs
CRUD stands for Create, Read, Update, and Delete. RESTful APIs use HTTP verbs to specify the CRUD operation an endpoint is performing.
| HTTP Verb | CRUD Operation |
|---|---|
| POST | Create/Update |
| GET | Read |
| PUT | Update/Replace |
| PATCH | Update/Modify |
| DELETE | Delete |

What is Django?¶

Django
- Python web framework for creating server-side application
Follows MVC:
- Model - database
- View – Interface (API or User Interface)
- Controller – URLs + routes
See Documentation
What is Django REST Framework (DRF)?¶
- library for creating REST-API
- just makes it easier develop REST-API
In:
- Authentication + Permission
- Generic API Views
- Serialisers (payload validation and format)
See Documentation
Interactive Workshop Time!!!¶
Firstly, open your IDE (VSCode) and open the terminal.
- Clone the repo:
git clone https://github.com/CodersforLearning/django-workshop-winter-2024.git - Go to the directory:
cd django-workshop-winter-2024 - Open in dev container
What does the setup script do?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | |
Initial files¶
manage.py- the entrypoint of the Django applicationsnapstagram- the main Django applicationsnapstagram/settings.py- the configuration file of the Django applicationsnapstagram/urls.py- the URL routes of the Django applicationsnapstagram/wsgi.pyandsnapstagram/asgi.py- used as the script to run production django application
Let's start the Django application¶
- Run
cd snapstagram - Perform the initial migration:
python manage.py migrate. Notice that when you run this command, it will create adb.sqlite3file. - Let's take a look at this file - just click it in the file explorer.
- Run the Django application:
python manage.py runserver - Check out the Django application: http://localhost:8000
- Check out the Django admin: http://localhost:8000/admin
- Create a superuser:
python manage.py createsuperuser. Login and look around the Django admin.
Additional info
Django ships default "django apps" defined settings.py file. You can see the list of apps in the INSTALLED_APPS variable.
Django apps are plugins that can be used to extend the functionality of the Django application. It's the core method of developing with this backend framework.
For the db.sqlite3 file, it's the default database that Django uses. You can change this to other databases like MySQL, PostgreSQL, etc.
Let's create our first Django app: post¶
Run this command python manage.py startapp post.
What did this command do?¶
Initial files that it created:
post/admin.py- the admin interfacepost/apps.py- the configurationpost/models.py- the database schemapost/tests.py- the test casespost/views.py- the views
Some files you want to create later are:
post/serializers.py- the serializerspost/urls.py- the URL routespost/permissions.py- the permissions
Creation of the database schema¶
Quick Reference: ERD
erDiagram
user {
uuid user_id PK
string username
string email
string password
datetime created_at
}
post {
uuid post_id PK
uuid user_id FK
string content
boolean is_draft
datetime created_at
datetime updated_at
}
comment {
uuid comment_id PK
uuid post_id FK
uuid user_id FK
string content
uuid parent_comment_id
datetime created_at
datetime updated_at
}
image {
uuid image_id PK
uuid post_id FK
string url
}
user ||--o{ post : creates
user ||--o{ comment : creates
post ||--o{ image : contains
post ||--o{ comment : has
comment ||--o{ comment : "replies to"
WIP Answer
1 2 3 4 5 6 7 8 9 10 11 12 13 | |
After creating this, you'll need to add it in the INSTALLED_APPS variable in the settings.py file.
Then run python manage.py makemigrations and python manage.py migrate.
What did these commands do?
makemigrations - creates the migration files
These are scripts that defines how the database schema changed.
migrate - applies the migration files to the database
Similar to how we performed our initial migration
When you have created that, check out db.sqlite3 and you'll see that there's a new table called post_post.
Creation of the admin interface¶
WIP Answer
1 2 3 4 | |
Now visit the admin interface and you'll see the Post model there.
A cooler example
1 2 3 4 5 6 7 | |
Do the same thing again for Image and Comment¶
Quick Reference: ERD
erDiagram
user {
uuid user_id PK
string username
string email
string password
datetime created_at
}
post {
uuid post_id PK
uuid user_id FK
string content
boolean is_draft
datetime created_at
datetime updated_at
}
comment {
uuid comment_id PK
uuid post_id FK
uuid user_id FK
string content
uuid parent_comment_id
datetime created_at
datetime updated_at
}
image {
uuid image_id PK
uuid post_id FK
string url
}
user ||--o{ post : creates
user ||--o{ comment : creates
post ||--o{ image : contains
post ||--o{ comment : has
comment ||--o{ comment : "replies to"
WIP Answer
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 | |
Creation Views: the Interface in API¶
Installed Apps Rest Framework
Before going further, add rest_framework in INSTALLED_APPS in settings.py.
serializers.py¶
Serialisers are a way to convert Python models to JSON, XML or any other format you wish.
WIP Answer
1 2 3 4 5 6 | |
views.py and urls.py¶

Here's our goal:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | |
Firstly, we need to set the urls.py to import the views from post/views.py.
1 2 3 4 5 6 7 8 | |
There's 2 main ways to create views
- Functional
- Class-based
Class-based Views
This is the easiest way - full of magic. If you need flexibility, you can override the methods.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 | |
Important to note that the basename is used to generate the URL name. In this case, it will be posts-list and posts-detail. (you will need this automated testing)
Functional Views
This is the more flexible way, but it does mean there's quite of bit of boilerplate code.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | |
Reuse Same Path with different HTTP Methods
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | |
Another Example Class-based Views
This is the easiest way - full of magic. If you need flexibility, you can override the methods.
1 2 3 4 5 6 7 8 9 | |
After that long discussion, let's create the views and play around.
Going back to Serializer for Organizer¶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | |
What about if we also want to include comments?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | |
Now what if you want to nest all the replies as well?
1 2 3 4 5 6 7 8 9 10 11 | |
Now let's do comment endpoints¶
1 2 3 4 5 6 7 8 9 10 11 | |
1 2 3 4 5 6 | |
Image API endpoints are left as an exercise to the reader
- Create a new serializer for images
- Create a new viewset for images
- Add the viewset to the router
- Add the router to the urls
Bonus: Filters and Search¶
https://www.django-rest-framework.org/api-guide/filtering/
What if I want to only see posts that are not yet published?
Need to add this in settings.py
1 2 3 | |
then in views.py
1 2 3 4 5 6 7 8 9 10 11 12 13 | |
Bonus: Viewset Actions (Will not be covered in this workshop)¶
If you want to create a special endpoint that doesn't follow the traditional CRUD operations, you can use viewset actions. Docs: Viewset Actions
For example, what if you want a new endpoint called POST /api/posts/<id>/schedule-publish, which adds to another database, and there's some task that will change the is_draft to False after a certain time.
Automated Testing¶
https://www.django-rest-framework.org/api-guide/testing/
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | |
To run this test, run python manage.py test
Authentication and Authorization¶
What if we only want the PUT/PATCH/DELETE methods to be accessible by the organizer?
Docs: - Authentication https://www.django-rest-framework.org/api-guide/authentication/ - Authorization/Permissions https://www.django-rest-framework.org/api-guide/permissions/
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | |
TDD: Test-Driven Development With Multiple Accounts¶
This is a perfect showcase how it's very difficult to manage manually testing with multiple accounts.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | |
How to manually test this?
- Login as the organiser
- Create an Post
- Create a new user
- Login as the new user
- Try to modify the Post
- Check that it returns a 403 Forbidden
Exercise for the reader: Fix broken test
The example code given is broken after the authentication enforcement. Can you fix the issue?
Extra-Reading - JWT Authentication¶
The only key bit to know is
install pip install djangorestframework-simplejwt
in settings.py
1 2 3 4 5 6 7 8 | |
in urls.py
1 2 3 4 5 6 7 8 9 10 11 | |
If you login via /api/token , you will send something like this
1 2 3 4 | |
you're going to get something like this back
1 2 3 4 | |
And if you are using an authenticated endpoint, you need to add the Authorization header with the token.
"Authorization: Bearer eyJhbEXAMPLEOFJWTOKEN..."
Bonus" Sending Emails¶
Run docker-compose up to start the mail server.
Your mail server will have this UI at http://localhost:8025/. It communicates via SMTP on port 1025.
Add these in your settings.py
1 2 3 4 5 6 7 8 | |
Where are these values coming from?
If you check the docker-compose.yml, you will understand that we are just configuring the values to send via SMTP to the mail server.
In actual production services, you will change this to your actual SMTP server like AWS SES, Sendgrid, Mailgun, etc.
New file called utils.py
1 2 3 4 5 6 7 8 9 10 11 | |
Now let's change it so that when a new post is created, an email is sent to the organizer.
1 2 3 4 5 6 7 8 9 | |
Now when you create a new post, you should see an email in the mail server at http://localhost:8025/.