Search icon CANCEL
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Conferences
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

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

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