Django, Query and Database layer

Django는 ORM을 채택하고 있으며, 다른 ORM과 같이 여러 종류의 Data를 DB 종류와는 독립적인 형태로 객체화 한다. 그리고 생성된 객체에는 Method Set을 제공한다.

Django ORM은 대부분의 경우 사용자가 필요한 DB의 작업을 잘 수행하지만 때때로 예상과는 다른 결과를 보여주기도 한다.



  • get_object_or_404()

from django.shortcuts import get_object_or_404
단일 객체를 가져와서 작업을 하는 detail view에서는 objects.get() 대신 get_object_or_404()를 사용하자.

예를들어

views.py
from django.shortcuts import get_object_or_404

from .models import Post


def post_detail(request, pk):
post = Post.objects.get(pk=pk)
return render(request,"blog/post_detail.html",{“post":post,})


이처럼 detail view를 만들었다고 생각해보자. 만약 사용자가 정당한 pk값을 넣지 않을 경우 서버는 http 500 error를 반환하게 된다. 하지만 이는 사실 서버의 error로 찍히기는 것보다 http 404 error를 찍는 것이 정당하다. 엄밀히 말하면 서버의 오류가 아닌 사용자가 잘못된 url로 자원을 찾았기 때문이다. 그러므로 아래의 코드가 더 적합하다.

views.py
from django.shortcuts import render
from django.http import Http404

from .models import Post

def post_detail(request, pk):
try:
post = Post.objects.get(pk=pk)
except Post.DoseNotExist:
raise Http404
return render(request,"blog/post_detail.html",{})


이처럼 URL에 붙어온 pk값에 알맞는 Post가 없을 경우에 대비하여 try-except 블록을 사용하여 예외처리를 해야한다. 하지만 get_object_or_404()를 이용한다면?

views.py
from django.shortcuts import render, get_object_or_404

from .models import Post


def post_detail(request, pk):
post = get_object_or_404(Post,pk=pk)
return render(request,"blog/post_detail.html",{“post":post,})


이렇게 깔끔하게 view를 가져갈 수 있다.

하지만 get_object_or_404() 메서드를 사용하는 데 있어 주의할 점이 있다.

1. view에서만 사용할 것
2. helper function, form, model method, view를 제외한 다른 곳
그리고 뷰와 직접적으로 관련된 곳이 아닌 곳에서는 이용하지말자

예를들어 admin페이지에서 특정 데이터를 지우면 404 error 가 발생하면서 모든 사이트가 망가져버리는 결과를 얻는다. get_object_or_404()는 뷰에서만 이용해야 한다!


  • ObjectDoesNotExist, DoesNotExist, MultipleObjectReturend

단일 모델 instance에서 get_object_or_404()를 사용할 때는 try-except 블록으로 예외처리를 할 필요가 없다. get_object_or_404()가 자동으로 처리하기 때문이다.

하지만 이를 제외한 경우도 있으므로 몇가지 더 알아보자


views.py
from django.shortcuts import render
from django.core.exceptions import ObjectDoesNotExist
from django.http import Http404

from .models import Post

def post_detail(request, pk):
try:
post = Post.objects.get(pk=pk)
except Post.DoesNotExist:
#or ObejctDoesNot Exist
raise Http404
return render(request,"blog/post_detail.html",{"post":post,})


ObjectDoesNotExist는 어떤 모델 객체에서도 사용할 수 있지만 DoesNotExist는 특정 모델에서만 사용할 수 있다.

만약 하나이상의 객체를 반환하는 것을 catch하고 싶다면 MultipleObjectsReturned exception을 참고하자. 이 셋을 이용해서 우리가 원하는 방향으로 log를 남기면 된다.



  • 지연 평가를 사용하자

Django의 ORM기능의 가장 강력한 부분 중의 하나는 Data가 정말로 필요하기 전까지 DB에 연동하지 않는다. 따라서 우리는 원하는 결과가 나올 때까지 Chaining을 이용해 결과를 써내려 갈 수 있다. 그러므로 한번에 여러 메서드와 각종 기능을 넣는 대신에 가독성을 위해 여러줄에 걸쳐 쓰는 것이 가능해진다.

이를 통해 얻어지는 가독성의 향상은 결국 유지 보수를 한결 더 쉽게 할 수 있다.

views.py
from django.models import Q
from .models import Post


“""
def search_post(**kwargs):
return Post.objects.active().filter(Q(name__startswith=name)|Q(dexcription_icontains=name))
“""
#위의 코드보다 아래의 코드가 더 읽기 좋다.

def search_post(**kwargs):
results = Post.objects.active()
results = results.filter(Q(name__startswith=name)|Q(dexcription_icontains=name))
return results


가독성의 향상은 최종 결과가 어떻다는 것을 좀 더 쉽게 이야기 해준다. 코드를 여러 줄로 분리함으로써 코드에 주석을 좀 더 쉽게 달 수도 있다. 파이썬을 쓰는 이유와 완전히 일치하지 않는가?