Отношение один ко многим (One to Many)

Последнее обновление: 21.08.2022

Рассмотрим организацию связи один ко многим, при которой одна главная сущность может быть связаны с несколькими зависимыми сущностями. Например, одна компания может выпускать несколько товаров:

from django.db import models

class Company(models.Model):
    name = models.CharField(max_length=30)

class Product(models.Model):
    company = models.ForeignKey(Company, on_delete = models.CASCADE)
    name = models.CharField(max_length=30)
    price = models.IntegerField()

В данном случае модель Company представляет производителя и является главной моделью, а модель Product представляет товар компании и является зависимой моделью.

Конструктор типа models.ForeignKey настраивает связь с главной сущностью. Первый параметр указывает, с какой моделью будет создаваться связь - в данном случае это модель Company. Второй параметр - on_delete задает опцию удаления объекта текущей модели при удалении связанного объекта главной модели. Всего для параметра on_delete мы можем использовать следующие значения:

  • models.CASCADE: автоматически удаляет строку из зависимой таблицы, если удаляется связанная строка из главной таблицы

  • models.PROTECT: блокирует удаление строки из главной таблицы, если с ней связаны какие-либо строки из зависимой таблицы

  • models.SET_NULL: устанавливает NULL при удалении связанной строка из главной таблицы

  • models.SET_DEFAULT: устанавливает значение по умолчанию для внешнего ключа в зависимой таблице. В этом случае для этого столбца должно быть задано значение по умолчанию

  • models.DO_NOTHING: при удалении связанной строки из главной таблицы не производится никаких действий в зависимой таблице

И в результате миграции в базе данных SQLite будут создаваться следующие таблицы:

CREATE TABLE "hello_company" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "name" varchar(30) NOT NULL)
CREATE TABLE "hello_product" (
	"id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, 
	"name" varchar(30) NOT NULL, 
	"price" integer NOT NULL, 
	"company_id" bigint NOT NULL REFERENCES "hello_company" ("id") DEFERRABLE INITIALLY DEFERRED
)

Операции с моделями

Из определения таблиц мы видим, что модель Product связана с таблицей Company через столбец "company_id". Однако в самом определении модели Product есть поле company, через которое можно получить связанную сущность:

# получение id связанной с товаром компании
Product.objects.get(id=1).company.id

# получение названия связанной с товаром компании
Product.objects.get(id=1).company.name

# получение товаров, которые принадлежат к компании "Apple"
Product.objects.filter(company__name="Apple")

С помощью выражения модель__свойство (два подчеркивания) можно использовать свойство главной модели для фильтрации по объектам зависимой модели.

Хотя с точки зрения модели Company она не имеет никаких свойств, которые связывали бы ее с моделью Product. Но с помощью синтаксиса

"главная_модель"."зависимая_модель"_set

можно изменить направление связи. Например:

from .models import Company, Product

apple = Company.objects.get(name="Apple")

# получение всех товаров
apple.product_set.all()

# получение количества товаров
apple.product_set.count()

# получение товаров, название которых начинается на "iPhone"
apple.product_set.filter(name__startwith="iPhone")

Причем с помощью выражения _set можно выполнять операции добавления, изменения, удаления объектов зависимой модели из главной модели.

# создаем объект Company
apple = Company.objects.create(name="Apple")


# создание товара определенной компании
apple.product_set.create(name="iPhone 8", price=67890)

# отдельное создание объекта с последующим добавлением
ipad = Product(name="iPad", price=34560)
# при добавлении необходимо указать параметр bulk =False
apple.product_set.add(ipad, bulk =False)

# исключает из компании все товары, 
# при этом товары остаются в бд, просто им не назначена компания
# работает, если в зависимой модели ForeignKey(Company, null = True)
# apple.product_set.clear()

# то же самое, только в отношении одного объекта
# ipad = Product.objects.get(name="iPad")
# apple.product_set.remove(ipad)

Стоит отметить три метода:

  • add(): добавляет связь между объектом зависимой модели и объектом главной модели. В своей сути этот метод фактически вызывает для модели метод update() для добавления связи. Однако это требует, чтобы обе модели уже были в базе данных. И чтобы обойти это ограничение, применяется параметр bulk=False, для того, чтобы объект зависимой модели сразу был добавлен и для него была установлена связь.

  • clear(): удаляет связь между всеми объектами зависимой модели и объектом главной модели. При этом сами объекты зависимой модели остаются в базе данных, и для их внешнего ключа устанавливается значение NULL. Поэтому данный метод будет работать, если в самой зависимой модели при установки связи использовался параметр null=True: ForeignKey(Company, null = True).

  • remove(): также, как и clear() удаляет связь, только между одним объектом зависимой модели и объектом главной модели. При этом также все объекты остаются в бд. И также в самой зависимой модели при установки связи должен использоваться параметр null=True

Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850