Поле ForeignKey

Поле ForeignKey применяется для установления связи между двумя моделями, где одной записи родительской модели может соответствовать множество записей дочерней модели. Поле принимает обязательный аргумент - класс связанной модели, а также поддерживает дополнительные параметры, такие как on_delete, определяющий поведение при удалении связанной записи, related_name для обратной связи и другие. Связи через ForeignKey позволяют выполнять запросы как от родителя к дочерним записям, так и в обратную сторону.

Синтаксис

pythonfrom django.db import models

class ChildModel(models.Model):
  parent = models.ForeignKey(ParentModel, on_delete=models.CASCADE)

Пример

Давайте создадим две модели: Category и Article, где каждая статья принадлежит одной категории:

pythonfrom django.db import models

class Category(models.Model):
  name = models.CharField(max_length=100)
  
  def __str__(self):
    return self.name

class Article(models.Model):
  title = models.CharField(max_length=200)
  content = models.TextField()
  category = models.ForeignKey(Category, on_delete=models.CASCADE)
  
  def __str__(self):
    return self.title

Пример

Теперь создадим категорию и несколько статей, связанных с этой категорией:

pythonfrom myapp.models import Category, Article

# Create a category
category = Category.objects.create(name="Programming")

# Create articles belonging to this category
article1 = Article.objects.create(
  title="Django Basics",
  content="Introduction to Django",
  category=category
)
article2 = Article.objects.create(
  title="Advanced Django",
  content="Deep dive into Django",
  category=category
)

print(article1.category.name)
print(article2.category.name)

Результат выполнения кода:

python"Programming"
"Programming"

Пример

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

pythonfrom myapp.models import Category

category = Category.objects.get(name="Programming")
articles = category.article_set.all()

for article in articles:
  print(article.title)

Результат выполнения кода:

python"Django Basics"
"Advanced Django"

Пример

Используем параметр related_name для задания более удобного имени обратной связи:

pythonfrom django.db import models

class Category(models.Model):
  name = models.CharField(max_length=100)

class Article(models.Model):
  title = models.CharField(max_length=200)
  content = models.TextField()
  category = models.ForeignKey(
    Category,
    on_delete=models.CASCADE,
    related_name="articles"
  )

Теперь обращаться к статьям категории можно через имя articles:

pythonfrom myapp.models import Category

category = Category.objects.get(name="Programming")
articles = category.articles.all()

for article in articles:
  print(article.title)

Результат выполнения кода:

python"Django Basics"
"Advanced Django"

Пример

Рассмотрим параметр on_delete, который определяет поведение при удалении связанного объекта. Используем models.CASCADE для автоматического удаления всех дочерних записей:

pythonfrom myapp.models import Category, Article

category = Category.objects.get(name="Programming")

# Delete category - all related articles will be deleted automatically
category.delete()

# Check that articles are deleted
articles_count = Article.objects.filter(category=category).count()
print(articles_count)

Результат выполнения кода:

python"0"

Пример

Используем параметр on_delete=models.PROTECT, который запрещает удаление категории, если к ней привязаны статьи:

pythonfrom django.db import models

class Category(models.Model):
  name = models.CharField(max_length=100)

class Article(models.Model):
  title = models.CharField(max_length=200)
  content = models.TextField()
  category = models.ForeignKey(
    Category,
    on_delete=models.PROTECT
  )

При попытке удалить категорию со статьями будет вызвано исключение:

pythonfrom myapp.models import Category

category = Category.objects.get(name="Programming")

try:
  category.delete()
except Exception as e:
  print(str(e))

Результат выполнения кода:

python"Cannot delete some instances of model 'Category' because they are referenced through protected foreign key"

Смотрите также

  • поле ManyToManyField,
    которое создает связь многие-ко-многим между моделями
  • поле OneToOneField,
    которое создает связь один-к-одному между моделями
  • метод select_related,
    который оптимизирует запросы с внешними ключами
  • метод prefetch_related,
    который оптимизирует запросы с обратными связями