Property
클래스는 서브클래스로 처리되도록 설계되었습니다.
하지만 일반적으로 기존의 Property
서브클래스를 서브클래스로 만드는 것이 더 쉽습니다.
'공개'로 간주되는 속성을 포함하여 모든 특수 Property
속성의 이름은 밑줄로 시작됩니다.
이는 StructuredProperty
가 중첩 Property
이름을 표시할 때 밑줄이 아닌 속성 네임스페이스를 사용하기 때문입니다. 이는 하위 속성에 쿼리를 지정하는 데 필수적입니다.
Property
클래스 및 사전 정의된 서브클래스를 이용하면 구성 가능한(또는 스택 가능한) 유효성 검사와 변환 API를 사용하여 서브클래스로 만들 수 있습니다. 이를 위해서는 몇 가지 용어를 정의해야 합니다.
- 사용자 값은 항목의 표준 속성을 사용하여 애플리케이션 코드로 설정 및 액세스되는 값입니다.
- 기본 값은 Datastore에서 직렬화 및 비직렬화되는 값입니다.
사용자 값과 직렬화 가능 값 간의 특정 변환을 구현하는 Property
서브클래스는 _to_base_type()
및 _from_base_type()
의 두 가지 메서드를 구현해야 합니다.
이러한 메서드는 super()
메서드를 호출해서는 안 됩니다.
이는 구성 가능한(또는 스택 가능한) API의 영향입니다.
API는 더 정교한 사용자 값에서 기본 값으로의 변환을 지원하는 stacking 클래스를 지원합니다. 사용자 값에서 기본 값으로의 변환은 더 정교한 쪽에서 덜 정교한 쪽으로 변환되며, 기본 값에서 사용자 값으로의 변환은 덜 정교한 쪽에서 더 정교한 쪽으로 변환되는 것입니다. 예를 들어 BlobProperty
, TextProperty
, StringProperty
간의 관계를 참조하세요.
예를 들어 TextProperty
는 BlobProperty
에서 상속되며, 필요한 동작 대부분을 상속받기 때문에 코드는 매우 간단합니다.
_to_base_type()
및 _from_base_type()
외에도 _validate()
메서드 또한 구성 가능한 API입니다.
유효성 검사 API는 느슨한 사용자 값과 엄격한 사용자 값을 구별합니다. 느슨한 값 집합은 엄격한 값 집합의 상위 집합입니다. _validate()
메서드는 느슨한 값을 취하고 필요한 경우 엄격한 값으로 변환합니다. 즉 속성 값을 설정할 때에는 느슨한 값이 허용되며 속성 값을 가져올 때에는 엄격한 값만이 반환됩니다. 변환할 필요가 없는 경우 _validate()
는 None을 반환할 수도 있습니다. 인수가 허용되는 느슨한 값의 집합을 벗어나는 경우 _validate()
는 예외를 발생시킵니다(예: TypeError
또는 datastore_errors.BadValueError
).
_validate()
, _to_base_type()
, _from_base_type()
은 다음을 처리할 필요가 없습니다.
None
:None
으로 호출되지 않습니다. 만약 None이 반환될 경우 값을 변환할 필요가 없습니다.- 반복되는 값: 값이 반복되는 각 목록 항목에 대해 인프라가
_from_base_type()
또는_to_base_type()
에 대한 호출을 처리합니다. - 사용자 값과 기본 값 구별: 인프라가 구성 가능한 API를 호출하여 이를 처리합니다.
- 비교: 비교 연산은 피연산자에 대해
_to_base_type()
을 호출합니다. - 사용자 값과 기본 값 구별: 인프라는
_from_base_type()
이 래핑 해제된 기본 값으로 호출되고_to_base_type()
이 사용자 값으로 호출되도록 보장합니다.
예를 들어, 매우 긴 정수를 저장해야 한다고 가정해 보세요.
표준 IntegerProperty
는 서명된 64비트 정수만을 지원합니다.
속성에 더 긴 정수를 문자열로 저장할 수도 있습니다. 이 경우 속성 클래스에서 변환을 처리하도록 하는 것이 좋습니다.
속성 클래스를 사용하는 애플리케이션은 다음과 비슷합니다.
...
...
...
...
이는 간단하고 직관적입니다. 또한 일부 표준 속성 옵션(기본, 반복)을 사용하는 방법도 보여줍니다. LongIntegerProperty
를 작성해 본 사용자라면 작업을 수행하기 위해 '상용구'를 쓸 필요가 없다는 것은 좋은 소식일 것입니다. 다른 속성의 서브클래스를 정의하는 것은 더 쉽습니다. 예를 들면 다음과 같습니다.
항목에 속성 값을 설정하면(예: ent.abc = 42
) _validate()
메서드가 호출되며 예외가 발생하지 않는 경우 값이 항목에 저장됩니다. Datastore에 항목을 쓸 때 _to_base_type()
메서드가 호출되어 값을 문자열로 변환합니다. 그런 다음 해당 값은 기본 클래스 StringProperty
에 의해 직렬화됩니다.
이벤트의 역 체인은 항목을 Datastore에서 다시 읽을 때 발생합니다. StringProperty
클래스와 Property
클래스는 함께 문자열 직렬화 및 비직렬화, 기본값 설정, 반복되는 속성 값 처리와 같은 기타 세부 작업을 처리합니다.
이 예에서 부등식(예: <, <=, >, >=을 사용하는 쿼리)을 지원하려면 더 많은 작업이 필요합니다. 다음 예시 구현은 정수의 최대 크기를 적용하고 값을 고정 길이 문자열로 저장합니다.
이는 속성 생성자에 비트 수를 전달해야 한다는 점을 제외하면(예: BoundedLongIntegerProperty(1024)
) LongIntegerProperty
와 같은 방식으로 사용할 수 있습니다.
비슷한 방식으로 다른 속성 유형을 서브클래스로 만들 수 있습니다.
이 접근법은 구조화된 데이터 저장에도 사용할 수 있습니다.
기간을 나타내는 FuzzyDate
Python 클래스가 있다고 가정해 보겠습니다. 이 클래스는 first
및 last
필드를 사용하여 기간의 시작과 종료를 저장합니다.
...
StructuredProperty
에서 파생된 FuzzyDateProperty
를 만들 수 있습니다. 하지만 후자의 경우 기존 Python 클래스에서는 사용할 수 없으며 Model
서브클래스가 필요합니다.
따라서 Model 서브클래스를 중간 표현으로 정의해야 합니다.
그런 다음, modelclass 인수를 FuzzyDateModel
로 하드코딩하는 StructuredProperty
의 서브클래스를 구성하고 _to_base_type()
및 _from_base_type()
메서드를 정의하여 FuzzyDate
와 FuzzyDateModel
간의 변환을 수행합니다.
애플리케이션은 다음과 같이 이 클래스를 사용할 수 있습니다.
...
FuzzyDate
객체 이외에도 일반 date
객체를 FuzzyDateProperty
값으로 허용하려 한다고 가정해 보세요. 이렇게 하려면 _validate()
메서드를 다음과 같이 수정해야 합니다.
FuzzyDateProperty._validate()
가 위에 표시된 대로라고 가정한다면 대신 다음과 같이 FuzzyDateProperty
를 서브클래스로 만들 수 있습니다.
MaybeFuzzyDateProperty
필드에 값을 할당하면 MaybeFuzzyDateProperty._validate()
및 FuzzyDateProperty._validate()
가 모두 순서대로 호출됩니다.
이는 _to_base_type()
및 _from_base_type()
에도 동일하게 적용됩니다. 즉, 슈퍼클래스 및 서브클래스의 메서드는 암시적으로 결합됩니다.
(이를 위해 상속된 동작을 제어하는데 super
를 사용하지 마세요.
이 세 가지 메서드 간의 상호작용은 미묘하며 super
로는 제어할 수 없습니다.)