항목 모델 만들기

항목에 대한 모델 클래스를 만들어야 합니다. 여기에는 두 가지 방법이 있습니다.

  • 항목 속성을 정의하는 모델 클래스를 만듭니다.
  • 사전에 항목을 정의하지 않는 expando 모델 클래스를 만듭니다.

이 문서는 이러한 각 모델 클래스 유형을 만드는 방법을 설명합니다. 또한 특정 작업 유형 이전이나 이후(예: 각 get() 이전)에 애플리케이션이 일부 코드를 실행할 수 있도록 모델 후크를 만드는 방법도 설명합니다.

속성을 포함한 모델 클래스 만들기

항목을 만들기 전에 항목 속성을 한 개 이상 정의하는 모델 클래스를 만들어야 합니다. 예를 들면 다음과 같습니다.

from google.appengine.ext import ndb
...
class Account(ndb.Model):
    username = ndb.StringProperty()
    userid = ndb.IntegerProperty()
    email = ndb.StringProperty()

여기서 각 계정 항목에 사용자 이름, 사용자 ID, 이메일에 대한 속성을 포함하려 합니다.

속성 유형 전체 목록에 대해서는 항목 속성 참조 문서를 참조하세요.

Expando 모델 클래스 만들기

속성을 미리 정의하는 모델 클래스를 사용하지 않아도 됩니다. 할당된 속성이 Datastore에 저장되도록 Expando라는 특수한 모델 서브클래스가 항목 동작을 변경합니다. 이러한 속성은 밑줄로 시작될 수 없다는 점에 유의하세요.

다음은 Expando 모델을 만드는 방법입니다.

class Mine(ndb.Expando):
    pass
...
e = Mine()
e.foo = 1
e.bar = 'blah'
e.tags = ['exp', 'and', 'oh']
e.put()

그러면 정수값이 1foo 속성, 문자열 값이 'blah'bar 속성, 문자열 값이 'exp', 'and', 'oh'로 반복되는 tag 속성을 사용하여 Datastore에 항목이 기록됩니다. 속성에 대한 색인이 생성되며 항목의 _properties 속성을 사용하여 속성을 검사할 수 있습니다.

return e._properties
# {
#     'foo': GenericProperty('foo'),
#     'bar': GenericProperty('bar'),
#     'tags': GenericProperty('tags', repeated=True)
# }

Datastore에서 값을 가져와 생성된 Expando에는 Datastore에 저장된 모든 속성 값에 대한 속성이 포함됩니다.

애플리케이션은 Expando 서브클래스에 사전 정의된 속성을 추가할 수 있습니다.

class FlexEmployee(ndb.Expando):
    name = ndb.StringProperty()
    age = ndb.IntegerProperty()
...
employee = FlexEmployee(name='Sandy', location='SF')

그러면 employee에 값이 'Sandy'name 속성, 값이 Noneage 속성, 값이 'SF'인 동적 속성 location이 지정됩니다.

색인이 생성되지 않은 속성의 Expando 서브클래스를 만들려면 서브클래스 정의에 _default_indexed = False를 설정하세요.

class Specialized(ndb.Expando):
    _default_indexed = False
...
e = Specialized(foo='a', bar=['b'])
return e._properties
# {
#     'foo': GenericProperty('foo', indexed=False),
#     'bar': GenericProperty('bar', indexed=False, repeated=True)
# }

Expando 항목에 _default_indexed도 설정할 수 있습니다. 그러면 설정된 이후에 할당된 모든 속성이 영향을 받습니다.

또 다른 유용한 방법은 동적 속성에 대해 Expando 종류를 쿼리하는 것입니다. 다음과 같은 쿼리는

FlexEmployee.query(FlexEmployee.location == 'SF')

이 쿼리는 클래스에 위치 속성에 대한 속성 객체가 없기 때문에 작동하지 않습니다. 대신 Expando 클래스가 동적 속성에 사용하는 GenericProperty를 사용하세요.

FlexEmployee.query(ndb.GenericProperty('location') == 'SF')

모델 후크 사용

NDB는 가벼운 후킹 메커니즘을 제공합니다. 후크를 정의하면 애플리케이션이 특정 작업 유형 이전이나 이후에 일부 코드를 실행할 수 있습니다. 예를 들어 Model은 매 get() 실행 이전에 특정 함수를 실행할 수 있습니다.

적절한 메서드의 동기식, 비동기식, 멀티 버전을 사용하면 후크 함수가 실행됩니다. 예를 들어 'pre-get' 후크는 get(), get_async(), get_multi()에 모두 적용됩니다. 각 후크에는 사전 RPC와 사후 RPC 버전이 있습니다.

후크는 다음의 경우에 유용할 수 있습니다.

  • 쿼리 캐싱
  • 사용자별로 Cloud Datastore 활동 감사
  • 데이터베이스 트리거 모방

다음 예는 후크 함수를 정의하는 방법을 보여 줍니다.

from google.appengine.ext import ndb
...
class Friend(ndb.Model):
    name = ndb.StringProperty()

    def _pre_put_hook(self):
        _notify('Gee wiz I have a new friend!')

    @classmethod
    def _post_delete_hook(cls, key, future):
        _notify('I have found occasion to rethink our friendship.')
...
f = Friend()
f.name = 'Carole King'
f.put()  # _pre_put_hook is called
fut = f.key.delete_async()  # _post_delete_hook not yet called
fut.get_result()  # _post_delete_hook is called

비동기식 API로 사후 후크를 사용하는 경우 check_result(), get_result()를 호출하거나 tasklet 내에서 비동기 메서드의 future를 생성하면 후크가 트리거됩니다. 사후 후크는 RPC가 성공했는지 여부를 확인하지 않습니다. 즉, 실패하더라도 후크가 실행됩니다.

모든 사후 후크에는 호출 서명 맨 뒤에 Future 인수가 있습니다. 이 Future 객체는 작업 결과를 유지합니다. 이 Future에서 get_result()를 호출하여 결과를 검색할 수 있습니다. 후크가 호출되는 시점 전까지 Future가 완료되므로 get_result()가 차단되지 않는다고 확신할 수 있습니다.

사전 후크 중에 예외가 발생하면 요청이 수행되지 않습니다. 후크는 <var>*</var>_async 메서드 내에서 트리거되지만 사전 RPC 후크에서 tasklets.Return을 발생시켜 RPC를 선점할 수 없습니다.

다음 단계