Skip to main content
Django Basics Tutorial
CHAPTER 10 Beginner

Building CRUD Applications in Django

Updated: May 14, 2026
40 min read

# CHAPTER 10

Building CRUD Applications in Django

1. Introduction

In the previous chapter, we successfully manipulated the database using the Django terminal shell. However, real users interact with websites through web browsers, not terminal windows. In this chapter, we will synthesize everything we've learned so far. We will connect our ORM queries directly into our views.py functions, and inject that live database information into our HTML templates to create a fully functional, dynamic web application.

2. Learning Objectives

By the end of this chapter, you will be able to:
  • Query the database inside a View function.
  • Pass a QuerySet to a template via the Context dictionary.
  • Render dynamic database content using template loops.
  • Handle DoesNotExist errors gracefully using get_object_or_404.

3. Beginner-Friendly Explanation

Think of your View function as a News Reporter.
  1. 1. The Reporter gets an assignment from the Editor (The URL dispatcher says "Go cover the homepage!").
  1. 2. The Reporter runs to the library (The Database) and asks the Librarian (The ORM) for all the latest articles: Post.objects.all().
  1. 3. The Librarian hands the Reporter a stack of files (A QuerySet).
  1. 4. The Reporter packages the files into a briefcase (The Context dictionary).
  1. 5. The Reporter runs to the Printing Press (The Template) and hands them the briefcase. The Printing Press prints the files onto the front page of the newspaper (The HTML Response).

4. Step 1: The "Read All" View (Homepage)

We want our homepage to display every blog post in the database.

Update blog/views.py:

python
1234567891011121314
from django.shortcuts import render
from .models import Post # Import the database Model!

def home(request):
    # 1. Ask the ORM for all posts
    all_posts = Post.objects.all()
    
    # 2. Package the QuerySet into the context dictionary
    context = {
        'posts': all_posts
    }
    
    # 3. Send it to the template
    return render(request, 'blog/home.html', context)

5. Step 2: The "Read All" Template

Now we update the HTML file to loop over the actual database objects.

Update blog/templates/blog/home.html:

html
1234567891011121314151617
{% extends "blog/base.html" %}

{% block content %}
    <h1>Latest Blog Posts</h1>
    
    {% for post in posts %}
        <article style="border: 1px solid #ccc; padding: 10px; margin: 10px 0;">
            <!-- Dynamically inject the database columns! -->
            <h2>{{ post.title }}</h2>
            <p style="color: gray;">Posted on: {{ post.date_posted }}</p>
            <p>{{ post.content }}</p>
            
            <!-- Link to the detail page (We will build this next) -->
            <a href="{% url &#039;post-detail' post.id %}">Read More</a>
        </article>
    {% endfor %}
{% endblock %}

*If you refresh your browser, you will see the actual posts you created in the terminal from Chapter 9!*

6. Step 3: The "Read One" View (Detail Page)

Users need to click "Read More" to view a single post.

1. Create the URL (blog/urls.py):

python
1
path(&#039;post/<int:pk>/', views.post_detail, name='post-detail'),

*(Note: pk stands for Primary Key, which is the exact same thing as id).*

2. Create the View (blog/views.py):

python
123456789101112
from django.shortcuts import render, get_object_or_404
from .models import Post

def post_detail(request, pk):
    # Fetch exactly ONE post based on the ID in the URL.
    # If a user types /post/999/ and it doesn't exist, this securely returns a 404 page!
    single_post = get_object_or_404(Post, pk=pk)
    
    context = {
        &#039;post': single_post
    }
    return render(request, &#039;blog/post_detail.html', context)

3. Create the Template (blog/templates/blog/post_detail.html):

html
123456789
{% extends "blog/base.html" %}

{% block content %}
    <h1>{{ post.title }}</h1>
    <p style="color: gray;">Posted on: {{ post.date_posted }}</p>
    <hr>
    <p>{{ post.content }}</p>
    <a href="{% url &#039;blog-home' %}">Back to Home</a>
{% endblock %}

7. Backend Workflow: The get_object_or_404 Shortcut

Why did we use get_object_or_404(Post, pk=pk) instead of the standard Post.objects.get(pk=pk)? If you use .get(), and a malicious user types http://mysite.com/post/9999/ into their browser, the database fails to find it. Python panics, throws a DoesNotExist exception, and crashes the server with a massive yellow error screen. By using get_object_or_404, Django gracefully catches the error and displays a standard "404 Page Not Found" screen to the user, keeping your server completely secure and online.

8. Best Practices

  • Sorting Data: Usually, you want the newest posts at the top of the page. You can chain ORM methods in your view: Post.objects.all().order_by('-date_posted'). The minus sign - means descending order (newest first).

9. Common Mistakes

  • Url Name Mismatch: In the HTML tag {% url 'post-detail' post.id %}, the string 'post-detail' MUST exactly match the name='post-detail' argument defined in your urls.py file. If there is a typo, Django will throw a massive NoReverseMatch error.

10. Exercises

  1. 1. Explain the chronological flow of data starting from the moment a user clicks a "Read More" link on the homepage, to the moment they see the post_detail.html page.

11. Coding Challenges

  • Challenge: Update the home View function. Instead of fetching ALL posts, use the ORM to .order_by('-date_posted') and then use Python slicing [:5] to only return the 5 most recent posts to the template.

12. MCQs with Answers

Question 1

What is the primary purpose of the get_object_or_404() shortcut function in a Django View?

Question 2

When linking to dynamic detail pages in a Django template, what is the correct syntax to pass the post's ID into the URL tag?

13. Interview Questions

  • Q: Explain why it is considered a severe anti-pattern to use Model.objects.get() inside a View without wrapping it in a try/except block. What built-in Django shortcut solves this issue?
  • Q: Describe the architectural relationship between a View and a Context Dictionary. How does data cross the boundary from Python code into a rendered HTML string?

14. FAQs

Q: We covered Read, but what about Create, Update, and Delete? A: To Create and Update data via the web browser, users must submit HTML Forms. Django has an incredibly powerful Form processing engine, which requires an entire chapter of its own. We will cover this in the very next chapter!

15. Summary

In Chapter 10, we successfully wired the MVT architecture together. We executed live ORM database queries directly inside our views.py controllers. By packaging the resulting QuerySets into Context dictionaries, we successfully transmitted database records to the frontend. Finally, we utilized Template tags and loops to dynamically generate HTML, bringing our application to life with real, permanent data.

16. Next Chapter Recommendation

Users can now read data, but they cannot create data without using the terminal. We need to build web forms. Proceed to Chapter 11: Django Forms and Validation.

Finish this Chapter

Save your progress on your learning path and prepare for coding interview challenges.

Discussion

Join the discussion

Log in or create a free account to participate.

Sort: ·