← Назад к вопросам

Как обратится к связанным полям через ORM Django?

1.3 Junior🔥 161 комментариев
#Django#Базы данных (SQL)

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI22 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Доступ к связанным полям в Django ORM

Django ORM позволяет работать с отношениями между моделями (ForeignKey, OneToOneField, ManyToManyField) с помощью специального синтаксиса с двойным подчеркиванием (__).

ForeignKey — один ко многим

Определение моделей:

from django.db import models

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

class Book(models.Model):
    title = models.CharField(max_length=200)
    author = models.ForeignKey(Author, on_delete=models.CASCADE)
    published_date = models.DateField()
    pages = models.IntegerField()
    
    def __str__(self):
        return self.title

Доступ от книги к автору:

# Получить книгу с её автором
book = Book.objects.get(id=1)
print(book.title)              # "Django for Beginners"
print(book.author.name)        # "Jane Doe" (доступ к связанному объекту)
print(book.author.email)       # "jane@example.com"

# Доступ через точку — это SQL запрос!
# SELECT * FROM books WHERE id=1;
# SELECT * FROM authors WHERE id=...;

Оптимизированный доступ с select_related():

# НЕОПТИМАЛЬНО
books = Book.objects.all()
for book in books:
    print(f"{book.title} by {book.author.name}")
    # Это выполнит 1 запрос для книг + N запросов для авторов!

# ОПТИМАЛЬНО
books = Book.objects.select_related('author')
for book in books:
    print(f"{book.title} by {book.author.name}")
# Выполнит всего 1 запрос (JOIN)

Фильтрация по связанным полям:

# Найти все книги автора Jane Doe
books = Book.objects.filter(author__name="Jane Doe")
# author__name — доступ к полю связанного объекта

for book in books:
    print(book.title)

# Несколько условий
books = Book.objects.filter(
    author__name__startswith="J",
    published_date__year=2023
)

# Фильтрация по id связанного объекта
book = Book.objects.filter(author_id=42).first()

Обратное отношение (Reverse ForeignKey)

Доступ от автора к его книгам:

# Получить автора
author = Author.objects.get(name="Jane Doe")

# Доступ к его книгам (автоматически создается обратное отношение)
books = author.book_set.all()  # По умолчанию: <model>_set

for book in books:
    print(book.title)

# Или с custom related_name
class Book(models.Model):
    author = models.ForeignKey(
        Author,
        on_delete=models.CASCADE,
        related_name="books"  # Вместо book_set
    )

# Теперь можно:
books = author.books.all()  # Более понятно!

OneToOneField — один к одному

Определение:

class UserProfile(models.Model):
    user = models.OneToOneField(
        User,
        on_delete=models.CASCADE,
        related_name="profile"
    )
    bio = models.TextField()
    avatar = models.ImageField()

Доступ:

# От User к Profile
user = User.objects.get(id=1)
profile = user.profile
print(profile.bio)

# От Profile к User
profile = UserProfile.objects.get(id=1)
user = profile.user
print(user.username)

# Фильтрация
users = User.objects.filter(
    profile__bio__contains="Developer"
)

ManyToManyField — многие ко многим

Определение:

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

class Course(models.Model):
    title = models.CharField(max_length=100)
    students = models.ManyToManyField(
        Student,
        related_name="courses"
    )

Доступ:

# От Course к Students
course = Course.objects.get(id=1)
students = course.students.all()

for student in students:
    print(student.name)

# От Student к Courses
student = Student.objects.get(id=1)
courses = student.courses.all()

for course in courses:
    print(course.title)

# Добавить связь
student.courses.add(course)

# Удалить связь
student.courses.remove(course)

# Проверить наличие
if course in student.courses.all():
    print("Student enrolled")

# Удалить все связи
student.courses.clear()

Фильтрация ManyToMany:

# Найти все курсы, в которых учится Alice
courses = Course.objects.filter(
    students__name="Alice"
)

# Найти всех студентов конкретного курса
students = Student.objects.filter(
    courses__title="Python Basics"
)

# Найти студентов, которые учатся на двух конкретных курсах
students = Student.objects.filter(
    courses__title="Python Basics"
).filter(
    courses__title="Web Development"
)

Цепочка отношений

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

class Author(models.Model):
    name = models.CharField(max_length=100)
    publisher = models.ForeignKey(Publisher, on_delete=models.CASCADE)

class Book(models.Model):
    title = models.CharField(max_length=200)
    author = models.ForeignKey(Author, on_delete=models.CASCADE)

# Доступ через цепочку (book → author → publisher)
books = Book.objects.filter(
    author__publisher__city="New York"
)
# Найти все книги авторов, издатели которых в Нью-Йорке

# Оптимизировать
books = Book.objects.select_related(
    'author__publisher'  # Загрузит author и publisher в один запрос
)

Практический пример: Blog

class User(models.Model):
    username = models.CharField(max_length=100)

class Post(models.Model):
    title = models.CharField(max_length=200)
    author = models.ForeignKey(
        User,
        on_delete=models.CASCADE,
        related_name="posts"
    )
    published = models.BooleanField(default=False)

class Comment(models.Model):
    text = models.TextField()
    post = models.ForeignKey(
        Post,
        on_delete=models.CASCADE,
        related_name="comments"
    )
    author = models.ForeignKey(
        User,
        on_delete=models.CASCADE,
        related_name="comments"
    )

# Получить все посты пользователя
user = User.objects.get(username="alice")
user_posts = user.posts.all()

# Получить все комментарии к определенному посту
post = Post.objects.get(id=1)
post_comments = post.comments.all()

# Найти все посты с комментариями от Bob
posts = Post.objects.filter(
    comments__author__username="bob"
).distinct()

# Найти все комментарии постов Alice
comments = Comment.objects.filter(
    post__author__username="alice"
)

# Оптимизированный запрос
posts = Post.objects.filter(
    published=True
).select_related('author').prefetch_related('comments')

for post in posts:
    print(post.title)
    print(post.author.username)  # select_related загрузил
    for comment in post.comments.all():  # prefetch_related загрузил
        print(comment.text)

Лучшие практики

1. select_related для ForeignKey и OneToOneField:

books = Book.objects.select_related('author')

2. prefetch_related для ManyToManyField и обратных отношений:

books = Book.objects.prefetch_related('author__books')

3. Используй only() для выбора нужных полей:

books = Book.objects.only('title', 'author').select_related('author')

4. Избегай запросов в циклах:

# НЕПРАВИЛЬНО
for book in Book.objects.all():
    author = book.author  # Запрос в цикле!

# ПРАВИЛЬНО
for book in Book.objects.select_related('author'):
    author = book.author  # Уже загружено

Понимание связанных полей в Django ORM критично для создания эффективных запросов и оптимизации производительности приложения.