Рассмотрим организацию связи один ко многим, при которой одна главная сущность может быть связаны с несколькими зависимыми сущностями. Например, одна компания может выпускать несколько товаров:
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