Sign In Start Free Trial
Account

Add to playlist

Create a Playlist

Modal Close icon
You need to login to use this feature.
  • Django 3 Web Development Cookbook
  • Toc
  • feedback
Django 3 Web Development Cookbook

Django 3 Web Development Cookbook

By : Aidas Bendoraitis, Jake Kronika
3.6 (9)
close
Django 3 Web Development Cookbook

Django 3 Web Development Cookbook

3.6 (9)
By: Aidas Bendoraitis, Jake Kronika

Overview of this book

Django is a web framework for perfectionists with deadlines, designed to help you build manageable medium and large web projects in a short time span. This fourth edition of the Django Web Development Cookbook is updated with Django 3's latest features to guide you effectively through the development process. This Django book starts by helping you create a virtual environment and project structure for building Python web apps. You'll learn how to build models, views, forms, and templates for your web apps and then integrate JavaScript in your Django apps to add more features. As you advance, you'll create responsive multilingual websites, ready to be shared on social networks. The book will take you through uploading and processing images, rendering data in HTML5, PDF, and Excel, using and creating APIs, and navigating different data types in Django. You'll become well-versed in security best practices and caching techniques to enhance your website's security and speed. This edition not only helps you work with the PostgreSQL database but also the MySQL database. You'll also discover advanced recipes for using Django with Docker and Ansible in development, staging, and production environments. By the end of this book, you will have become proficient in using Django's powerful features and will be equipped to create robust websites.
Table of Contents (15 chapters)
close

Changing a foreign key to the many-to-many field

This recipe is a practical example of how to change a many-to-one relation to a many-to-many relation, while preserving the already existing data. We will use both schema and data migrations in this situation.

Getting ready

Let's suppose that you have the Idea model, with a foreign key pointing to the Category model.

  1. Let's define the Category model in the categories app, as follows:
# myproject/apps/categories/models.py
from
django.db import models
from django.utils.translation import gettext_lazy as _

from myproject.apps.core.model_fields import MultilingualCharField


class Category(models.Model):
title = MultilingualCharField(
_("Title"),
max_length=200,
)

class Meta:
verbose_name = _("Category")
verbose_name_plural = _("Categories")

def __str__(self):
return self.title
  1. Let's define the Idea model in the ideas app, as follows:
# myproject/apps/ideas/models.py
from django.db import models
from django.conf import settings
from django.utils.translation import gettext_lazy as _

from myproject.apps.core.model_fields import (
MultilingualCharField,
MultilingualTextField,
)

class Idea(models.Model):
title = MultilingualCharField(
_("Title"),
max_length=200,
)
content = MultilingualTextField(
_("Content"),
)
category = models.ForeignKey(
"categories.Category",
verbose_name=_("Category"),
blank=True,
null=True,
on_delete=models.SET_NULL,
related_name="category_ideas",
)

class Meta:
verbose_name = _("Idea")
verbose_name_plural = _("Ideas")

def __str__(self):
return self.title
  1. Create and execute initial migrations by using the following commands:
(env)$ python manage.py makemigrations categories
(env)$ python manage.py makemigrations ideas
(env)$ python manage.py migrate

How to do it...

The following steps will show you how to switch from a foreign key relation to a many-to-many relation, while preserving the already existing data:

  1. Add a new many-to-many field, called categories, as follows:
# myproject/apps/ideas/models.py
from django.db import models
from django.conf import settings
from django.utils.translation import gettext_lazy as _

from myproject.apps.core.model_fields import (
MultilingualCharField,
MultilingualTextField,
)

class Idea(models.Model):
title = MultilingualCharField(
_("Title"),
max_length=200,
)
content = MultilingualTextField(
_("Content"),
)
category = models.ForeignKey(
"categories.Category",
verbose_name=_("Category"),
blank=True,
null=True,
on_delete=models.SET_NULL,
related_name="category_ideas",
)
categories = models.ManyToManyField(
"categories.Category",
verbose_name=_("Categories"),
blank=True,
related_name="ideas",
)

class Meta:
verbose_name = _("Idea")
verbose_name_plural = _("Ideas")

def __str__(self):
return self.title
  1. Create and run a schema migration, in order to add the new relationship to the database, as shown in the following code snippet:
(env)$ python manage.py makemigrations ideas
(env)$ python manage.py migrate ideas
  1. Create a data migration to copy the categories from the foreign key to the many-to-many field, as follows:
(env)$ python manage.py makemigrations --empty \
> --name=copy_categories ideas
  1. Open the newly created migration file (0003_copy_categories.py), and define the forward migration instructions, as shown in the following code snippet:
# myproject/apps/ideas/migrations/0003_copy_categories.py
from django.db import migrations


def copy_categories(apps, schema_editor):
Idea = apps.get_model("ideas", "Idea")
for idea in Idea.objects.all():
if idea.category:
idea.categories.add(idea.category)


class Migration(migrations.Migration):

dependencies = [
('ideas', '0002_idea_categories'),
]

operations = [
migrations.RunPython(copy_categories),
]
  1. Run the new data migration, as follows:
(env)$ python manage.py migrate ideas
  1. Delete the foreign key category field in the models.py file, leaving only the new categories many-to-many field, as follows:
# myproject/apps/ideas/models.py
from django.db import models
from django.conf import settings
from django.utils.translation import gettext_lazy as _

from myproject.apps.core.model_fields import (
MultilingualCharField,
MultilingualTextField,
)

class Idea(models.Model):
title = MultilingualCharField(
_("Title"),
max_length=200,
)
content = MultilingualTextField(
_("Content"),
)

categories = models.ManyToManyField(
"categories.Category",
verbose_name=_("Categories"),
blank=True,
related_name="ideas",
)

class Meta:
verbose_name = _("Idea")
verbose_name_plural = _("Ideas")

def __str__(self):
return self.title
  1. Create and run a schema migration, in order to delete the Categories field from the database table, as follows:
(env)$ python manage.py makemigrations ideas
(env)$ python manage.py migrate ideas

How it works...

At first, we add a new many-to-many field to the Idea model, and a migration is generated to update the database accordingly. Then, we create a data migration that will copy the existing relations from the foreign key category to the new many-to-many categories. Lastly, we remove the foreign key field from the model, and update the database once more.

There's more...

Our data migration currently includes only the forward action, copying the foreign
key category as the first related item in the new categories relationship. Although we did not elaborate here, in a real-world scenario it would be best to include the reverse operation as well. This could be accomplished by copying the first related item back to the category foreign key. Unfortunately, any Idea object with multiple categories would lose extra data.

See also

  • The Using migrations recipe
  • The Handling multilingual fields recipe
  • The Working with model translation tables recipe
  • The Avoiding circular dependencies recipe
bookmark search playlist download font-size

Change the font size

margin-width

Change margin width

day-mode

Change background colour

Close icon Search
Country selected

Close icon Your notes and bookmarks

Delete Bookmark

Modal Close icon
Are you sure you want to delete it?
Cancel
Yes, Delete