Book Image

Web Development with Django - Second Edition

By : Ben Shaw, Saurabh Badhwar, Chris Guest, Bharath Chandra K S
4.7 (3)
Book Image

Web Development with Django - Second Edition

4.7 (3)
By: Ben Shaw, Saurabh Badhwar, Chris Guest, Bharath Chandra K S

Overview of this book

Do you want to develop reliable and secure applications that stand out from the crowd without spending hours on boilerplate code? You’ve made the right choice trusting the Django framework, and this book will tell you why. Often referred to as a “batteries included” web development framework, Django comes with all the core features needed to build a standalone application. Web Development with Django will take you through all the essential concepts and help you explore its power to build real-world applications using Python. Throughout the book, you’ll get the grips with the major features of Django by building a website called Bookr – a repository for book reviews. This end-to-end case study is split into a series of bitesize projects presented as exercises and activities, allowing you to challenge yourself in an enjoyable and attainable way. As you advance, you'll acquire various practical skills, including how to serve static files to add CSS, JavaScript, and images to your application, how to implement forms to accept user input, and how to manage sessions to ensure a reliable user experience. You’ll cover everyday tasks that are part of the development cycle of a real-world web application. By the end of this Django book, you'll have the skills and confidence to creatively develop and deploy your own projects.
Table of Contents (19 chapters)

Django’s database CRUD operations

As we have created the necessary database tables for the book review application, let’s work on understanding the basic database operations with Django.

We’ve already briefly touched on database operations using SQL statements in the SQL CRUD operations section. We tried creating an entry in the database using the insert statement, read from the database using the select statement, updated an entry using the update statement, and deleted an entry from the database using the delete statement.

Django’s ORM provides the same functionality without having to deal with SQL statements. Django’s database operations are simple Python code, hence we overcome the hassle of maintaining SQL statements among the Python code. Let’s take a look at how these are performed.

To execute the CRUD operations, we will enter Django’s command-line shell by executing the following command:

python manage.py shell

Note

For this chapter, we will designate Django shell commands using the >>> notation (highlighted) at the start of the code block. When pasting the query into DB Browser, make sure you exclude this notation every time.

When the interactive console starts, it looks as follows:

Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>>

In the following exercise, we shall perform a create operation.

Exercise 2.02 – creating an entry in the bookr database

In this exercise, you will create a new entry in the database by saving a model instance. In other words, you will create an entry in a database table without explicitly running a SQL query. Follow these steps to do this:

  1. First, import the Publisher class/model from reviews.models:
    >>> from reviews.models import Publisher
  2. Create an object or an instance of the Publisher class by passing all the field values (name, website, and email) required by the Publisher model:
    >>> publisher = Publisher(name='Packt Publishing',
        website='https://www.packtpub.com',
        email='[email protected]')
  3. Next, to write the object into the database, it is important to call the save() method, because until this is called, the entry will not be created in the database:
    >>> publisher.save()

Now you can see a new entry created in the database using DB Browser:

Figure 2.19: An entry created in the database

Figure 2.19: An entry created in the database

  1. Use the object attributes to make any further changes to the object and save the changes to the database:
    >>> publisher.email = '[email protected]'
    >>> publisher.email = '[email protected]'
    >>> publisher.save()

You can see the changes using DB Browser as follows:

Figure 2.20: An entry with the updated email field

Figure 2.20: An entry with the updated email field

In this exercise, we created an entry in the database by creating an instance of the model object and used the save() method to write the model object into the database.

Note that by following the preceding method, the changes to the class instance are not saved until the save() method is called. However, if we use the create() method, Django saves the changes to the database in a single step. We’ll use this method in the exercise that follows.

Exercise 2.03 – using the create() method to create an entry

Unlike the previous exercise where we executed two separate steps by first creating an object and later using the save() method to create an entry in the database, in this exercise, you will create a record in the contributor table using the create() method in a single step:

  1. First, import the Contributor class as before:
    >>> from reviews.models import Contributor
  2. Invoke the create() method to create an object in the database in a single step. Ensure that you pass all the required parameters (first_names, last_names, and email):
    >>> contributor  =
        Contributor.objects.create(first_names="Rowel",
        last_names="Atienza",
        email="[email protected]")
  3. Use DB Browser to verify that the contributor record has been created in the database. If DB Browser is not already open, open the db.sqlite3 database file as we just did in the previous section. Click Browse Data and select the desired table – in this case, the reviews_contributor table from the Table drop-down menu, as shown in the screenshot – and verify the newly created database record:
Figure 2.21: Verifying the creation of the record in DB Browser

Figure 2.21: Verifying the creation of the record in DB Browser

In this exercise, we learned that using the create() method, we can create a record for a model in a database in a single step.

Creating an object with a foreign key

Similar to how we created a record in the Publisher and Contributor tables, let’s now create one for the Book table. If you recall, the Book model has a foreign key to Publisher that cannot have a null value. So, a way to populate the publisher’s foreign key is by providing the created publisher object in the book’s publisher field as shown in the following exercise.

Exercise 2.04 – creating records for a many-to-one relationship

In this exercise, you will create a record in the Book table, including a foreign key to the Publisher model. As you already know, the relationship between Book and Publisher is a many-to-one relationship, so you have to first fetch the Publisher object and then use it while creating the book record. To do that, follow these steps:

  1. First, import the Publisher class:
    >>> from reviews.models import Book, Publisher
  2. Retrieve the publisher object from the database using the following command. The get() method is used to retrieve an object from the database. We still haven’t explored database read operations. For now, use the following command; we will go deeper into reading/retrieving from databases in the next section:
       >>> publisher = 
            Publisher.objects.get(name='PacktPublishing')
  3. When creating a book, we need to supply a date object, as publication_date is a date field in the Book model. So, import date from datetime so that a date object can be supplied when creating the book object, as shown in the following code:
    >>> from datetime import date
  4. Use the create() method to create a record of the book in the database. Ensure that you pass all the fields, namely title, publication_date, isbn, and the publisher object:
    >>> book = Book.objects.create(title="Advanced Deep
        Learning with Keras", publication_date=date(2018,
        10, 31), isbn="9781788629416",
        publisher=publisher)

Note that since publisher is a foreign key and it is not nullable (cannot hold a null value), it is mandatory to pass a publisher object. When the mandatory foreign key object, publisher, is not provided, the database will throw an integrity error.

Figure 2.22 shows the Book table where the first entry is created. Notice that the foreign key field (publisher_id) points to the id value (primary key) of the Publisher table. The publisher_id entry in the book’s record points to a Publisher record has a primary key of 1, as shown in the following two screenshots:

Figure 2.22: Foreign key pointing to the primary key for reviews_book

Figure 2.22: Foreign key pointing to the primary key for reviews_book

Figure 2.23: Foreign key pointing to the primary key for reviews_publisher

Figure 2.23: Foreign key pointing to the primary key for reviews_publisher

In this exercise, we learned that when creating a database record, an object can be assigned to a field if it is a foreign key. We know that the Book model also has a many-to-many relationship with the Contributor model. Let’s now explore the ways to establish many-to-many relations as we create records in the database.

Exercise 2.05 – creating records with many-to-many relationships

In this exercise, you will create a many-to-many relationship between Book and Contributor using the BookContributor relationship model:

  1. In case you have restarted the shell and lost the publisher and book objects, retrieve them from the database by using the following set of Python statements:
    >>> from reviews.models import Book
    >>> from reviews.models import Contributor
    >>> contributor =
        Contributor.objects.get(first_names='Rowel')
    >>> book = Book.objects.get(title="Advanced Deep
        Learning with Keras")
  2. The way to establish a many-to-many relationship is by storing the information about the relationship in the intermediary model or the relationship model; in this case, BookContributor. Since we have already fetched the book and the contributor records from the database, let’s use these objects when creating a record for the BookContributor relationship model. To do so, first, create an instance of the BookContributor relationship class and then save the object to the database. While doing so, ensure you pass the required fields, namely the book object, the contributor object, and role:
    >>> from reviews.models import BookContributor
    >>> book_contributor = BookContributor(book=book,
        contributor=contributor, role='AUTHOR')
    >>> book_contributor.save()

Notice that we set role to AUTHOR while creating the book_contributor object. This is a classic example of storing relationship data while establishing a many-to-many relationship. The role can be AUTHOR, CO_AUTHOR, or EDITOR.

This established the relationship between the book Advanced Deep Learning with Keras and the contributor Rowel (Rowel being the author of the book).

In this exercise, we established a many-to-many relationship between Book and Contributor using the BookContributor relationship model. With regards to the verification of the many-to-many relationship that we just created, we will see this in detail in a few exercises later on in this chapter.

Exercise 2.06 – a many-to-many relationship using the add() method

In this exercise, you will establish a many-to-many relationship using the add() method. When we don’t use a relationship to create the objects, we can use through_default to pass in a dictionary with the parameters defining the required fields. Continuing from the previous exercise, let’s add one more contributor to the book titled Advanced Deep Learning with Keras. This time, the contributor is an editor of the book:

  1. If you have restarted the shell, run the following two commands to import and fetch the desired book instance:
    >>> from reviews.models import Book, Contributor
    >>> book = Book.objects.get(title="Advanced Deep
        Learning with Keras")
  2. Use the create() method to create a contributor, as shown here:
    >>> contributor =
        Contributor.objects.create(first_names='Packt',
        last_names='Example Editor',
        email='[email protected]')
  3. Add the newly created contributor to the book using the add() method. Ensure you provide the role relationship parameter as dict. Enter the following code:
    >>> book.contributors.add(contributor,
        through_defaults={'role': 'EDITOR'})

Thus, we used the add() method to establish a many-to-many relationship between the book and the contributor while storing the relationship data role as EDITOR. Let’s now take a look at other ways of doing this.

Using the create() and set() methods for many-to-many relationships

Assume the book Advanced Deep Learning with Keras has a total of two editors. Let’s use the following method to add another editor to the book. If the contributor is not already present in the database, then we can use the create() method to simultaneously create an entry as well as to establish its relationship with the book:

>>> book.contributors.create(first_names='Packtp',
    last_names='Editor Example',
    email='[email protected]',
    through_defaults={'role': 'EDITOR'})

Similarly, we can also use the set() method to add a list of contributors for a book. Let’s create a publisher, a set of two contributors who are the co-authors, and a book object. First, import the Publisher model, if not already imported, using the following code:

>>> from reviews.models import Publisher

The following code will help us do so:

>>> publisher =
    Publisher.objects.create(name='Pocket Books',
    website='https://pocketbookssampleurl.com',
    email='[email protected]')
>>> contributor1 =
    Contributor.objects.create(first_names='Stephen',
    last_names='Stephen', email='[email protected]')
>>> contributor2 =
    Contributor.objects.create(first_names='Peter',
    last_names='Straub', email='[email protected]')
>>> book = Book.objects.create(title='The Talisman',
    publication_date=date(2012, 9, 25),
    isbn='9781451697216', publisher=publisher)

Since this is a many-to-many relationship, we can add a list of objects in just one go using the set() method. We can use through_defaults to specify the role of the contributors; in this case, they are co-authors:

>>> book.contributors.set([contributor1, contributor2],
    through_defaults={'role': 'CO_AUTHOR'})

Read operations

Django provides us with methods that allow us to read/retrieve from the database. We can retrieve a single object from the database using the get() method. We have already created a few records in the previous sections, so let’s use the get() method to retrieve an object.

Exercise 2.07 – using the get() method to retrieve an object

In this exercise, you will retrieve an object from the database using the get() method. Follow these steps:

  1. Fetch a Publisher object that has a name field of Pocket Books:
    >>> from reviews.models import Publisher
    >>> publisher =
        Publisher.objects.get(name='Pocket Books')
  2. Re-enter the retrieved publisher object and press Enter:
    >>> publisher
    <Publisher: Pocket Books>

Notice that the output is displayed in the shell. This is called a string representation of an object. It is the result of adding the __str__() model method as we did in the Model methods section for the Publisher class.

  1. Upon retrieving the object, you have access to all the object’s attributes. Since this is a Python object, the attributes of the object can be accessed by using . followed by the attribute name. So, you can retrieve the publisher’s name with the following command:
    >>> publisher.name
    'Pocket Books'
  2. Similarly, to retrieve the publisher’s website, use the following:
    >>> publisher.website
    'https://pocketbookssampleurl.com'

The publisher’s email address can be retrieved as follows:

>>> publisher.email
'[email protected]'

In this exercise, we learned how to fetch a single object using the get() method. There are several disadvantages to using this method, though. Let’s find out what they are next.

Returning an object using the get() method

It is important to note that the get() method can only fetch one object. If there is another object carrying the same value as the field mentioned, then we can expect a returned more than one error message. For example, if there are two entries in the Publisher table with the same value for the name field, we can expect an error. In such cases, there are alternate ways to retrieve those objects, which we will explore in the subsequent sections.

We can also get a matching query does not exist error message when there are no objects returned by the get() query. The get() method can be used with any of the object’s fields to retrieve a record. In the following case, we use the website field:

>>> publisher =
    Publisher.objects.get(
    website='https://pocketbookssampleurl.com')

After retrieving the object, we can still get the publisher’s name, as shown here:

>>> publisher.name
'Pocket Books'

Another way to retrieve an object is by using its primary key, pk, as can be seen here:

>>> Publisher.objects.get(pk=2)
<Publisher: Pocket Books>

Using pk for the primary key is a more generic way of using the primary key field. But for the Publisher table, since we know that id is the primary key, we can simply use the id field name to create our get() query:

>>> Publisher.objects.get(id=2)
<Publisher: Pocket Books>

Note

For Publisher and all the other tables, the primary key is id, which Django automatically created. This happens when a primary key field is not mentioned at the time of the creation of the table. But there can be instances where a field can be explicitly declared as a primary key.

In the next exercise, we will see how to query and retrieve all the objects for a given model.

Exercise 2.08 – using the all() method to retrieve a set of objects

We can use the all() method to retrieve a set of all objects. In this exercise, you will use this method to retrieve the names of all contributors. To do that, follow these steps:

  1. Add the following code to retrieve all the objects from the Contributor table:
    >>> from reviews.models import Contributor
    >>> Contributor.objects.all()
    <QuerySet [<Contributor: Rowel>, <Contributor: Packt>, <Contributor: Packtp>, <Contributor: Stephen>, <Contributor: Peter>]>

Upon execution, you will get QuerySet of all the objects.

  1. We can use list indexing to look up a specific object or to iterate over the list using a loop to do any other operation:
    >>> contributors = Contributor.objects.all()
  2. Since Contributor is a list of objects, you can use indexing to access any element in the list, as shown in the following command:
    >>> contributors[0]
    <Contributor: Rowel>

In this case, the first element in the list is a contributor with a first_names value of 'Rowel' and a last_names value of 'Atienza', as you can see from the following code:

>>> contributors[0].first_names
'Rowel'
>>> contributors[0].last_names
'Atienza'

In this exercise, we learned how to retrieve all the objects using the all() method and how to use the retrieved set of objects as a list.

Retrieving objects by filtering

If we have more than one object for a field value, we cannot use the get() method since the get() method can return only one object. For such cases, we have the filter() method, which can retrieve all the objects that match a specified condition.

Exercise 2.09 – using the filter() method to retrieve objects

In this exercise, you will use the filter() method to get a specific set of objects for a certain condition. Specifically, you will retrieve all the contributors’ names whose first name is Peter. To do that, follow these steps:

  1. First, create two more contributors:
    >>> from reviews.models import Contributor
    >>> Contributor.objects.create(first_names='Peter',
        last_names='Wharton',
        email='[email protected]')
    >>> Contributor.objects.create(first_names='Peter',
        last_names='Tyrrell',
        email='[email protected]')
  2. To retrieve contributors who have the first_names value of Peter, add the following code:
    >>> Contributor.objects.filter(first_names='Peter')
    <QuerySet [<Contributor: Peter>, <Contributor: Peter>,
    <Contributor: Peter>]>
  3. The filter() method returns the object even if there is only one. You can see this here:
    >>> Contributor.objects.filter(first_names='Rowel')
    <QuerySet [<Contributor: Rowel>]>
  4. Furthermore, the filter() method returns an empty QuerySet if nothing matches the query. This can be seen here:
    >>> Contributor.objects.filter(first_names='Nobody')
    <QuerySet []>

In this exercise, we saw the use of filters to retrieve a set of a few objects filtered by a certain condition. In the next section, we will learn about filtering by using field lookups.

Filtering by field lookups

Now, let’s suppose we want to filter and query a set of objects using the object’s fields by providing certain conditions. In such a case, we can use a double-underscore lookup. For example, the Book object has a publication_date field; let’s say we want to filter and fetch all the books that were published after 01-01-2014. We can easily look these up by using the double-underscore method. To do this, we will first import the Book model:

>>> from reviews.models import Book
>>> book = Book.objects.filter(publication_date__gt=date(
    2014, 1, 1))

Here, publication_date__gt indicates the publication date, which is greater than (gt) a certain specified date, in this case, 01-01-2014. Similar to this, we have the following abbreviations:

  • lt: Less than
  • lte: Less than or equal to
  • gte: Greater than or equal to

The result after filtering can be seen here:

>>> book
<QuerySet [<Book: Advanced Deep Learning with Keras>]>

Here is the publication date of the book that is part of the query set, which confirms that the publication date was after 01-01-2014:

>>> book[0].publication_date
datetime.date(2018, 10, 31)

Using pattern matching for filtering operations

For filtered results, we can also look up whether the parameter contains a part of the string we are looking for:

>>> book =
    Book.objects.filter(title__contains='Deep learning')

Here, title__contains looks for all objects with titles containing 'Deep learning' as a part of the string:

>>> book
<QuerySet [<Book: Advanced Deep Learning with Keras>]>
>>> book[0].title
'Advanced Deep Learning with Keras'

Similarly, we can use icontains if the string match needs to be case-insensitive. Using startswith matches any string starting with the specified string.

Retrieving objects by using the exclude() method

In the previous section, we learned about fetching a set of objects by matching a certain condition. Now, suppose we want to do the opposite; that is, we want to fetch all those objects that do not match a certain condition. In such cases, we can use the exclude() method to exclude a certain condition and fetch all the required objects. This will be clearer with an example. The following is a list of all contributors:

>>> Contributor.objects.all()
<QuerySet [<Contributor: Rowel>, <Contributor: Packt>,
<Contributor: Packtp>, <Contributor: Stephen>,
<Contributor: Peter>, <Contributor: Peter>,
<Contributor: Peter>]>

Now, from this list, we will exclude all those contributors who have a first_names value of Peter:

>>> Contributor.objects.exclude(first_names='Peter')
<QuerySet [<Contributor: Rowel>, <Contributor: Packt>,
<Contributor: Packtp>, <Contributor: Stephen>]>

We see here that the query returned all contributors whose first name is not Peter.

Retrieving objects using the order_by() method

We can retrieve a list of objects while ordering by a specified field using the order_by() method. For example, in the following code snippet, we order the books by their publication date:

>>> books = Book.objects.order_by("publication_date")
>>> books
<QuerySet [<Book: The Talisman>, <Book: Advanced Deep Learning with Keras>]>

Let’s examine the order of the query. Since the query set is a list, we can use indexing to check the publication date of each book:

>>> books[0].publication_date
datetime.date(2012, 9, 25)
>>> books[1].publication_date
datetime.date(2018, 10, 31)

Notice that the publication date of the first book with a 0 index is older than the publication date of the second book with a 1 index. So, this confirms that the queried list of books has been properly ordered as per their publication dates. We can also use a prefix with the negative sign for the field parameter to order results in descending order. This can be seen from the following code snippet:

>>> books = Book.objects.order_by("-publication_date")
>>> books
<QuerySet [<Book: Advanced Deep Learning with Keras>,
<Book: The Talisman>]>

Since we have prefixed a negative sign to the publication date, notice that the queried set of books has now been returned in the opposite order, where the first book object with a 0 index has a more recent date than the second book:

>>> books[0].publication_date
datetime.date(2018, 10, 31)
>>> books[1].publication_date
datetime.date(2012, 9, 25)

We can also order by using a string field or a numerical one. For example, the following code can be used to order books by their primary key or id:

>>> books = Book.objects.order_by('id')
>>> books
<QuerySet [<Book: Advanced Deep Learning with Keras>,
<Book: The Talisman>]>

The queried set of books has been ordered by book id in ascending order:

>>> books[0].id
1
>>> books[1].id
2

Again, to order it in descending order, the negative sign can be used as a prefix, as follows:

>>> books = Book.objects.order_by('-id')
>>> books
<QuerySet [<Book: The Talisman>, <Book: Advanced Deep Learning with Keras>]>

Now, the queried set of books has been ordered by book id in descending order:

>>> books[0].id
2
>>> books[1].id
1

To order by a string field in alphabetical order, we can do something like this:

>>> books = Book.objects.order_by('title')
>>> books
<QuerySet [<Book: Advanced Deep Learning with Keras>, <Book: The Talisman>]>

Since we have used the title of the book to order by, the query set has been ordered in alphabetical order. We can see this as follows:

>>> books[0]
<Book: Advanced Deep Learning with Keras>
>>> books[1]
<Book: The Talisman>

Similar to what we’ve seen for the previous ordering types, the negative sign prefix can help us sort in reverse alphabetical order, as we can see here:

>>> books = Book.objects.order_by('-title')
>>> books
<QuerySet [<Book: The Talisman>, <Book: Advanced Deep Learning with Keras>]>

This will lead to the following output:

>>> books[0]
<Book: The Talisman>
>>> books[1]
<Book: Advanced Deep Learning with Keras>

Yet another useful method offered by Django is values(). It helps us get a query set of dictionaries instead of objects. In the following code snippet, we’re using this for a Publisher object:

>>> publishers = Publisher.objects.all().values()
>>> publishers
<QuerySet [{'id': 1, 'name': 'Packt Publishing', 'website':
'https://www.packtpub.com', 'email':
'[email protected]'}, {'id': 2, 'name':
'Pocket Books', 'website':
'https://pocketbookssampleurl.com',
'email': '[email protected]'}]>
>>> publishers[0]
{'id': 1, 'name': 'Packt Publishing', 'website':
'https://www.packtpub.com', 'email':
'[email protected]'}
>>> publishers[0]
{'id': 1, 'name': 'Packt Publishing', 'website':
'https://www.packtpub.com', 'email':
'[email protected]'}

In the next few sections, we will explore how to query across relationships.

Querying across relationships

As we have studied in this chapter, the reviews app has two kinds of relationships: many-to-one and many-to-many. So far, we have learned various ways of making queries using get(), filters, field lookups, and so on. Now let’s study how to perform queries across relationships. There are several ways to go about this – we could use foreign keys, object instances, and more. Let’s explore these with the help of some examples.

Querying using foreign keys

When we have relationships across two models/tables, Django provides a way to perform a query using the relationship. The command shown in this section will retrieve all the books published by Packt Publishing by performing a query using model relationships. Similar to what we’ve seen previously, this is done using the double-underscore lookup. For example, the Book model has a foreign key of publisher pointing to the Publisher model. Using this foreign key, we can perform a query using double underscores and the name field in the Publisher model. This can be seen from the following code:

>>> Book.objects.filter(publisher__name='Packt Publishing')
<QuerySet [<Book: Advanced Deep Learning with Keras>]>

Querying using the model name

Another way of querying is using a relationship to do the query backward, using the model name in lowercase. For instance, let’s say we want to query the publisher who published the book Advanced Deep Learning with Keras using model relationships in the query. For this, we can execute the following statement to retrieve the Publisher information object:

>>> Publisher.objects.get(book__title='Advanced Deep
    Learning with Keras')
<Publisher: Packt Publishing>

Here, book is the model’s name in lowercase. As we already know, the Book model has a publisher foreign key with a name value of Packt Publishing.

Querying across foreign key relationships using the object instance

We can also retrieve the information using the object’s foreign key. Suppose we want to query the publisher’s name for the title The Talisman:

>>> book = Book.objects.get(title='The Talisman')
>>> book.publisher
<Publisher: Pocket Books>

Using the object here is an example of where we use the reverse direction to get all the books published by a publisher by using the set.all() method:

>>> publisher = Publisher.objects.get(name='Pocket Books')
>>> publisher.book_set.all()
<QuerySet [<Book: The Talisman>]>

We can also create queries using chains of queries:

>>> Book.objects.filter(publisher__name='Pocket
    Books').filter(title='The Talisman')
<QuerySet [<Book: The Talisman>]>

Let’s perform some more exercises to shore up our knowledge of the various kinds of queries we have learned about so far.

Exercise 2.10 – querying across a many-to-many relationship using the field lookup

We know that Book and Contributor have a many-to-many relationship. In this exercise, without creating an object, you will perform a query to retrieve all the contributors who contributed to writing the book titled The Talisman:

  1. First, import the Contributor class:
    >>> from reviews.models import Contributor
  2. Now, add the following code to query for the set of contributors to The Talisman:
    >>> Contributor.objects.filter(book__title='The
        Talisman')

You should see the following:

<QuerySet [<Contributor: Stephen>, <Contributor: Peter>]>

From the preceding output, we can see that Stephen and Peter are the contributors who contributed to writing the book The Talisman. The query uses the book model (written in lowercase) and does a field lookup for the title field using a double underscore, as shown in the command.

In this exercise, we learned how to perform queries across many-to-many relationships using a field lookup. Let’s now look at using another method to carry out the same task.

Exercise 2.11 – a many-to-many query using objects

In this exercise, using a Book object, search for all the contributors who contributed to writing the book with the title The Talisman. The following steps will help you do that:

  1. Import the Book model:
    >>> from reviews.models import Book
  2. Retrieve a book object with the title The Talisman, by adding the following line of code:
    >>> book = Book.objects.get(title='The Talisman')
  3. Then, retrieve all the contributors who worked on the book The Talisman using the book object. Add the following code to do so:
    >>> book.contributors.all()
    <QuerySet [<Contributor: Stephen>, <Contributor: Peter>]>

Again, we can see that Stephen and Peter are the contributors who worked on the book The Talisman. Since the book has a many-to-many relationship with contributors, we have used the contributors.all() method to get a query set of all those contributors who worked on the book. Now, let’s try using the set() method to perform a similar task.

Exercise 2.12 – a many-to-many query using the set() method

In this exercise, you will use a contributor object to fetch all the books written by the contributor named Rowel:

  1. Import the Contributor model:
    >>> from reviews.models import Contributor
  2. Fetch a contributor object whose first_names is 'Rowel' using the get() method:
    >>> contributor =
        Contributor.objects.get(first_names='Rowel')
  3. Using the contributor object and the book_set() method, get all those books written by the contributor:
    >>> contributor.book_set.all()
    <QuerySet [<Book: Advanced Deep Learning with Keras>]>

Since Book and Contributor have a many-to-many relationship, we can use the set() method to query a set of objects associated with the model. In this case, contributor.book_set.all() returned all the books written by the contributor.

Exercise 2.13 – using the update() method

In this exercise, you will use the update() method to update an existing record:

  1. Change first_names for a contributor who has the last name Tyrrell:
    >>> from reviews.models import Contributor
    >>> Contributor.objects.filter(last_names='Tyrrell').
        update(first_names='Mike')
    1

The return value shows the number of records that have been updated. In this case, one record has been updated.

  1. Fetch the contributor that was just modified using the get() method and verify that the first name has been changed to Mike:
    >>> Contributor.objects.get(last_names='Tyrrell').
        first_names
    'Mike'

Note

If the filter operation has more than one record, then the update() method will update the specified field in all the records returned by the filter.

In this exercise, we learned how to use the update() method to update a record in the database. Now, finally, let’s try deleting a record from the database using the delete() method.

Exercise 2.14 – using the delete() method

An existing record in the database can be deleted using the delete() method. In this exercise, you will delete a record from the contributors table that has the last_name value of Wharton:

  1. Fetch the object using the get() method and use the delete() method, as shown here:
    >>> from reviews.models import Contributor
    >>> Contributor.objects.get(last_names='Wharton').
        delete()
    (1, {'reviews.Contributor': 1}

Notice that you called the delete() method without assigning the contributor object to a variable. Since the get() method returns a single object, you can access the object’s method without actually creating a variable for it.

  1. Verify the contributor object with last_name as 'Wharton' has been deleted:
    >>> Contributor.objects.get(last_names='Wharton')
    Traceback (most recent call last):
    File "<console>", line 1, in <module>
    File "/../site-packages/django/db/models/manager.py",
    line 82, in manager_method
    return getattr(self.get_queryset(), name)(*args,
    **kwargs)
    File "/../site-packages/django/db/models/query.py",
    line 417, in get
    self.model._meta.object_name
    reviews.models.Contributor.DoesNotExist: Contributor
    matching query does not exist.

As you can see, upon running the query, we got an object does not exist error. This is expected since the record has been deleted. In this exercise, we learned how to use the delete() method to delete a record from the database.