Search icon CANCEL
Subscription
0
Cart icon
Your Cart (0 item)
Close icon
You have no products in your basket yet
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Free Learning
Arrow right icon
Arrow up icon
GO TO TOP
Web Development with Django

You're reading from   Web Development with Django Learn to build modern web applications with a Python-based framework

Arrow left icon
Product type Paperback
Published in Feb 2021
Publisher Packt
ISBN-13 9781839212505
Length 826 pages
Edition 1st Edition
Languages
Tools
Arrow right icon
Authors (5):
Arrow left icon
Saurabh Badhwar Saurabh Badhwar
Author Profile Icon Saurabh Badhwar
Saurabh Badhwar
Bharath Chandra K S Bharath Chandra K S
Author Profile Icon Bharath Chandra K S
Bharath Chandra K S
Andrew Bird Andrew Bird
Author Profile Icon Andrew Bird
Andrew Bird
Ben Shaw Ben Shaw
Author Profile Icon Ben Shaw
Ben Shaw
Chris Guest Chris Guest
Author Profile Icon Chris Guest
Chris Guest
+1 more Show less
Arrow right icon
View More author details
Toc

Table of Contents (17) Chapters Close

Preface
1. Introduction to Django 2. Models and Migrations FREE CHAPTER 3. URL Mapping, Views, and Templates 4. Introduction to Django Admin 5. Serving Static Files 6. Forms 7. Advanced Form Validation and Model Forms 8. Media Serving and File Uploads 9. Sessions and Authentication 10. Advanced Django Admin and Customizations 11. Advanced Templating and Class-Based Views 12. Building a REST API 13. Generating CSV, PDF, and Other Binary Files 14. Testing 15. Django Third-Party Libraries 16. Using a Frontend JavaScript Library with Django

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 section titled SQL CRUD Operations. We tried creating an entry into 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 the 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. While 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)
>>> 

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:

  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='info@packtpub.com')
  3. Next, to write the object into the database, it is important to call the save() method, because until this is called there will not be an entry created in the database:
    >>>publisher.save()

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

    Figure 2.20: Entry created in the database

    Figure 2.20: Entry created in the database

  4. Use the object attributes to make any further changes to the object and save the changes to the database:
    >>>publisher.email
    'info@packtpub.com'
    >>> publisher.email = 'customersupport@packtpub.com'
    >>> publisher.save()

    You can see the changes using DB Browser as follows:

    Figure 2.21: Entry with the updated email field

Figure 2.21: Entry with the updated email field

In this exercise, you 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

Here, 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="RowelAtienza@example.com")
  3. Use DB Browser to verify that the contributor record has been created in the database. If your DB Browser is not already open, open the database file db.sqlite3 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 dropdown, as shown in the screenshot – and verify the newly created database record:
    Figure 2.22: Verifying the creation of the record in DB Browser

Figure 2.22: 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:

  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 database read/retrieve in the next section:
    >>>publisher = Publisher.objects.get(name='Packt Publishing')
  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.23 shows the Book table where the first entry is created. Notice that the foreign key field (publisher_id ) points to the id (primary key) of the Publisher table. The entry publisher_id in the book's record is pointing to a Publisher record that has id (primary key) 1 as shown in the following two screenshots:

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

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

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

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

In this exercise, we learned that while 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 relationship model BookContributor:

  1. In case you have restarted the shell and lost the publisher and the 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, it is BookContributor. Since we have already fetched the book and the contributor records from the database, let's use these objects while 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, contributor object, and role:
    >>>from reviews.models import BookContributor
    >>>book_contributor = BookContributor(book=book,   contributor=contributor, role='AUTHOR')
    >>> book_contributor.save()

    Notice that we specified the role as 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 the 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='PacktEditor@example.com')
  3. Add the newly created contributor to the book using the add() method. Ensure you provide the relationship parameter role 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 contributor while storing the relationship data role as Editor. Let's now take a look at other ways of doing this.

Using 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 the relation with the book:

>>>book.contributors.create(first_names='Packtp', last_names=  'Editor Example', email='PacktEditor2@example.com',     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='pocketbook@example.com')
>>> contributor1 = Contributor.objects.create(first_names=  'Stephen', last_names='Stephen', email='StephenKing@example.com')
>>> contributor2 = Contributor.objects.create(first_names=  'Peter', last_names='Straub', email='PeterStraub@example.com')
>>> 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:

  1. Fetch a Publisher object that has a name field with the value 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 model method __str__() as we did in the Model Methods section for the Publisher class.

  3. 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'
  4. Similarly, retrieve the publisher's website:
    >>> publisher.website
    'https://pocketbookssampleurl.com'

    The publisher's email address can be retrieved as well:

    >>> publisher.email
    'pocketbook@example.com'

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 why.

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 be exploring in the subsequent sections.

We can also get a "matching query does not exist" error message when there are no objects returned from 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 are using 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 field name id 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 was automatically created by Django. 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.

Exercise 2.08: Using the all() Method to Retrieve a Set of Objects

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

  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 a QuerySet of all the objects.

  2. 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()
  3. 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 we also learned 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, then 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 who have their first name as Peter:

  1. First, create two more contributors:
    >>>from reviews.models import Contributor
    >>> Contributor.objects.create(first_names='Peter', last_names='Wharton', email='PeterWharton@example.com')
    >>> Contributor.objects.create(first_names='Peter', last_names='Tyrrell', email='PeterTyrrell@example.com')
  2. To retrieve those contributors who have the value of first_names as 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 there is none matching 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.

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 what is called a double-underscore lookup. For example, the Book object has a field named publication_date; 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 those 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 Excluding

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 the value of first_names as Peter:

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

We see here that the query returned all those 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 index 0 is older than the publication date of the second book with index 1. 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 index 0 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. For example, the following code can be used to order books by their primary key or id:

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

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

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

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

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

Now, the queried set of books has been ordered per 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:

>>>Book.objects.order_by('title')
<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:

>>> Book.objects.order_by('-title')
<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':     'customersupport@packtpub.com'}, {'id': 2, 'name':       'Pocket Books', 'website': 'https://pocketbookssampleurl.com',        'email': 'pocketbook@example.com'}]>
>>> publishers[0]
{'id': 1, 'name': 'Packt Publishing', 'website':  'https://www.packtpub.com', 'email':     'customersupport@packtpub.com'}
>>> publishers[0]
{'id': 1, 'name': 'Packt Publishing', 'website':   'https://www.packtpub.com', 'email':    'customersupport@packtpub.com'}

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 field name 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 Model Name

Another way of querying is where we can use 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 name in lowercase. As we already know, the Book model has a publisher foreign key with the value of name as 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 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 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 on 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 the double underscore as shown in the command.

In this exercise, we learned how to perform queries across many-to-many relationships using 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.

  2. 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 value of last_name as 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.BookContributor': 0, '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.

  2. 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.

Activity 2.01: Create Models for a Project Management Application

Imagine you are developing a project management application called Juggler. Juggler is an application that can track multiple projects, and each project can have multiple tasks associated with it. The following steps will help you complete this activity:

  1. Using the techniques we have learned so far, create a Django project called juggler.
  2. Create a Django app called projectp.
  3. Add the app projects in the juggler/settings.py file.
  4. Create two related model classes called Project and Task in projectp/models.py.
  5. Create migration scripts and migrate the models' definitions to the database.
  6. Open the Django shell now and import the models.
  7. Populate the database with an example and write a query displaying the list of tasks associated with a given project.

    Note

    The solution to this activity can be found at http://packt.live/2Nh1NTJ.

Populating the Bookr Project's Database

Although we know how to create database records for the project, in the next few chapters, we will have to create a lot of records to work with the project. For that reason, we have created a script that can make things easy for us. This script populates the database by reading a .csv (Comma-Separated Values) file consisting of many records. Follow the next few steps to populate the project's database:

  1. Create the following folder structure inside the project directory:
    bookr/reviews/management/commands/
  2. Copy the loadcsv.py file from the following location and WebDevWithDjangoData.csv into the folder created. This can be found on the GitHub repository for this book at http://packt.live/3pvbCLM.

    Because loadcsv.py is placed inside the management/commands folder, now it works like a Django custom management command. You can go through the loadcsv.py file and read more about writing Django custom management commands at this link: https://docs.djangoproject.com/en/3.0/howto/custom-management-commands/.

  3. Now let's recreate a fresh database. Delete your SQL database file present in the project folder:
    rm reviews/db.sqlite3
  4. To create a fresh database again, execute the Django migrate command:
    python manage.py migrate

    Now you can see the newly created db.sqlite3 file under the reviews folder.

  5. Execute the custom management command loadcsv to populate the database:
    python manage.py loadcsv --csv reviews/management/commands/WebDevWithDjangoData.csv
  6. Using DB Browser for SQLite, verify that all the tables created by the bookr project are populated.
You have been reading a chapter from
Web Development with Django
Published in: Feb 2021
Publisher: Packt
ISBN-13: 9781839212505
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at $19.99/month. Cancel anytime
Banner background image