ORM을 어떻게 사용할 수 있을까?
테이블명이 약간 다를 수 있지만, 아래와 같이 ORM은 SQL 구문을 직접 사용하지 않고 DB에 쿼리 요청을 할 수 있는 추상화된 도구라고 할 수 있다.
User.objects.get(name="Dongyeon")
Python
복사
SELECT * FROM myapp_user WHERE name="Dongyeon";
SQL
복사
그렇다면 위와 같은 코드는 어떻게 동작하는 걸까? ORM을 사용한 코드를 다시 확인해보자
User.objects.get(name="Dongyeon")
Python
복사
Django ORM을 조금이라도 써보면 알겠지만, 우리는 get을 통해 SELECT 작업을 요청한다는 것으로 추측할 수 있다.
하지만, get 은 단독으로 선언된 메서드가 아닌 User.objects 에 존재하는 메서드임을 짐작할 수 있다.
한단계 더 나아가면, User는 get 을 제공하는 objects 를 제공한다고 생각할 수 있다.
그렇다면 User Model이 제공하는 objects 는 어떤 것일까?
이를 알기 위해서는 User 즉, model.Model을 통해 Model이 어떻게 만들어지는지 과정을 간단하게 확인할 필요가 있다.
Deep-Dive Model for objects
class User(models.Model):
name = models.CharField(max_length=100)
email = models.EmailField()
age = models.PositiveIntegerField()
Python
복사
각자 내용이 다르겠지만, 중요한점은 models.Model 을 사용한다는 점이다.
결국엔 objects 라는 것은 우리가 모델 선언 시 직접 설정하는게 아니다.
그럼 models.Model 의 코드에서 objects 에 대한 내용을 찾아보자.
결과적으로는 models.Model 에 대한 코드에서는 __init__ 등을 포함한 모든 메서드에서 objects 속성을 설정하는 부분은 없었다.
그때 이 내용이 눈에 보였다. Model은 ModelBase 라는 메타클래스를 가지고 있었다. AltersData 내용도 확인해보았지만, objects 와는 큰 관련은 없었다.
class Model(AltersData, metaclass=ModelBase):
...
Python
복사
ModelBase?
ModelBase 를 보며 처음 느낀건 엄청 많은 동작을 한다는 것이었다.
메타클래스라는 개념은 클래스를 만들기 위한 클래스라고 생각하면 된다.
즉, User Model도 ModelBase 를 메타클래스로 사용할 것이다. 한번 내용을 보자.
워낙 내용이 길어서, 중요 부분만 가져왔다. 직접 내용을 보면 좋을 듯 하다.
class ModelBase(type):
"""Metaclass for all models."""
def __new__(cls, name, bases, attrs, **kwargs):
...
super_new = super().__new__
...
new_class = super_new(cls, name, bases, new_attrs, **kwargs)
...
new_class.add_to_class("_meta", Options(meta, app_label))
...
abstract = getattr(attr_meta, "abstract", False)
meta = attr_meta or getattr(new_class, "Meta", None)
base_meta = getattr(new_class, "_meta", None)
...
new_class._prepare()
new_class._meta.apps.register_model(new_class._meta.app_label, new_class)
return new_class
def add_to_class(cls, name, value):
if _has_contribute_to_class(value):
value.contribute_to_class(cls, name)
else:
setattr(cls, name, value)
def _prepare(cls):
"""Create some methods once self._meta has been populated."""
opts = cls._meta
opts._prepare(cls)
...
if not opts.managers:
if any(f.name == "objects" for f in opts.fields):
raise ValueError(
"Model %s must specify a custom Manager, because it has a "
"field named 'objects'." % cls.__name__
)
manager = Manager()
manager.auto_created = True
cls.add_to_class("objects", manager)
...
Python
복사
ModelBase의 기능들
다른 메서드나 프로퍼티들도 존재하지만, 현재 objects 와 가장 관련 있어보이는 메서드를 작성해보았다.
•
__new__ : Model 생성 전, Model의 메타 데이터 등 입력 옵션을 설정하는 영역
◦
class Meta 로 설정한 옵션들이 이곳에서 설정된다.
▪
DoesNotExist / MultipleObjectsReturned 등 Model의 기본 Exception이 설정된다.
▪
해당 모델이 사용할 Manager를 설정한다.
▪
_meta 라는 속성이 설정된다. 이 속성은 모델의 핵심 메타 데이터가 저장된 데이터이다.
•
add_to_class : 설정될 속성의 class에 contribute_to_class 를 사용하여 Model 속성을 설정하거나, setattr을 사용한다.
•
_prepare : _meta 의 설정 작업이 끝난 후, Model 등록 전 _meta 를 사용하여 추가 작업을 진행한다.
ModelBase 에서 진행되는 일은 너무 많지만, 우리가 알고 싶은 것은 objects 를 어떻게 설정되는지 알고 싶은 것이다.
위 코드를 한번 봤다면 알 수 있겠지만, _prepare 에서 해당 작업을 진행한다.
def _prepare(cls):
"""Create some methods once self._meta has been populated."""
opts = cls._meta
opts._prepare(cls)
...
if not opts.managers:
if any(f.name == "objects" for f in opts.fields):
raise ValueError(
"Model %s must specify a custom Manager, because it has a "
"field named 'objects'." % cls.__name__
)
manager = Manager()
manager.auto_created = True
cls.add_to_class("objects", manager)
...
Python
복사
위 코드를 통해, 우리가 굳이 관련 설정을 하지 않더라도 objects 를 Manager 라는 클래스의 인스턴스로 설정하는 것을 알 수 있다.
Model, BaseModel의 Deep-Dive를 통해 User.objects 까지 어떤 과정으로 설정되는지 알 수 있었고, objects 는 기본적으로 Manager 클래스의 기능을 사용하는 것이라는 것을 알 수 있었다.
참고로, Manager는 Model별로 커스텀한 Manager를 사용하거나 기본 Manager를 변경하거나 목적에 따라 여러 Manager를 사용할 수도 있다.
화해 기술블로그에 관련한 비슷한 아티클이 있어 공유한다.
화해 블로그 | 기술 블로그DRY를 위해 Django Manager를 적용해봅시다. – 화해 블로그 | 기술 블로그