ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Django Value(), F() 란?
    언어/파이썬 & 장고 2018. 6. 26. 19:48

    Value()

    Value(value, output_field=None)

    value() 객체는 나타낼 수 있는 가장 작은 값을 표현할 수 있습니다. 예를 들어 쿼리에서 integer나 boolean 또는 string 을 표현하고자 할 때 Value()로 감싸서 나타낼 수 있습니다.

    사실상 Value()를 직접 사용할 필요는 드뭅니다. F('foo') + 1 이라는 표현식이 있을 때, 장고는 암시적으로 1을 Value()로 감싸 복잡한 표현식에서 단순한 값을 사용하게 합니다. 만약 string 값을 표현식에 나타내고자 할 때 Value()를 사용하여 나타낼 수 있습니다. 대부분의 표현식은 Lower('name')과 같이 해석하고 있습니다.


    output_field는 IntegerField() 나 BooleanField()와 같은 장고 모델에서 제공하는 인스턴스를 작성해야하고 장고는 데이터베이스에서 값을 가져온 후 값을 로드합니다. 일반적으로 데이터 유효성 검사와 관련된 argument(max_length, max_digits 등)가 표현식의 출력 값에 적용되지 않으므로 모델 필드를 인스턴스화 할 때는 arguments가 필요하지 않습니다.

    F()

    F(name)

    F() 객체는 모델 필드의 값을 나타냅니다. 데이터베이스에서 파이썬 메모리로 데이터를 가져 오지 않고 모델 필드 값을 참조하고 사용하여 데이터베이스 작업을 수행 할 수 있습니다. 대신 장고는 F() 객체를 사용하여 데이터베이스 수준에서 필요한 작업을 설명하는 SQL 표현식을 생성합니다.

    아래 예제에서 F() 객체의 사용법을 알 수 있습니다.


    reporter = Reporters.objects.get(name='Tintin')
    reporter.stories_filed += 1
    reporter.save()


    위 파이썬 구문은 reporter.stories_filed의 값을 데이터베이스에서 메모리로 가져와 파이썬 연산자를 사용하여 조작 한 다음 데이터베이스에 다시 저장했습니다. 아래는 F() 객체를 사용하여 위와 동일한 결과를 생성하는 구문입니다.

    from django.db.models import F
    reporter = Reporters.objects.get(name='Tintin')
    reporter.stories_filed = F('stories_filed') + 1
    reporter.save()


    reporter.stories_filed = F ( 'stories_filed') + 1은 값을 인스턴스 속성에 할당 한 것처럼 보이지만 데이터베이스에 대한 연산을 설명하는 SQL 구문입니다.  장고는 F()의 인스턴스를 만나면 파이썬 연산자를 오버라이드하여 캡슐화된 SQL 표현식을 생성합니다. 이 경우 reporter.stories_filed가 나타내는 데이터베이스 필드를 증가 시키도록 데이터베이스에 지시합니다. reporter.stories_filed나 다른 어떠한 값이 있어도 데이터베이스에 의해 처리되기 때문에 파이썬은 알지 못합니다. 

    위 방법으로 저장한 새 값을 얻기 위해선 다시 호출해야 합니다.

    reporter = Reporters.objects.get(pk=reporter.pk)



    위 구문을 .update()와 F() 객체를 사용하여 줄일 수 있습니다.

    reporter = Reporters.objects.filter(name='Tintin')
    reporter.update(stories_filed=F('stories_filed') + 1)


    F()를 사용하면 race condition을 피할수 있다

    F()의 또 다른 유용한 이점은 파이썬이 아닌 데이터베이스가 필드 값을 업데이트하면 race condition을 피할 수 있다는 것입니다. 

    예를 들어, 첫 번째 예시(F()를 사용하지 않은 예시)를 두개의 파이썬 thread를 사용해 실행하면 A라는 thread가 값을 받아 파이썬 메모리에 저장할 때 B라는 thread는 값을 추출, 증가, 저장할 수 있습니다. A라는 thread는 값을 증가, 저장하게 되면 B 라는 thread가 작업했던 내용이 손실됩니다. (A는 B의 작업 이전에 값을 가져왔기 때문..)

    F()를 사용하면 위 문제를 해결할 수 있습니다. 메모리에 가져온 값을 기반으로 작업하는 것이 아닌 save() 나 update()가 실행될 때, 데이터베이스의 필드 값을 기준으로 작업하기 때문입니다.

    F()는 여러가지로 사용할 수 있다

    filter()에서의 사용

    SELECT a, b, c, d
    FROM test
    WHERE a = c


    위와 같은 쿼리가 있다고 가정하면 F()를 사용하여 아래와 같이 표현할 수 있습니다.

    from django.db.models import F
    
    Test.objects.filter(a=F('c'))

    annotate()에서의 사용 - 필드명 변경

    SELECT a, b, c, d as dddd
    FROM test


    위처럼 d라는 컬럼의 이름을 dddd로 변경하고자 할 때 F()를 사용하여 아래와 같이 표현할 수 있습니다.


    from django.db.models import F
    
    Test.objects.annotate(dddd=F('d'))

    요약

    • F()와 Value()는 연관이 없다.
    • Value()는 사실상 안써도 무관하다.
    • F()는 좋다.



    참조문서:

    https://docs.djangoproject.com/en/1.7/ref/models/queries/#f-expressions

    https://docs.djangoproject.com/en/2.0/ref/models/expressions/#value-expressions

    https://docs.djangoproject.com/en/1.7/topics/db/queries/#using-f-expressions-in-filters

    댓글