How to Get Multiple Objects in Django (Function-based & Class-based Views)

Share your love

Django provides powerful functionalities out of the box, saving development time. One of the functionalities that Django provides is the ability to get multiple objects stored in the database, manipulate them, and display them to the users on a web page or an API.

When retrieving multiple items from the database, Django provides a bunch of model manager functions to help you do that easily.

Here are the most commonly used functions for retrieving multiple objects from the database in Django.

Functions used to get multiple objects in Django are:

  1. Using the all() method: Model.objects.all()
  2. Using the filter() method: Model.objects.filter()

The Model.objects.all() and .filter() methods return a queryset that gets evaluated when you loop through the queryset or convert the queryset into a list.

Let’s see how you can get multiple objects from the database for each approach provided by Django.

Here is the model that I will be using to illustrate how to retrieve multiple blog posts objects from the database:

from django.db import models
from django.utils import timezone
from django.urls import reverse


# a category can be in multiple blogposts
class Category(models.Model):
   category_title = models.CharField(max_length=100)


# a blog post can only be in one category
class BlogPost(models.Model):
   POST_STATUS = (
       ('published', 'Published'),
       ('draft', 'Draft'),
   )
   title = models.CharField(max_length=100)
   slug = models.SlugField(max_length=100)
   body = models.TextField()
   publish_date = models.DateTimeField(default=timezone.now)
   created_on = models.DateTimeField(auto_now_add=True)
   updated_on = models.DateTimeField(auto_now=True)
   status = models.CharField(max_length=10, choices=POST_STATUS, default='draft')
   category = models.ForeignKey(Category, on_delete=models.CASCADE)

#     new instance method code
   def get_absolute_url(self):
       return reverse('post-detail', args=[str(self.slug)])

   def __str__(self):
       return self.title

You can define the model code in any of your app models.py files. Besides, tweak it according to the requirements of your application.

When retrieving objects from the database, you should write the functionality inside the views of your app, app/views.py file.

How to get multiple objects in Django using the .all() method

The most commonly used function for retrieving multiple objects in Django is the Models.objects.all() method.

When you want to display all the items stored in your database, the .all() method is the go-to method. 

What does .all do in Django? The function all() in Django is used to retrieve all the objects stored in the database in a function-based view.

So, if you wish to list all or a subset of the blog posts, students, fruits, products, and orders, in your database, the all() model manager method will do that for you.

The .all() method returns a queryset that is evaluated later. Therefore, when you define the Model.objects.all() method in your views, you get a queryset.

The queryset returned contains the queries that can be evaluated to return all objects later.

You can use the slicing syntax or nest a filter method to return a subset of the items in the database through a queryset object. 

A queryset is an object that contains a query and a set of queries. Django will then evaluate the queryset to generate the right SQL statements that are sent to the database and retrieve the items. 

Here is an example code for retrieving all the blog post objects from the database regardless of whether they are published or draft posts.

queryset = BlogPost.objects.all()

Here, I am using the default Django model manager, objects, which provides a couple of methods to get one or more items from the database. The model manager method for retrieving multiple items from the database in Django is the .all() method.

To retrive a single object in Django, you may use the .get() model manager method. Read more about getting a single object in Django in this article.

If you wish to return multiple objects but a subset of all the objects in the database, you can use:

  1. The slicing method (BlogPost.objects.all()[:5]) or
  2. Nesting a filter() method with the .all() or another .filter() model manager method (BlogPost.objects.filter(status=’published’).filter(word_count__gte=2000))

Using the slicing method to retrieve a subset of objects in Django

Let’s say you wish to get objects from the database but limit the results to only five objects. You can do that using the Python slicing syntax that will instruct Django to send queries to the database with a LIMIT clause of 5.

For our BlogPost model, you can retrieve five blog posts like this:

BlogPost.objects.all()[:5]

Besides, using the same Python slicing syntax, you can get the objects starting from blog post number 5 to number 10. The queries sent to the database will include the LIMIT clause of 5 and an OFFSET of 5 to retrieve five objects but not including the first five.

BlogPost.objects.all()[5:10]

Retrieving a subset of objects in your Django database is as easy as that.

Using a nested .filter() method to get multiple items that are a subset of all the objects in the database

A more conventional way to retrieve a subset of objects that meet certain criteria is to use the filter() method with the .all() method. The objects.all() model manager method comes first, followed by a filter() method or multiple nested filter() methods. 

So, you can filter the objects to only include or exclude some objects based on the values of the fields.

For example, you can return all the students that have paid their fees, blog posts that are published, books that are in the store, orders that have been fulfilled, etc. Based on your business requirements, there will be a case where you will be required to retrieve a subset of objects that meet certain criteria. With that, take into consideration to use of the filter() model manager method.

For our example, you can retrieve all published blog posts and display them to your users on a blog page. Thus, your users should not be able to see the draft blog posts.

Here’s how to do that:

BlogPost.objects.filter(status=’published’)

Because we have defined the status field in our BlogPost model, we can use the field and the lookup value we wish to look for in each object. If the field’s value, ‘status’ matches the ‘published’ status, we only get those blog posts and exclude draft blog posts. 

When using the .filter() method, you can nest them to include multiple filter methods. The syntax to use is Model.objects.filter().filter()...

For the BlogPost model, you can retrieve all the published blog posts and their title containing the word ‘blog’ like this.

BlogPost.objects.filter(status='published').filter(title__contains='blog')

In the example above, I have used the two .filter() methods to create a complex query that returns all the published blog posts and their title containing the word ‘blog’ in it.

__contains and __icontains are field lookups that come with Django managers and queryset. Field lookups follow the model field, two underscores, and lookup name syntax.

For example title__contains=’1’ or title__icontains=’blog’

In Django __contains field lookup matches the lowercase of the lookup value provided while the __icontains matches both lowercase and uppercase letters of the lookup value provided.

__contains is case sensitive while __icontains is case insensitive.

In summary, here’s how you would write a function-based view to retrieve all the published blog posts and display them on a web page for your site visitors to read.

Example Project: How to get all the objects in Django

In your views.py file of the blog app, create the following function-based view we’ll use to retrieve multiple blog post objects.

First, import the model at the top

from .models import BlogPost,

Create the view

def published_posts_view(request):
   # retrieve the blog posts objects
   queryset = BlogPost.objects.filter(status='published')
   context = {
       'blogposts': queryset
   }
   template_name = 'list.html'
   return render(request, template_name, context)

After creating the view, connect it to the URL for displaying the blog posts. Usually, the /blog/ URL endpoint.

In your urls.py of the blog app

from django.urls import path

from .views import published_posts_view

urlpatterns = [
   path('blog/', published_posts_view, name='all_blog_posts'),
]

You can loop through the context variable we assigned for the retrieved blog post objects in your templates. It was ‘blogposts

In your /blog/templates/list.html template file, add the following

{% for post in blogposts %}
   <ul>
       <li>
           <a href="#">{{ post.title }}</a>
           <p>{{ post.body|truncatewords:30 }}</p>
       </li>
   </ul>

{% endfor %}

Here, I am iterating through all the posts in the blogposts context variable that invokes the queryset. Django then executes the queries in the queryset to retrieve published blog posts. Remember, Django only sends the SQL queries when we loop through a queryset.

In the <p> tag, I have used the truncatewords template filter to limit the number of words displayed in each blog post’s description. Otherwise, Django would return all the words filling the blog posts list page if I hadn’t used it.

If you access http://127.0.0.1:8000/blog/, you should see all the published blog posts, excluding the draft posts.

results of getting multiple objects from the database in Django

A queryset is an object that contains a query and a set of queries that Django will then evaluate to generate the right SQL statements at a later time. Thus, querysets are lazy loaded.

The purpose of returning the queryset is to avoid hitting the database everytime we call the model manager methods. Besides, we can build multiple queries by having other model manager methods such as .filter() method  nested.

Therefore, we can use the queryset objects to build complex queries. 

Take a scenario where the .all() method returns all the objects from the database. You will end selecting all the objects from the database and returning them. After returning the objects, let’s say you wish to return only the first 10 items for pagination. Therefore, you will add the slicing syntax to get the first 10 items, the second batch with another ten items, the third and so on. Besides, you may wish to retrieve the items in the order they were created. Thus, nesting another .order_by() method.

So,

First, you get the objects

Model.objects.all()

Then, get the first 10 items to display on the first paginated page

Model.objects.all()[:10]

Then get another ten items to display for the second paginated page

Model.objects.all()[10:20]

Then order the items by a field, such as date

BlogPost.objects.all().order_by('-publish_date')[:10]

With the implementation where we disregard a queryset and retrieving the objects every time we encounter the model manager methods such as the .all() method, we end up hitting the database even when it may be unnecessary.

So, getting all the objects by sending the SQL statement for retrieving all the objects would be unnecessary when all we want is to get the first 10 items. 

A better approach is to send the final SQL statements that only gets the first ten items from the database.

Django does that with the help of the queryset that help build complex queries that are only evaluated once they are defined.

So, a query like this: Blogpost.objects.all() will return a queryset rather than retrieving all the objects from the database.

We can build on the queryset and end up with a new queryset like this: Blogpost.objects.filter().filter().filter().order_by(). Thus, we can apply more than one filter and other model manager methods in a queryset before we ever hit the database. 

With such a chained complex query, Django only evaluate the final queries to retrieve the objects when we iterate or convert the queryset to a list. 

Thus, Django does not retrieve all the objects at first, then sorts them, or display only 10 of them. Django only evaluates the final query that limits the results to only 10, ending up getting 10 items from the database. 

The queryset is not evaluated until you do one of the following:

  1. Iterate through the queryset e.g for blogpost in queryset: …
  2. Access an individual element e.g getting the first element, queryset[0]
  3. Access multiple items using the slicing syntax e.g. queryset[0:5] 
  4. Convert the queryset into a list e.g list(queryset)

How to retrieve multiple objects in Django using the .filter() method

With the .filter() method, you can retrieve objects from the database that meet certain criteria or lookup values. Therefore, you can retrieve objects from the database that fulfill a certain requirement, such as being in a category, having a relationship with other models, etc.

Besides, you can also get the objects from the database with certain keywords in their fields by specifying the lookup values in the .filter() model manager methods. An example we saw earlier is where we retrieved blog post items that match a title with the keyword ‘blog’.

The Django filter() method only matches rows that match the search term provided. Same as the .all() model manager, the .filter() method returns a queryset that gets evaluated when you:

  1. Use a for loop to iterate through it getting each object
  2. Convert the queryset into a list.

When retrieving multiple objects in Django, you should use the queryset or query_set variable instead of the associated items generic name. 

So, write,

query_set = Product.objects.all()

And not,

products  = Product.objects.all()

It makes sense because we are only getting a queryset instead of all the products.

After defining your queries to get all the objects, you will get a queryset containing the queries that Django send as SQL statements. In a queryset, you should loop through it or convert it into a list to access the values of the objects. 

After creating the views, you should associate them with the appropriate URL endpoint for displaying them using a template or API. For templates, you have to define the template name you wish to use to display all the objects to your users on a webpage.

Inside the template file for function and class-based views, you can access the data for each object by iterating through the context variable and using Django templates filter, {{ }}, to display each object field value.

To loop through a queryset variable, you can use the template tag, {% for %}, to iterate through the context variable and display each object retrieved.

You can read more about template tags in this article.

So, in your /blog/templates/list.html, write the following code

{% for post in blogposts %}
    <ul>
        <li>
            <a href="#">{{ post.title }}</a>
            <p>{{ post.body|truncatewords:30 }}</p>
        </li>
    </ul>

{% endfor %}

Then create a URL that maps to the view.

In /blog/urls.py

from .views import published_posts_view

urlpatterns = [
    path('', published_posts_view, name='all_blog_posts'),
]

How to get multiple objects in Django using class-based views 

The syntax for retrieving multiple objects in Django is more concise when using class-based views. However, you should use class-based views after you have understood how to retrieve objects using function-based views.

After you understand how getting objects from the database using FBV works, you can clean up your code by converting the function-based views into class-based views.

Let’s see how you can retrieve multiple objects from the database using the class-based views in Django.

The conventional way of getting multiple objects in Django is to use the generic ListView view that will take the model class to retrieve items automatically. The view handles the exceptions associated with retrieving multiple objects from the database.

Here is an example of a class-based view that retrieves all the blog posts from the database.

from django.views.generic import ListView


class BlogPostListView(ListView):
    model = BlogPost
    template_name = 'list.html'
    context_object_name = 'blogposts'

Here, I am importing the ListView view from which our custom BlogPostListView view will inherit. We supply the model from which we wish to retrieve multiple objects and the template name to display the items on a web page.

I have defined the third class variable, context_object_name, within the BlogPostListView view class. The context_object_name defines the context variable to use on a template to loop through the queryset. Therefore, I can iterate through the queryset object to return each item.

You can also define a custom queryset by overriding the get_queryset() method that comes with the class-based views. For example, you can retrieve all the blog posts that are published by writing your view like this:

from django.views.generic import ListView


class BlogPostListView(ListView):
    model = BlogPost
    template_name = 'list.html'
    context_object_name = 'blogposts'
  
    # override the get_queryset method to define custom queries
    def get_queryset(self):
        queryset = super().get_queryset()
        return queryset.filter(status='published')

After creating your class-based view, write the code for the template file. In this example, create the template tag {% for %} to iterate through each blog post and return the values you wish to display on the blog page.

{% for post in blogposts %}
    <ul>
        <li>
            <a href="#">{{ post.title }}</a>
            <p>{{ post.body|truncatewords:30 }}</p>
        </li>
    </ul>

{% endfor %}

In /blog/urls.py

from .views import BlogPostListView

urlpatterns = [
    path('blog/', BlogPostListView.as_view(), name='blog_posts'),
]

Related Questions

What is __ in Django ORM

In Django ORM, two double underscores, __, are used to define field lookups that come with managers and queryset. A lookup comprises a model field and two underscores, followed by a lookup name. The lookup types are supplied to other model manager methods as keyword arguments.

An example of a lookup type being supplied to the filter() method looks like this:

query_set = BlogPost.objects.filter(word_count__gte=2000)

Therefore, you can retrieve multiple objects from the database that matches a particular lookup keyword of a field. A common example of a filed lookup is the, title__icontains='lookupvalue', which is a query that matches objects in the database with the title matching case-insensitive lookup keyword.

Other common field lookup types that come with the default Django model manager are:

  1. __contains for matching case-sensitive lookup values
  2. __gt for getting multiple items that are greater than the lookup integer value
  3. __gte for getting multiple items that are greater or equal to the lookup integer value
  4. __lt for getting multiple items that are less than the lookup integer value
  5. __lte for getting multiple items that are less or equal to the lookup integer value
  6. __exact and __iexact to match exact character match on the field names
  7. __range that matches lookup values for a given range, e.g., get products priced in a given range, such as $20 to $ 40. Example code: Product.objects. filter(price__range=(20, 40))

How to return multiple objects related to ForeignKey in Django

You can use the field lookup types provided by Django model managers to return multiple objects related to the ForeignKey. For example, you can use a field lookup to type to retrieve all the blog posts in a category of id 1 using the following code:

query_set = BlogPost.objects.filter(category__id=1)

Here, I use a field lookup type that uses the category field defined in our BlogPost model class. Then, I filter the objects to get multiple blog post objects only in the category with an id of 1.

Therefore, I can get one or more blog post objects in the category with an id of 1.

Share your love
Badi
Badi

Badi here, creator of ngangasn.com— A website dedicated to providing helpful information and how-to's of web development and hosting. Inspired by a fascination to write the most efficient code to make a computer laugh, & humans, Steve has a passion for organizing characters to create code and informative content.
What makes me happy?
Well, nothing like the feeling of finally figuring out that one pesky bug that's been driving me crazy.

Leave a Reply

Your email address will not be published. Required fields are marked *