select_related, prefetch_related, ManytoManyField

Models.py

from django.db import models

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

    def __str__(self):
        return self.name

class Book(models.Model):
    name = models.CharField(max_length=300)
    price = models.IntegerField(default=0)
    publisher = models.ForeignKey(Publisher, on_delete=models.CASCADE)

    class Meta:
        default_related_name = 'books'

    def __str__(self):
        return self.name

class Store(models.Model):
    name = models.CharField(max_length=300)
    books = models.ManyToManyField(Book)

    class Meta:
        default_related_name = 'stores'

    def __str__(self):
        return self.name

select_related(정참조)효과

def book_list():
    
    queryset = Book.objects.all()
    
    books = []
    for book in queryset:
        books.append({'id': book.id, 'name': book.name, 'publisher': book.publisher.name})
        
    return books

Function : book_list
Number of Queries : 101
Finished in : 0.08s

def book_list_select_related():

    queryset = Book.objects.select_related('publisher').all()

    books = []

    for book in queryset:
        books.append({'id': book.id, 'name': book.name, 'publisher': book.publisher.name})

    return books

Function : book_list
Number of Queries : 101
Finished in : 0.08s

prefetch_related(역참조)효과

@query_debugger
def store_list():

    queryset = Store.objects.all()

    stores = []

    for store in queryset:
        books = [book.name for book in store.books.all()]
        stores.append({'id': store.id, 'name': store.name, 'books': books})

    return stores

Function : store_list
Number of Queries : 11
Finished in : 0.02s

@query_debugger
def store_list_prefetch_related():
  
    queryset = Store.objects.prefetch_related('books')

    stores = []

    for store in queryset:
        books = [book.name for book in store.books.all()]
        stores.append({'id': store.id, 'name': store.name, 'books': books})

    return stores

Function : store_list_expensive_books_prefetch_related
Number of Queries : 12
Finished in : 0.05s

fetch -> SQL query 문

Model.objects
  .filter(조건절)
  .select_related('정방향_참조_필드')   # 해당 필드를 join해서 가져온다.
  .prefetch_related('역방향_참조_필드') # 해당 필드는 추가쿼리로 가져온다.
select * from 'Model' m
(inner OR left outer) join '정방향_참조_필드' r on m.r_id=r.id 
'where '조건절';
select * from '역방향_참조_필드' where id in ('첫번째 쿼리 결과의 id 리스트');

Manytomanyfield through

Manytomanyfield는 자동으로 middletable을 만들어준다.
수동으로 그 model class를 정의하고 싶을때 through를 사용한다.

Book모델에 Tag가 붙은 시기를 알고싶을때

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


class Book(models.Model):
    title = models.CharField(max_length=100)
    tags = models.ManyToManyField(Tag, through='BookTag')


class BookTag(models.Model):
    book = models.ForeignKey(Book, on_delete=models.CASCADE)
    tag = models.ForeignKey(Tag, on_delete=models.CASCADE)
    created_at = models.DateTimeField(auto_now_add=True)    

    class Meta:
        db_table = '생성된 ManyToMany Table 이름'

modelsfield.through 하면 해당 필드의 manytomanyfield 중간 table객체에 접근
field는 id, from_id, to_id 등이 있다.

좋은 웹페이지 즐겨찾기