Reader small image

You're reading from  Web Development with Django

Product typeBook
Published inFeb 2021
Reading LevelIntermediate
PublisherPackt
ISBN-139781839212505
Edition1st Edition
Languages
Tools
Right arrow
Authors (5):
Ben Shaw
Ben Shaw
author image
Ben Shaw

Ben Shaw is a software engineer based in Auckland, New Zealand. He has worked as a developer for over 14 years and has been building websites with Django since 2007. In that time, his experience has helped many different types of companies, ranging in size from start-ups to large enterprises. He is also interested in machine learning, data science, automating deployments, and DevOps. When not programming, Ben enjoys outdoor sports and spending time with his partner and son.
Read more about Ben Shaw

Saurabh Badhwar
Saurabh Badhwar
author image
Saurabh Badhwar

Saurabh Badhwar is an infrastructure engineer who works on building tools and frameworks that enhance developer productivity. A major part of his work involves using Python to develop services that scale to thousands of concurrent users. He is currently employed at LinkedIn and works on infrastructure performance tools and services.
Read more about Saurabh Badhwar

Andrew Bird
Andrew Bird
author image
Andrew Bird

Andrew Bird is the data and analytics manager of Vesparum Capital. He leads the software and data science teams at Vesparum, overseeing full-stack web development in Django/React. He is an Australian actuary (FIAA, CERA) who has previously worked with Deloitte Consulting in financial services. Andrew also currently works as a full-stack developer for Draftable Pvt. Ltd. He manages the ongoing development of the donation portal for the Effective Altruism Australia website on a voluntary basis. Andrew has also co-written one of our bestselling titles, "The Python Workshop".
Read more about Andrew Bird

Bharath Chandra K S
Bharath Chandra K S
author image
Bharath Chandra K S

Bharath Chandra K S lives in Sydney, Australia, and has over 14 years of software industry experience. He is very passionate about software development on the Python stack, including frameworks such as Flask and Django. He has experience working with both monolithic and microservice architectures and has built various public-facing applications and data processing backend systems. When not cooking up software applications, he likes to cook some nice food.
Read more about Bharath Chandra K S

Chris Guest
Chris Guest
author image
Chris Guest

Chris Guest is based in Melbourne and started programming in Python 24 years ago, when it was an obscure academic language. He has since used his Python knowledge in the publishing, hospitality, medical, academic and financial sectors. Throughout his career, he has worked with many Python web development frameworks, including Zope, TurboGears, web2py, and Flask, although he still prefers Django.
Read more about Chris Guest

View More author details
Right arrow

Many to Many

In this relationship, multiple records in a table can have a relationship with multiple records in a different table. For example, a book can have multiple co-authors and each author (contributor) could have written multiple books. So, this forms a many-to-many relationship between the Book and Contributor tables:

Figure 2.15: Many-to-many relationship between books and co-authors

Figure 2.15: Many-to-many relationship between books and co-authors

In models.py, for the Book model, add the last line as shown here:

class Book(models.Model):
    """A published book."""
    title = models.CharField\
            (max_length=70, \
             help_text="The title of the book.")
    publication_date = models.DateField\
                       (verbose_name=\
                        "Date the book was published.")
    isbn = models.CharField\
           (max_length=20, \
            verbose_name="ISBN number of the book.")
    publisher = models.ForeignKey\
                (Publisher, on_delete=models.CASCADE)
    contributors = models.ManyToManyField\
                   ('Contributor', through="BookContributor")

The newly added contributors field establishes a many-to-many relationship with Book and Contributor using the ManyToManyField field type:

  • models.ManyToManyField: This is the field type to establish a many-to-many relationship.
  • through: This is a special field option for many-to-many relationships. When we have a many-to-many relationship across two tables, if we want to store some extra information about the relationship, then we can use this to establish the relationship via an intermediary table.

For example, we have two tables, namely Book and Contributor, where we need to store the information on the type of contributor for the book, such as Author, Co-author, or Editor. Then the type of contributor is stored in an intermediary table called BookContributor. Here is how the BookContributor table/model looks. Make sure you include this model in reviews/models.py:

class BookContributor(models.Model):
    class ContributionRole(models.TextChoices):
        AUTHOR = "AUTHOR", "Author"
        CO_AUTHOR = "CO_AUTHOR", "Co-Author"
        EDITOR = "EDITOR", "Editor"
    book = models.ForeignKey\
           (Book, on_delete=models.CASCADE)
    contributor = models.ForeignKey\
                  (Contributor, \
                   on_delete=models.CASCADE)
    role = models.CharField\
           (verbose_name=\
            "The role this contributor had in the book.", \
            choices=ContributionRole.choices, max_length=20)

Note

The complete models.py file can be viewed at this link: http://packt.live/3hmFQxn.

An intermediary table such as BookContributor establishes relationships by using foreign keys to both the Book and Contributor tables. It can also have extra fields that can store information about the relationship the BookContributor model has with the following fields:

  • book: This is a foreign key to the Book model. As we saw previously, on_delete=models.CASCADE will delete an entry from the relationship table when the relevant book is deleted from the application.
  • Contributor: This is again a foreign key to the Contributor model/table. This is also defined as CASCADE upon deletion.
  • role: This is the field of the intermediary model, which stores the extra information about the relationship between Book and Contributor.
  • class ContributionRole(models.TextChoices): This can be used to define a set of choices by creating a subclass of models.TextChoices. For example, ContributionRole is a subclass created out of TextChoices, which is used by the roles field to define Author, Co-Author, and Editor as a set of choices.
  • choices: This refers to a set of choices defined in the models, and they are useful when creating Django Forms using the models.

    Note

    When the through field option is not provided while establishing a many-to-many relationship, Django automatically creates an intermediary table to manage the relationship.

One-to-One Relationships

In this relationship, one record in a table will have a reference to only one record in a different table. For example, a person can have only one driver's license, so a person to their driver's license could form a one-to-one relationship:

Figure 2.16: Example of a one-to-one relationship

Figure 2.16: Example of a one-to-one relationship

The OneToOneField can be used to establish a one-to-one relationship, as shown here:

class DriverLicence(models.Model):
    person = models.OneToOneField\
             (Person, on_delete=models.CASCADE)
    licence_number = models.CharField(max_length=50)

Now that we have explored database relationships, let's come back to our bookr application and add one more model there.

Adding the Review Model

We've already added the Book and Publisher models to the reviews/models.py file. The last model that we are going to add is the Review model. The following code snippet should help us do this:

from django.contrib import auth
class Review(models.Model):
    content = models.TextField\
              (help_text="The Review text.")
    rating = models.IntegerField\
             (help_text="The rating the reviewer has given.")
    date_created = models.DateTimeField\
                   (auto_now_add=True, \
                    help_text=\
                    "The date and time the review was created.")
    date_edited = models.DateTimeField\
                  (null=True, \
                   help_text=\
                   "The date and time the review was last edited.")
    creator = models.ForeignKey\
              (auth.get_user_model(), on_delete=models.CASCADE)
    book = models.ForeignKey\
           (Book, on_delete=models.CASCADE, \
            help_text="The Book that this review is for.")

Note

The complete models.py file can be viewed at this link: http://packt.live/3hmFQxn.

The review model/table will be used to store user-provided review comments and ratings for books. It has the following fields:

  • content: This field stores the text for a book review, hence the field type used is TextField as this can store a large amount of text.
  • rating: This field stores the review rating of a book. Since the rating is going to be an integer, the field type used is IntegerField.
  • date_created: This field stores the time and date when the review was written, hence the field type is DateTimeField.
  • date_edited: This field stores the date and time whenever a review is edited. The field type is again DateTimeField.
  • Creator: This field specifies the review creator or the person who writes the book review. Notice that this is a foreign key to auth.get_user_model(), which is referring to the User model from Django's built-in authentication module. It has a field option on_delete=models.CASCADE. This explains that when a user is deleted from the database, all the reviews written by that user will be deleted.
  • Book: Reviews have a field called book, which is a foreign key to the Book model. This is because for a book review application, reviews have to be written, and a book can have many reviews, so this is a many-to-one relationship. This is also defined with a field option, on_delete=models.CASCADE, because once the book is deleted, there is no point in retaining the reviews in the application. So, when a book is deleted, all the reviews referring to the book will also get deleted.

Model Methods

In Django, we can write methods inside a model class. These are called model methods and they can be custom methods or special methods that override the default methods of Django models. One such method is __str__(). This method returns the string representation of the Model instances and can be especially useful while using the Django shell. In the following example, where the __str__() method is added to the Publisher model, the string representation of the Publisher object will be the publisher's name:

class Publisher(models.Model):
    """A company that publishes books."""
    name = models.CharField\
           (max_length=50, \
            help_text="The name of the Publisher.")
    website = models.URLField\
              (help_text="The Publisher's website.")
    email = models.EmailField\
            (help_text="The Publisher's email address.")
    def __str__(self):
        return self.name

Add the _str_() methods to Contributor and Book as well, as follows:

class Book(models.Model):
    """A published book."""
    title = models.CharField\
            (max_length=70, \
             help_text="The title of the book.")
    publication_date = models.DateField\
                       (verbose_name=\
                        "Date the book was published.")
    isbn = models.CharField\
           (max_length=20, \
            verbose_name="ISBN number of the book.")
    publisher = models.ForeignKey\
                (Publisher, \
                 on_delete=models.CASCADE)
    contributors = models.ManyToManyField\
                   ('Contributor', through="BookContributor")
    def __str__(self):
        return self.title
class Contributor(models.Model):
"""
A contributor to a Book, e.g. author, editor, \
co-author.
"""
    first_names = models.CharField\
                  (max_length=50, \
                   help_text=\
                   "The contributor's first name or names.")
    last_names = models.CharField\
                 (max_length=50, \
                  help_text=\
                  "The contributor's last name or names.")
    email = models.EmailField\
            (help_text=\
             "The contact email for the contributor.")
    def __str__(self):
        return self.first_names

Migrating the Reviews App

Since we have the entire model file ready, let's now migrate the models into the database, similar to what we did before with the installed apps. Since the reviews app has a set of models created by us, before running the migration, it is important to create the migration scripts. Migration scripts help in identifying any changes to the models and will propagate these changes into the database while running the migration. Execute the following command to create the migration scripts:

python manage.py makemigrations reviews

You should get an output similar to this:

  reviews/migrations/0002_auto_20191007_0112.py
    - Create model Book
    - Create model Contributor
    - Create model Review
    - Create model BookContributor
    - Add field contributors to book
    - Add field publisher to book

Migration scripts will be created in a folder named migrations in the application folder. Next, migrate all the models into the database using the migrate command:

python manage.py migrate reviews

You should see the following output:

Operations to perform:
  Apply all migrations: reviews
Running migrations:
  Applying reviews.0002_auto_20191007_0112... OK

After executing this command, we have successfully created the database tables defined in the reviews app. You may use DB Browser for SQLite to explore the tables you have just created after the migration. To do so, open DB Browser for SQLite, click the Open Database button (Figure 2.17), and navigate to your project directory:

Figure 2.17: Click the Open Database button

Figure 2.17: Click the Open Database button

Select the database file named db.sqlite3 to open it (Figure 2.18).

Figure 2.18: Locating db.sqlite3 in the bookr directory

Figure 2.18: Locating db.sqlite3 in the bookr directory

You should now be able to browse the new sets of tables created. The following figure shows the database tables defined in the reviews app:

Figure 2.19: Database tables as defined in the reviews app

Figure 2.19: Database tables as defined in the reviews app

Previous PageNext Page
You have been reading a chapter from
Web Development with Django
Published in: Feb 2021Publisher: PacktISBN-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.
undefined
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 €14.99/month. Cancel anytime

Authors (5)

author image
Ben Shaw

Ben Shaw is a software engineer based in Auckland, New Zealand. He has worked as a developer for over 14 years and has been building websites with Django since 2007. In that time, his experience has helped many different types of companies, ranging in size from start-ups to large enterprises. He is also interested in machine learning, data science, automating deployments, and DevOps. When not programming, Ben enjoys outdoor sports and spending time with his partner and son.
Read more about Ben Shaw

author image
Saurabh Badhwar

Saurabh Badhwar is an infrastructure engineer who works on building tools and frameworks that enhance developer productivity. A major part of his work involves using Python to develop services that scale to thousands of concurrent users. He is currently employed at LinkedIn and works on infrastructure performance tools and services.
Read more about Saurabh Badhwar

author image
Andrew Bird

Andrew Bird is the data and analytics manager of Vesparum Capital. He leads the software and data science teams at Vesparum, overseeing full-stack web development in Django/React. He is an Australian actuary (FIAA, CERA) who has previously worked with Deloitte Consulting in financial services. Andrew also currently works as a full-stack developer for Draftable Pvt. Ltd. He manages the ongoing development of the donation portal for the Effective Altruism Australia website on a voluntary basis. Andrew has also co-written one of our bestselling titles, "The Python Workshop".
Read more about Andrew Bird

author image
Bharath Chandra K S

Bharath Chandra K S lives in Sydney, Australia, and has over 14 years of software industry experience. He is very passionate about software development on the Python stack, including frameworks such as Flask and Django. He has experience working with both monolithic and microservice architectures and has built various public-facing applications and data processing backend systems. When not cooking up software applications, he likes to cook some nice food.
Read more about Bharath Chandra K S

author image
Chris Guest

Chris Guest is based in Melbourne and started programming in Python 24 years ago, when it was an obscure academic language. He has since used his Python knowledge in the publishing, hospitality, medical, academic and financial sectors. Throughout his career, he has worked with many Python web development frameworks, including Zope, TurboGears, web2py, and Flask, although he still prefers Django.
Read more about Chris Guest