QuerySet
1. The Role of QuerySets in Django
A QuerySet represents a collection of database queries that return a set of results. QuerySets are lazily evaluated, meaning that the database query is only executed when the QuerySet is evaluated (e.g., when iterating over it or converting it to a list).
a. Basic QuerySet Operations
QuerySets allow you to filter, order, and retrieve data from the database.
# Retrieving all objects
books = Book.objects.all()
# Filtering objects
filtered_books = Book.objects.filter(author="George Orwell")
# Chaining filters (AND logic)
recent_books = Book.objects.filter(published_date__year=2020).filter(author="George Orwell")
# Retrieving a single object
book = Book.objects.get(id=1)
# Exclude objects
non_orwell_books = Book.objects.exclude(author="George Orwell")
# Ordering results
ordered_books = Book.objects.order_by('published_date')
# Limit number of results (slicing)
first_ten_books = Book.objects.all()[:10]b. QuerySet Evaluation
A QuerySet is lazily evaluated, meaning the query is not actually executed in the database until the QuerySet is iterated over or explicitly evaluated.
Examples of when QuerySets are evaluated: - Iterating over the QuerySet. - Slicing the QuerySet. - Serializing the QuerySet (e.g., converting it to a list or calling len()).
# Query is not executed yet
books = Book.objects.filter(author="George Orwell")
# Query is executed when you evaluate the QuerySet
for book in books:
    print(book.title)2. Custom QuerySets
Instead of overriding the manager, you can create a custom QuerySet and use it directly in your model manager. This method allows you to chain custom QuerySet methods.
a. Creating a Custom QuerySet
class BookQuerySet(models.QuerySet):
    def published(self):
        return self.filter(published_date__isnull=False)
    def by_author(self, author_name):
        return self.filter(author=author_name)
# Use the custom QuerySet in a manager
class BookManager(models.Manager):
    def get_queryset(self):
        return BookQuerySet(self.model, using=self._db)
# Use the manager in the model
class Book(models.Model):
    title = models.CharField(max_length=100)
    author = models.CharField(max_length=100)
    published_date = models.DateField()
    objects = BookManager()Now you can chain custom QuerySet methods together:
books = Book.objects.published().by_author("George Orwell")b. Chaining Custom QuerySet Methods
Custom QuerySet methods allow you to chain operations and write more readable and reusable query logic:
books = Book.objects.published().by_author("J.K. Rowling").order_by('published_date')c. Combining Managers and QuerySets
A common pattern is to define both custom QuerySets and managers, allowing you to use manager-level logic while preserving the ability to chain QuerySet methods.
class BookQuerySet(models.QuerySet):
    def published(self):
        return self.filter(published_date__isnull=False)
class BookManager(models.Manager):
    def get_queryset(self):
        return BookQuerySet(self.model, using=self._db)
    def published_last_year(self):
        last_year = timezone.now().year - 1
        return self.get_queryset().published().filter(published_date__year=last_year)3. QuerySet Methods
Django’s QuerySet API provides numerous built-in methods to filter, manipulate, and aggregate data. Some of the most commonly used methods are:
1. Retrieval Methods
- all(): Returns a new- QuerySetcontaining all objects in the database.
- filter(**kwargs): Returns a new- QuerySetcontaining objects that match the given lookup parameters.
- exclude(**kwargs): Returns a new- QuerySetexcluding objects that match the given lookup parameters.
- get(**kwargs): Returns a single object matching the given lookup parameters. Raises- DoesNotExistif no object is found and- MultipleObjectsReturnedif more than one object matches.
- first(): Returns the first object in the- QuerySet, or- Noneif the- QuerySetis empty.
- last(): Returns the last object in the- QuerySet, or- Noneif the- QuerySetis empty.
- earliest(field_name=None): Returns the earliest object according to a given field or- Metaordering.
- latest(field_name=None): Returns the latest object according to a given field or- Metaordering.
- count(): Returns the number of objects in the- QuerySet.
- exists(): Returns- Trueif the- QuerySetcontains any results, and- Falseif it is empty.
2. Aggregation and Annotation
- aggregate(**kwargs): Returns a dictionary with the results of aggregating over the- QuerySet.
- annotate(**kwargs): Adds annotations to each object in the- QuerySetbased on the provided aggregate functions.
3. Ordering Methods
- order_by(*field_names): Returns a new- QuerySetwith the objects ordered by the given fields.
- reverse(): Reverses the order of the- QuerySet.
4. Limiting Methods
- distinct(): Returns a new- QuerySetwith distinct results.
- values(*fields): Returns a- QuerySetthat returns dictionaries when iterated over, each representing an object with the specified fields as keys.
- values_list(*fields, flat=False): Returns a- QuerySetthat yields tuples when iterated over. If- flat=True, it returns single values.
- only(*fields): Limits the fields that are loaded to the ones specified.
- defer(*fields): Defers the loading of the specified fields until they are accessed.
5. Modification Methods
- update(**kwargs): Updates fields in the database for all objects in the- QuerySet(returns the number of rows affected).
- delete(): Deletes all objects in the- QuerySet(returns the number of objects deleted and a dictionary with details).
- create(**kwargs): Creates a new object, saves it, and returns it.
- bulk_create(objs, batch_size=None, ignore_conflicts=False): Inserts multiple objects into the database in a single query.
- bulk_update(objs, fields, batch_size=None): Updates multiple objects with new field values in a single query.
6. Chaining and Combining Methods
- union(*other_qs, all=False): Combines- QuerySetobjects by performing an SQL- UNION.
- intersection(*other_qs): Combines- QuerySetobjects by performing an SQL- INTERSECT.
- difference(*other_qs): Combines- QuerySetobjects by performing an SQL- EXCEPT.
7. Boolean and Existence Methods
- exists(): Returns- Trueif the- QuerySetcontains any results,- Falseif not.
- none(): Returns an empty- QuerySet.
8. Caching and Evaluation
- iterator(): Uses an iterator to loop over the- QuerySetwithout caching results.
- select_related(*fields): Creates an SQL join and includes related objects in the- QuerySet.
- prefetch_related(*lookups): Prefetches related objects to reduce the number of database queries.
9. Date and Time Methods
- dates(field_name, kind, order='ASC'): Returns a- QuerySetof all available dates.
- datetimes(field_name, kind, order='ASC', tzinfo=None): Returns a- QuerySetof all available datetimes.
10. Advanced Querying
- extra(select=None, where=None, params=None, tables=None, order_by=None, select_params=None): Adds extra SQL fragments to the query.
- raw(raw_query, params=None, translations=None): Executes a raw SQL query.
These methods make it easy to create powerful, flexible queries in Django.