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:
- First, import the
Publisher
class/model from reviews.models
:>>>from reviews.models import Publisher
- 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')
- 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
- 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
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:
- First, import the Contributor class as before:
>>> from reviews.models import Contributor
- 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")
- 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
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:
- First, import the
Publisher
class:>>>from reviews.models import Book, Publisher
- 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')
- 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
- 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.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
:
- 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")
- 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:
- 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")
- 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')
- 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:
- 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')
- 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.
- 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'
- 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:
- 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.
- 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()
- 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
:
- 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')
- 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>]>
- 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>]>
- 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:
- First, import the
Contributor
class:>>> from reviews.models import Contributor
- 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:
- Import the Book model:
>>> from reviews.models import Book
- Retrieve a book object with the title The Talisman, by adding the following line of code:
>>> book = Book.objects.get(title='The Talisman')
- 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
:
- Import the
Contributor
model:>>> from reviews.models import Contributor
- Fetch a
contributor
object whose first_names
is 'Rowel'
using the get()
method:>>> contributor = Contributor.objects.get(first_names='Rowel')
- 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:
- 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.
- 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
:
- 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.
- 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:
- Using the techniques we have learned so far, create a Django project called
juggler
.
- Create a Django app called
projectp
.
- Add the app projects in the
juggler/settings.py
file.
- Create two related model classes called
Project
and Task
in projectp/models.py
.
- Create migration scripts and migrate the models' definitions to the database.
- Open the Django shell now and import the models.
- 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:
- Create the following folder structure inside the project directory:
bookr/reviews/management/commands/
- 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/.
- Now let's recreate a fresh database. Delete your SQL database file present in the project folder:
rm reviews/db.sqlite3
- 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.
- Execute the custom management command
loadcsv
to populate the database:python manage.py loadcsv --csv reviews/management/commands/WebDevWithDjangoData.csv
- Using DB Browser for SQLite, verify that all the tables created by the
bookr
project are populated.