Django

Django 쿼리문 F() 사용하기

김지훈_ 2021. 8. 4. 15:33

장고 쿼리문의 다양한 기능에 대한 세미나를 듣고 궁금해서 찾아보다가, F()가 제공하는 다양한 기능들에 대해 기록하기 위해 포스트를 작성한다.

 

Database level update

F() 객체는 모델의 필드 혹은 어노테이트된 열의 값을 나타낸다. 

 

F() 객체를 사용하면 DB에서 파이썬 메모리로 데이터를 가져오지 않고 DB의 필드 값을 참조하여 작업을 수행할 수 있다.

 

예시를 통해 확인해보자.

 

answer_set = AnswerSet.objects.get(pk=1)

answer_set.score += 1
answer_set.save()

 

위 구문은 쿼리를 통해 pk가 1인 AnswerSet 객체를 가져오고 해당 객체의 score를 메모리에 올리고 이를 파이썬 연산자를 사용하여 조작한 후 다시 DB에 저장한다. 

 

answer_set = AnswerSet.objects.get(pk=1)

answer_set.score = F('score') + 1

answer_set.save()

 

위 구문에서 answer_set.score = F('score') + 1 구문은 DB 연산을 설명하는 SQL 구문이다. 다만, 이런식으로 DB 데이터를 변경한다면 파이썬 메모리 입장에서는 변경된 값을 알지 못하기 때문에 변경된 값을 알고 싶다면 새로 호출해서 가져와야 한다.

 

위 구문은 update를 사용하면 더욱 줄일 수 있다.

answer_set = AnswerSet.objects.get(pk=1)

answer_set.update(score = F('score') + 1)

 

Prevent Race Condition

Race Condition이란 여러 개의 Thread 가 하나의 공통의 Resourece를 사용하거나 접근할 때, 작업이 겹치게 되면 Thread 간의 Resource 사용이 맞물리게 되어 데이터 손실을 유발할지도 모르는 상황을 말한다.

 

다음 예시를 보자.

 

answer_set = AnswerSet.objects.get(pk=1)

answer_set.score += 1

print(answer_set.score)
answer_set.save()

 

answer_set.score의 초기값이 1이라고 하자. Thread A가 answer_set.score += 1 구문을 통해 score의 값을 1 증가시킨 후 save 하기 전에 Thread B가 answer_set.score += 1 구문에 들어와 버리면 DB에 저장되어 있는 값은 여전히 1이기 때문에 두 Thread 모두 2를 print할 것이고, DB에 2를 저장하게 되어 데이터가 손실된다.

 

이러한 Race Condition은 트래픽이 많은 곳에서는 빈번하게 발생되는데, F() 객체를 사용하면 이를 방지할 수 있다.

 

앞에서도 언급했다 시피 F() 객체는 database level에서 이루어지는 update이며, 데이터를 파이썬 메모리로 가져오지 않기 때문이다.

 

References

https://brownbears.tistory.com/367

https://blog.myungseokang.dev/posts/django-f-class/