Working with QuerySet and managers

Now that you have a fully functional administration site to manage your blog's content, it's time to learn how to retrieve information from the database and interact with it. Django comes with a powerful database-abstraction API that lets you create, retrieve, update, and delete objects easily. The Django Object-relational Mapper (ORM) is compatible with MySQL, PostgreSQL, SQLite, and Oracle. Remember that you can define the database of your project by editing the DATABASES setting in the settings.py file of your project. Django can work with multiple databases at a time and you can even program database routers that handle the data in any way you like.

Once you have created your data models, Django gives you a free API to interact with them. You can find the data model reference of the official documentation at https://docs.djangoproject.com/en/1.8/ref/models/.

Creating objects

Open the terminal and run the following command to open the Python shell:

python manage.py shell

Then type the following lines:

>>> from django.contrib.auth.models import User
>>> from blog.models import Post
>>> user = User.objects.get(username='admin')
>>> Post.objects.create(title='One more post',
                    slug='one-more-post',
                    body='Post body.',
                    author=user)
>>> post.save()

Let's analyze what this code does. First, we retrieve the user object that has the username admin:

user = User.objects.get(username='admin')

The get() method allows you to retrieve a single object from the database. Note that this method expects one result that matches the query. If no results are returned by the database, this method will raise a DoesNotExist exception, and if the database returns more than one result, it will raise a MultipleObjectsReturned exception. Both exceptions are attributes of the model class that the query is being performed on.

Then we create a Post instance with a custom title, slug, and body; and we set the user we previously retrieved as author of the post:

post = Post(title='Another post', slug='another-post', body='Post body.', author=user)

Note

This object is in memory and is not persisted to the database.

Finally, we save the Post object to the database using the save() method:

post.save()

This action performs an INSERT SQL statement behind the scenes. We have seen how to create an object in memory first and then persist it to the database, but we can also create the object into the database directly using the create() method as follows:

Post.objects.create(title='One more post', slug='one-more-post', body='Post body.', author=user)

Updating objects

Now, change the title of the post into something different and save the object again:

>>> post.title = 'New title'
>>> post.save()

This time, the save() method performs an UPDATE SQL statement.

Note

The changes you make to the object are not persisted to the database until you call the save() method.

Retrieving objects

The Django Object-relational mapping (ORM) is based on QuerySet. A QuerySet is a collection of objects from your database that can have several filters to limit the results. You already know how to retrieve a single object from the database using the get() method. As you have seen, we have accessed this method using Post.objects.get(). Each Django model has at least one manager, and the default manager is called objects. You get a QuerySet object by using your models manager. To retrieve all objects from a table, you just use the all() method on the default objects manager, like this:

>>> all_posts = Post.objects.all()

This is how we create a QuerySet that returns all objects in the database. Note that this QuerySet has not been executed yet. Django QuerySets are lazy; they are only evaluated when they are forced to do it. This behavior makes QuerySets very efficient. If we don't set the QuerySet to a variable, but instead write it directly on the Python shell, the SQL statement of the QuerySet is executed because we force it to output results:

>>> Post.objects.all()

Using the filter() method

To filter a QuerySet, you can use the filter() method of the manager. For example, we can retrieve all posts published in the year 2015 using the following QuerySet:

Post.objects.filter(publish__year=2015)

You can also filter by multiple fields. For example, we can retrieve all posts published in 2015 by the author with the username admin:

Post.objects.filter(publish__year=2015, author__username='admin')

This equals to building the same QuerySet chaining multiple filters:

Post.objects.filter(publish__year=2015)
             filter(author__username='admin')

Note

We are building queries with field lookup methods using two underscores (publish__year), but we are also accessing fields of related models using two underscores (author__username).

Using exclude()

You can exclude certain results from your QuerySet using the exclude() method of the manager. For example, we can retrieve all posts published in 2015 whose titles don't start by Why:

Post.objects.filter(publish__year=2015)
            .exclude(title__startswith='Why')

Using order_by()

You can order results by different fields using the order_by() method of the manager. For example, you can retrieve all objects ordered by their title:

Post.objects.order_by('title')

Ascending order is implied. You can indicate descending order with a negative sign prefix, like this:

Post.objects.order_by('-title')

Deleting objects

If you want to delete an object, you can do it from the object instance:

post = Post.objects.get(id=1)
post.delete()

Note

Note that deleting objects will also delete any dependent relationships.

When QuerySet are evaluated

You can concatenate as many filters as you like to a QuerySet and you will not hit the database until the QuerySet is evaluated. Querysets are only evaluated in the following cases:

  • The first time you iterate over them
  • When you slice them. for instance: Post.objects.all()[:3]
  • When you pickle or cache them
  • When you call repr() or len() on them
  • When you explicitly call list()on them
  • When you test it in a statement such as bool(), or , and, or if

Creating model managers

As we previously mentioned, objects is the default manager of every model, which retrieves all objects in the database. But we can also define custom managers for our models. We are going to create a custom manager to retrieve all posts with published status.

There are two ways to add managers to your models: You can add extra manager methods or modify initial manager querysets. The first one turns up something like Post.objects.my_manager()and the later like Post.my_manager.all(). Our manager will allow us to retrieve posts using Post.published.

Edit the models.py file of your blog application to add the custom manager:

class PublishedManager(models.Manager):
    def get_queryset(self):
        return super(PublishedManager,  
                     self).get_queryset()
                          .filter(status='published')

class Post(models.Model):
    # ...
    objects = models.Manager() # The default manager.
    published = PublishedManager() # Our custom manager.

get_queryset() is the method that returns the QuerySet to be executed. We use it to include our custom filter in the final QuerySet. We have defined our custom manager and added it to the Post model; we can now use it to perform queries. For example, we can retrieve all published posts whose title starts with Who using:

Post.published.filter(title__startswith='Who') 
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset