Django Docs | Customizing model

Jihun Kim·2022년 3월 14일
1

Django Docs

목록 보기
3/9
post-thumbnail

Customizing model

Overriding: Save

  • 만약 save 할 때마다 다른 어떤 일이 일어나도록 하고 싶은 경우 아래와 같이 overriding을 할 수 있다.
from django.db import models

class Blog(models.Model):
    name = models.CharField(max_length=100)
    tagline = models.TextField()

    def save(self, *args, **kwargs):
        do_something()
        super().save(*args, **kwargs)  # Call the "real" save() method.
        do_something_else()
  • 아래와 같이 saving이 일어날 상황을 분기하는 것도 가능하다.
from django.db import models

class Blog(models.Model):
    name = models.CharField(max_length=100)
    tagline = models.TextField()

    def save(self, *args, **kwargs):
        if self.name == "Yoko Ono's blog":
            return # Yoko shall never have her own blog!
        else:
            super().save(*args, **kwargs)  # Call the "real" save() method.

반드시 superclass method를 호출해야 데이터베이스에 저장 된다.


Classmethod

  • 아래와 같이 classmethod 데코레이터를 이용한다.
from django.db import models

class Book(models.Model):
    title = models.CharField(max_length=100)

    @classmethod
    def create(cls, title):
        book = cls(title=title)
        # do something with the book
        return book

book = Book.create("Pride and Prejudice")

Method on a custom manager

  • 장고 독스에 따르면 이 방법이 classmethod를 이용하는 위의 방법보다 더 선호되는 방법이라고 한다.
  • 아래와 같이 custom manager에 method를 정의한 후 모델에서 objects를 정의한 다음 해당 method를 사용하면 된다.
class BookManager(models.Manager):
    def create_book(self, title):
        book = self.create(title=title)
        # do something with the book
        return book

class Book(models.Model):
    title = models.CharField(max_length=100)

    objects = BookManager()

book = Book.objects.create_book("Pride and Prejudice")


Abstract base classes 상속

related_name 또는 related_query_name을 ForiegnKey 또는 ManyToManyField로 사용하고 있다면 유니크한 reverse name과 query name을 각 필드별로 지정해 주어야 한다.

그렇게 하지 않으면 abstract base classes에서는 class의 해당 field가 각 child class에서 해당 속성에 대해 같은 값을 갖게 되기 때문에 문제가 발생할 수 있다.

따라서, 해당 문제를 해결하기 위해 related_name 또는 related_query_name을 설정할 때 * '%(app_label)s'와 '%(class)s'가 포함되도록 해야 한다.

  • '%(app_label)s': child class가 속한 app의 lowercased명으로, application name은 unique 해야 하며 model class명 역시 각 앱 내에서 unique 해야 한다.
  • '%(class)s': 해당 필드가 사용되는 child class의 lowercased명

아래 예시와 같이 사용하면 된다.

common/models.py

from django.db import models

class Base(models.Model):
    m2m = models.ManyToManyField(
        OtherModel,
        related_name="%(app_label)s_%(class)s_related",
        related_query_name="%(app_label)s_%(class)ss",
    )

    class Meta:
        abstract = True

class ChildA(Base):
    pass

class ChildB(Base):
    pass

rare/models.py

from common.models import Base

class ChildB(Base):
    pass
  • common.ChildA.m2m 필드의 reverse name은 common_childa_related
  • common.ChildB.m2m 필드의 reverse name은 common_childb_related
  • rare.ChildB.m2m 필드의 reverse name은 rare_childb_related

만약 이런 식으로 related_name을 설정해 놓지 않으면 장고가 시스템 체크 또는 migrate를 할 때 에러가 발생하게 된다.

abstract base class에서 related_name을 지정하지 않으면 default reverse name은 child class 명에 _set을 붙인 것이 된다.
예를 들어, ChildA일 경우 m2m field의 reverse_name은 childa_set이 되고 마찬가지로 ChildB일 경우 m2m field의 reverse_name은 childb_set이 된다.



save 할 때 django에서 일어나는 일

1. pre-save signal 보내기

  • pre_save signal이 보내지면 해당 signal을 받는 함수들은 특정 일을 수행하게 된다.

2. 데이터 전처리

  • 만약 데이터 전처리가 필요한 경우 각 필드의 pre_save() 메소드가 호출 된다.
    - 예를 들어, date/time 필드는 pre_save() 메소드를 오버라이딩 해서 auto_now_addauto_now를 구현 한다.

3. 데이터베이스에 필요한 데이터 준비

  • 각 필드의 get_db_prep_save() 메소드가 호출되며 데이터베이스에 쓰여질 수 있는 타입의 데이터가 준비 된다.
    - DateField의 경우 파이선 datetime 객체를 사용하는데 데이터베이스는 datetime 객체를 저장하지 않는다. 따라서, ISO-compliant date string으로 변환해야 한다.

4. DB에 데이터 넣기

  • 전처리된 데이터는 DB에 넣기 위해 SQL문으로 전처리 된다.

5. post-save signal 보내기

  • post_save 시그널을 보내면 해당 시그널을 받는 함수가 실행 된다.
profile
쿄쿄

0개의 댓글