Django ORM 설명: selected_related 및 prefetch_related

8376 단어 databasequeriesdjango
Django ORM 작업은 SQL 문을 작성하지 않고 데이터베이스에서 데이터를 쿼리하는 방법을 제공합니다. 그러나 SQL 문 대신 ORM을 사용하면 확실히 성능이 떨어지지만 대부분의 CRUD 응용 프로그램의 경우 쓸모 없는 데이터베이스 쿼리를 줄이고 Django 응용 프로그램을 훨씬 더 빠르게 만드는 방법을 찾기 전까지는 중요하지 않습니다.

성능 문제의 예



이 모델의 예를 들어보겠습니다.

from django.db import models


class User(models.Model):
    name = models.CharField(max_length=50)
    email = models.EmailField(max_length=254)


class Profile(models.Model):
    user = models.ForeignKey(User, on_delete=models.CASCADE)
    first_name = models.CharField(max_length=50)


엄청난! 여기에는 두 가지 모델이 있으며 Profile 모델은 ForeignKey 모델과 User -- 일대다 -- 관계가 있습니다. 즉, 사용자가 많은 프로필을 가질 수 있습니다.

프로필 개체가 있는 경우 사용자를 얻는 기본 방법은 무엇입니까?

# hit the database
profile = Profile.objects.first()

# hit the database again to get the related User object
user = profile.user


글쎄, 우리는 여기서 두 가지 쿼리를 만들었습니다. 그다지 큰 문제는 아니지만 이러한 라인을 최적화하고 불필요한 쿼리를 피하고자 합니다.

관련 항목 선택



Django docs , select_related()에서 쿼리를 실행할 때 추가 관련 개체 데이터를 선택하여 외래 키 관계를 "따르는"QuerySet을 반환합니다. 이것은 하나의 더 복잡한 쿼리를 생성하는 성능 부스터이지만 나중에 외래 키 관계를 사용할 때 데이터베이스 쿼리가 필요하지 않음을 의미합니다.

기본적으로 선택하려는 개체가 단일 개체일 때 select_related를 사용하므로 OneToOneField 또는 ForeignKey 입니다.
user 개체가 있는 profile를 가져오는 줄을 다시 작성해 보겠습니다.

# Hits the database
profile = Profile.objects.select_related('user').first()

# Doesn't hit the database, because profile.user has been prepopulated
# in the previous query.
user = profile.user


그리고 방금 두 개의 쿼리를 하나로 변환했습니다. 그러나 ManyToMany 관계가 있거나 ManyToOne만 있으면 어떻게 됩니까?
사용자 개체가 있는 모든 프로필을 가져와야 하는 경우를 살펴보겠습니다.

프리페치 관련



User 모델에 str 메서드를 추가해 봅시다.

class User(models.Model):
    name = models.CharField(max_length=50)
    email = models.EmailField(max_length=254)
    password = models.CharField(max_length=50)

    def __str__(self) -> str:
        return "%s (%s)" % (self.name,
            ", ".join(profile.first_name for profile in self.profile_set.all()))


Note: If you are using a ForeignKey relationship in Django between an M1 model and an M2 model -- which is symmetrical by default --, the M1 model has automatically a reverse relationship field of type ManyToOne.




다음 코드를 실행하면 다음과 같은 결과가 나타납니다.

>>> User.objects.all()
[<User: User (John Doe)>, <User: User (Jane Doe)> ...]


이 쿼리의 문제점은 User 모델의 __str__ 메서드가 호출될 때마다 데이터베이스에서 self.profile_set.all() 를 쿼리해야 한다는 것입니다.
그리고 쿼리 수는 사용자의 프로필 수에 비례합니다. 음, 악몽입니다.🥶

하지만 어떻게 이것을 단 두 개의 쿼리로 줄일 수 있을까요? 음, prefetch_related를 사용합니다.

User.objects.prefetch_related('profile_set').all()

prefetch_related 사물의 "세트"를 얻을 때 사용하므로 ManyToMany 또는 반대 방향으로 ForeignKey -- ManyToOne .

그렇다면 둘의 차이점은 무엇입니까?



둘 다 데이터베이스에서 데이터를 미리 가져오는 동일한 원칙에 따라 작동하지만 서로 다른 접근 방식을 사용합니다.

아래에 이미 언급했지만 이 내용에 대한 결론을 내리자.
  • 선택하려는 개체가 단일 개체일 때 select_related를 사용하므로 OneToOneField 또는 ForeignKey 입니다.
  • prefetch_related 는 사물의 "세트"를 가져올 때 사용하므로 ManyToMany 또는 반대 방향으로 ForeignKey -- ManyToOne .

  • 그러나 데이터베이스 쿼리 수를 줄이고 데이터베이스에서 더 큰 크기의 데이터를 가져오는 데 문제가 없는 경우 이러한 방법을 사용해야 합니다.
    내 경험상 이러한 방법은 Django에서 배고픈 크론 작업을 실행하는 데 정말 도움이 되었습니다.🤟‍

    Django 애플리케이션에서 더 적은 수의 데이터베이스 쿼리를 만들기 위해 사용하는 다른 방법은 무엇입니까? 의견 섹션에서 논의합시다.

    bloggu.io을(를) 사용하여 게시된 기사. 무료로 사용해 보세요.

    좋은 웹페이지 즐겨찾기