Skip to main content

Welcome to a Grasp lesson

Create a free account to keep studying and create your own study path.

Lesson 22

Django ORM: CRUD Operations

Recommended resources

Hello! Welcome back to your Django journey.

In our last lesson, you did some excellent work structuring the heart of our application by defining models and establishing relationships between them using ForeignKey and ManyToManyField. We now have the blueprints for our Task, Tag, and User data, and the database schema is ready.

However, a database schema is just an empty container. To bring our task manager to life, we need to interact with it: add tasks, find them, mark them as complete, and clear them out.

This lesson focuses on exactly that. You will learn how to perform Create, Retrieve, Update, and Delete (CRUD) operations using the Django Object-Relational Mapper (ORM). This is where you'll stop just defining data and start manipulating it, all using Python code.

The Django ORM: Your Python-to-SQL Translator

Before we dive in, let's clarify what the ORM is. The Object-Relational Mapper is a powerful feature in Django that acts as a bridge between your Python objects (your models) and the relational database tables. Instead of writing raw SQL queries, you use Python methods, and Django translates them into SQL for you.

This concept is visualized well in the following diagram:

Django ORM Tutorial: Python Objects to Database Table Mapping
This diagram shows how a Django model, which is a Python class, maps directly to a database table. The ORM allows you to work with Python objects, and it handles the database communication behind the scenes.

For this lesson, we'll perform all our operations in the Django shell, an interactive console that gives us direct access to our project's components. To open it, navigate to your project's root directory (where manage.py is) in your terminal and run:

python manage.py shell

Once inside the shell, you can import your models to start working with them:

# In the Django shell
>>> from tasks.models import Task, Tag
>>> from django.contrib.auth.models import User

Now, let's get to the CRUD operations.


1. CREATE: Adding New Data to the Database

There are two primary ways to create a new object and save it to the database.

Method 1: Instantiate the model and call .save()
This is a two-step process: you create a Python object first, then explicitly save it.

# In the Django shell
>>> # First, let's create a user to own the task
>>> user1 = User.objects.create(username='alice')

>>> # Now, create a task instance
>>> task1 = Task(title='Learn Django ORM', description='Complete the CRUD lesson.', user=user1)

>>> # The object exists in Python, but not in the database yet.
>>> # Now, save it.
>>> task1.save()

Method 2: Use the .create() manager method
This is a more concise, one-step method that creates and saves the object in a single command. The objects attribute on a model is called a "manager," and it's your main entry point for database queries.

# In the Django shell
>>> # This one line does the same as the four lines above
>>> task2 = Task.objects.create(title='Build a portfolio project', user=user1)

The following video provides a clear demonstration of both of these creation methods.

Django ORM - Querying and Creating Records / Working with Foreign Keys

The video "Django ORM - Querying and Creating Records" by BugBytes shows how to create new records in the database. It covers both the .save() and .create() methods.

Watch the following segments: Creating with .save() (03:00 - 06:49): Pay attention to how a model instance is created and then saved. Creating with .create() (11:54 - 13:18): Notice how this is a more direct way to achieve the same result. Creating with Foreign Keys (16:20 - 18:29): This is a crucial section that shows how to pass a full object (like our user1 object) to create a relationship, just as we did in the code example.


2. RETRIEVE: Querying for Data

Once you have data, you need ways to retrieve it. Django's ORM offers a rich API for this. The results of most queries come back as a QuerySet, which you can think of as a "smart" list of objects.

Here are the most common methods for retrieval:

  • all(): Retrieves all objects of a model.
    >>> all_tasks = Task.objects.all()
    >>> print(all_tasks)
    <QuerySet [<Task: Learn Django ORM>, <Task: Build a portfolio project>]>
    
  • filter(**kwargs**): Retrieves a subset of objects that match your criteria.
    >>> # Get all incomplete tasks
    >>> incomplete_tasks = Task.objects.filter(completed=False)
    >>> print(incomplete_tasks)
    <QuerySet [<Task: Learn Django ORM>, <Task: Build a portfolio project>]>
    
  • exclude(**kwargs**): The opposite of filter(); retrieves all objects except those that match your criteria.
  • get(**kwargs**): Retrieves a single, unique object. This is strict: if it finds zero objects or more than one, it will raise an error. It's best used with unique identifiers like the primary key (id).
    >>> first_task = Task.objects.get(id=1)
    >>> print(first_task.title)
    'Learn Django ORM'
    

For a comprehensive walkthrough of these methods, this article provides clear, text-based examples.

How to Perform CRUD Operations with Django Models

The article "How to Perform CRUD Operations with Django Models" on freeCodeCamp offers a concise guide. Let's focus on the 'READ' section.

Read the section titled "READ". It clearly explains the difference between get() for single objects and filter() for multiple objects, using a Task model that's very similar to ours.

Test your understanding!

Using the Django shell and the Task objects you created earlier, write a single query to find all tasks that are assigned to the user alice AND are not yet completed.

Show answer

You can chain multiple conditions in a single filter() call.

# In the Django shell
>>> # First, make sure you have the user object
>>> alice = User.objects.get(username='alice')

>>> # Now, filter for tasks belonging to alice that are incomplete
>>> Task.objects.filter(user=alice, completed=False)

3. UPDATE: Modifying Existing Data

Updating records follows a similar pattern to creating them.

Method 1: Retrieve, modify, and .save()
This is ideal for updating a single object. You fetch it, change its attributes in Python, and then call .save() to write the changes back to the database.

# In the Django shell
>>> task_to_complete = Task.objects.get(id=1)
>>> task_to_complete.completed = True
>>> task_to_complete.save()

>>> # Verify the change
>>> print(Task.objects.get(id=1).completed)
True

Method 2: Use .update() on a QuerySet
This is for bulk updates. It's much more efficient than looping through objects and saving them one by one, as it translates to a single SQL UPDATE statement.

# In the Django shell
>>> # Let's create another incomplete task for 'alice'
>>> Task.objects.create(title='Drink water', user=User.objects.get(username='alice'))

>>> # Now, mark all of alice's tasks as complete in one go
>>> tasks_for_alice = Task.objects.filter(user__username='alice')
>>> tasks_for_alice.update(completed=True)
2 
# The method returns the number of rows updated.

Note the user__username syntax. The double underscore (__) lets you query across relationships.

The following video explains both single and bulk updates very clearly.

Django ORM - Updating and Deleting QuerySets / ForeignKey on_delete behaviour

Let's watch "Django ORM - Updating and Deleting QuerySets" by BugBytes to see updating in action.

Watch these two parts: Updating a Single Instance (00:49 - 03:54): See how to fetch an object, change it, and save it. Updating a QuerySet (06:54 - 15:21): This is a deep dive into the powerful .update() method for bulk changes. Pay close attention to how much more efficient this is.


4. DELETE: Removing Data

Finally, to remove records from your database, you can use the .delete() method.

  • On a single object instance:
    >>> task_to_delete = Task.objects.get(id=2)
    >>> task_to_delete.delete()
    (1, {'tasks.Task': 1}) 
    # The output shows 1 total object was deleted, specifically 1 from the 'tasks.Task' model.
    
  • On a QuerySet (for bulk deletion):
    >>> # Delete all completed tasks
    >>> Task.objects.filter(completed=True).delete()
    

Remember the on_delete=models.CASCADE we set on our Task's user field? If you were to delete a user, this setting would automatically delete all tasks associated with that user. The .delete() method intelligently handles these relationship rules.

This final video clip demonstrates the delete operation and revisits the important on_delete behaviors.

Django ORM - Updating and Deleting QuerySets / ForeignKey on_delete behaviour

The same BugBytes video also has an excellent section on deletion.

Watch these segments: Deleting a Single Instance & on_delete (15:21 - 21:33): This is a fantastic explanation of how on_delete=models.CASCADE works in practice when you delete a parent object. Deleting a QuerySet (21:33 - 23:29): See how to efficiently delete multiple records at once.


Conclusion

You now have the fundamental skills to manipulate data in your Django application. Using just a handful of Python methods, you can perform all four essential CRUD operations. This knowledge is the bedrock of building any dynamic web application where data is constantly being created, read, updated, and deleted.

Key Takeaways:

  • CRUD stands for Create, Retrieve, Update, and Delete.
  • You use the Django ORM and the model manager (.objects) to interact with the database.
  • Create: MyModel.objects.create(...) or create an instance and call .save().
  • Retrieve: .all(), .filter(...), .exclude(...), and .get(...). Most return a QuerySet.
  • Update: For single objects, retrieve, modify, and call .save(). For bulk updates, use .update() on a QuerySet.
  • Delete: Call .delete() on a single object instance or on a QuerySet.

Preview of the Next Lesson:
Working in the shell is great for learning and testing, but our users won't see that. In the next lesson, "Query the database using methods like filter(), get(), exclude(), and order_by()," we will take these powerful querying skills and apply them inside our Django views. You'll learn how to fetch data from the database and pass it to a template, making the first step toward displaying your application's data on a real web page.