Skip to main content
Django Basics Tutorial
CHAPTER 09 Beginner

Django ORM Basics

Updated: May 14, 2026
35 min read

# CHAPTER 9

Django ORM Basics

1. Introduction

In the previous chapter, we used the Django ORM to *create* the database tables. In this chapter, we will use the ORM to *interact* with those tables. This means performing CRUD operations: Create, Read, Update, and Delete. Instead of writing raw SQL commands (SELECT * FROM blog_post WHERE id=1), we will use elegant, native Python methods. Mastering the ORM is arguably the most important skill for a Django backend developer.

2. Learning Objectives

By the end of this chapter, you will be able to:
  • Use the Django Interactive Shell to test database queries.
  • Create new records using .save() and .create().
  • Retrieve all records using .objects.all().
  • Filter and retrieve specific records using .objects.filter() and .objects.get().
  • Update and Delete existing records.

3. Beginner-Friendly Explanation

Imagine interacting with a massive corporate filing cabinet. If you use raw SQL, you have to speak a specialized, complex language to the filing clerk: "SELECT document FROM cabinet WHERE color='blue' AND year=2026." If you use the Django ORM, you simply speak plain English (Python) to an assistant: "Assistant, Post.objects.filter(color='blue')." The assistant translates it to SQL, runs to the cabinet, and brings back a neat Python list of files. You never need to learn the complex SQL language.

4. Step 1: The Django Interactive Shell

To learn the ORM, we won't write code in views.py just yet. We will use the Django Shell, a powerful terminal tool that lets us execute Python code live against our database.

Open your terminal and run:

bash
1
python manage.py shell

*Your terminal prompt will change to >>>. You are now writing live Python code!*

5. Step 2: CREATE Operations

Let's add some blog posts to the database. First, import the Model.
python
12345678
>>> from blog.models import Post

# Method 1: Instantiate and Save
>>> post1 = Post(title='My First Post', content='Hello World!')
>>> post1.save() # This commits the insert to the database!

# Method 2: The Create shortcut (Saves automatically)
>>> Post.objects.create(title='Second Post', content='Django is awesome.')

6. Step 3: READ Operations

Let's query the database to fetch our data.

Get all records:

python
123
>>> all_posts = Post.objects.all()
>>> print(all_posts)
<QuerySet [<Post: My First Post>, <Post: Second Post>]>

*(A QuerySet is simply a Python list of objects).*

Get a single specific record (by ID):

python
12345
# The .get() method returns exactly ONE object. 
# If it finds zero, or more than one, it crashes!
>>> single_post = Post.objects.get(id=1)
>>> print(single_post.title)
&#039;My First Post'

Filter records based on a condition:

python
1234
# The .filter() method returns a QuerySet (a list) of matching objects.
>>> results = Post.objects.filter(title=&#039;Second Post')
>>> print(results)
<QuerySet [<Post: Second Post>]>

7. Step 4: UPDATE Operations

To update a record, you fetch it, change the Python attribute, and call .save().
python
123
>>> post = Post.objects.get(id=1)
>>> post.title = &#039;Updated First Post Title'
>>> post.save() # Executes an SQL UPDATE statement

8. Step 5: DELETE Operations

To delete a record, you fetch it and call .delete().
python
12
>>> post_to_delete = Post.objects.get(id=2)
>>> post_to_delete.delete()

*(To exit the interactive shell, type exit() and press Enter).*

9. Backend Workflow: Field Lookups (Advanced Filtering)

The true power of the ORM is "Field Lookups". By adding double underscores __ to a field name, you can execute complex SQL WHERE clauses.
  • Post.objects.filter(title__contains='Django') (SQL: LIKE '%Django%')
  • Post.objects.filter(title__startswith='Updated')
  • Post.objects.filter(id__gt=5) (id Greater Than 5)
This allows you to build powerful search bars and filters without writing a single line of SQL.

10. Best Practices

  • .get() vs .filter(): This is a crucial distinction.
  • Use .get() when you expect EXACTLY ONE unique record (like an ID or an Email). If it fails to find the record, it throws a DoesNotExist error.
  • Use .filter() when you are searching for a category (like finding all posts written in 2026). If it finds nothing, it safely returns an empty list [].

11. Common Mistakes

  • Forgetting .save(): A classic beginner mistake is typing post = Post(title="New") and wondering why the data isn't in the database. The Post() command only creates the object in your laptop's temporary RAM. It is not permanent until you execute post.save().

12. Exercises

  1. 1. What is the fundamental difference between the output of Post.objects.all() and Post.objects.get(id=1)?

13. Coding Challenges

  • Challenge: Open the python manage.py shell. Import your Post model. Write a query using .filter() and a Field Lookup (__icontains) to find all posts where the content contains the word "awesome".

14. MCQs with Answers

Question 1

Which ORM method is used to retrieve a single, unique record from the database, and will throw an error if multiple records are found?

Question 2

What is the correct syntax for an ORM Field Lookup to find all posts where the title contains the word "Django"?

15. Interview Questions

  • Q: Compare and contrast the .get() and .filter() methods in the Django ORM. Provide a real-world scenario detailing when to use each.
  • Q: Explain the concept of "QuerySet Evaluation" (Lazy Loading). If you write q = Post.objects.all(), does Django immediately execute the SQL query to the database?

16. FAQs

Q: Can I write raw SQL in Django if the ORM is too slow? A: Yes. For incredibly complex reporting queries, you can use Post.objects.raw('SELECT * FROM blog_post'). However, for 99% of web development, the ORM is heavily optimized and highly secure against SQL injection. Avoid raw SQL unless absolutely necessary.

17. Summary

In Chapter 9, we unlocked the ability to manipulate data. Utilizing the Django Interactive Shell, we executed Create, Read, Update, and Delete (CRUD) operations using native Python syntax. We differentiated between extracting single objects with .get() and extracting lists of objects using .filter(). Finally, we explored the powerful double-underscore __ syntax for advanced database lookups.

18. Next Chapter Recommendation

Practicing in the terminal is great, but users can't access your terminal! We must wire these ORM queries to our Views and HTML. Proceed to Chapter 10: Building CRUD Applications in Django.

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: ·