Python 2는 더 이상 커뮤니티에서 지원하지 않습니다. Python 2 앱을 Python 3로 마이그레이션하는 것이 좋습니다.

메타데이터

참고: 새로운 애플리케이션을 빌드하는 개발자는 NDB 클라이언트 라이브러리를 사용하는 것이 좋습니다. NDB 클라이언트 라이브러리는 이 클라이언트 라이브러리와 비교할 때 Memcache API를 통한 자동 항목 캐싱과 같은 여러 이점이 있습니다. 현재 이전 DB 클라이언트 라이브러리를 사용 중인 경우 DB에서 NDB로의 마이그레이션 가이드를 참조하세요.

Datastore는 일부 메타데이터에 프로그래매틱 방식으로 액세스 기능을 제공하여 메타프로그래밍을 통해 백엔드 관리 기능을 구현하고 일관된 캐싱을 단순화합니다. 또한 유사한 목적, 예를 들면 애플리케이션에 커스텀 Datastore 뷰어를 빌드하는 데에도 사용할 수 있습니다. 사용 가능한 메타데이터에는 항목 그룹, 네임스페이스, 항목 종류, 애플리케이션에서 사용하는 속성에 대한 정보와 각 속성의 속성 표현이 포함됩니다.

Cloud Console의 Datastore 대시보드도 애플리케이션에 대한 일부 메타데이터를 제공하지만 대시보드에 표시되는 데이터는 몇 가지 중요한 점에서 이러한 함수가 반환하는 데이터와 다릅니다.

  • 최신 정보. API를 사용한 메타데이터 읽기는 최신 데이터를 가져오며 이때 대시보드의 데이터는 매일 한 번씩만 업데이트됩니다.
  • 콘텐츠. 대시보드 메타데이터 중 일부는 API를 통해 제공되지 않으며 반대의 경우도 마찬가지입니다.
  • 속도. 메타데이터 Gets 및 쿼리는 Datastore Gets 및 쿼리와 동일한 방식으로 청구됩니다. 네임스페이스, 종류, 속성에 정보를 가져오는 메타데이터 쿼리는 일반적으로 느린 속도로 실행됩니다. 일반적으로 항목 N개를 반환하는 메타데이터 쿼리는 항목 하나씩 반환하는 일반 쿼리 N개와 비슷한 시간이 걸립니다. 그뿐만 아니라 속성 표현 쿼리(키 전용이 아닌 속성 쿼리)는 키 전용 속성 쿼리보다 느립니다. 항목 그룹 메타데이터의 메타데이터 가져오기는 일반 항목 가져오기보다 다소 빠르게 실행됩니다.

도우미 함수

다음 함수는 메타데이터 정보를 가져옵니다.

  • get_entity_group_version()은 항목 그룹의 버전 번호를 가져옵니다. 이 함수는 마지막으로 버전 번호를 가져온 후에 그룹의 항목이 변경되었는지 확인하는 데 유용합니다.
  • get_namespaces()는 모든 애플리케이션 네임스페이스의 이름 또는 지정한 범위의 네임스페이스의 이름이 포함된 목록을 반환합니다.
  • get_kinds()는 모든 애플리케이션 항목 종류의 이름 또는 지정한 범위의 항목 종류의 이름이 포함된 목록을 반환합니다.
  • get_properties_of_kind()는 지정한 항목 종류와 연관된 애플리케이션의 모든 색인이 생성된 속성의 이름 또는 지정한 범위의 색인이 생성된 속성의 이름이 포함된 목록을 반환합니다. 색인화되지 않은 속성은 포함되지 않습니다.
  • get_representations_of_kind()는 지정한 항목 종류와 연관된 색인이 생성된 모든 애플리케이션 속성의 표현 또는 지정한 범위의 색인이 생성된 속성의 표현이 포함된 사전을 반환합니다. 이 사전은 각 속성의 이름을 해당 속성의 표현으로 이루어진 목록에 매핑합니다. 색인이 생성되지 않은 속성은 포함되지 않습니다.

항목 그룹 메타데이터

Cloud Datastore는 항목 그룹의 '버전'에 대한 액세스를 제공합니다. 버전은 항목 그룹이 변경될 때마다 반드시 증가하는 값이며 전적으로 양수입니다.

다음 예시는 항목 그룹의 버전을 가져오는 방법을 보여줍니다.

from google.appengine.ext import db
from google.appengine.ext.db import metadata

class Simple(db.Model):
  x = db.IntegerProperty()

entity1 = Simple(x=11)
entity1.put()

# Print entity1's entity group version
print 'version', metadata.get_entity_group_version(entity1)

# Write to a different entity group
entity2 = Simple(x=22)
entity2.put()

# Will print the same version, as entity1's entity group has not changed
print 'version', metadata.get_entity_group_version(entity1)

# Change entity1's entity group by adding a new child entity
entity3 = Simple(x=33, parent=entity1.key())
entity3.put()

# Will print a higher version, as entity1's entity group has changed
print metadata.get_entity_group_version(entity1)

이전 동작

이전 항목 그룹 버전 동작에서는 항목 그룹 버전이 항목 그룹 변경시에만 증가합니다. 예를 들어 이전 항목 그룹 메타데이터 동작은 항목 그룹에서 복잡한 상위 쿼리의 일관된 캐시를 유지하는 데 사용될 수 있습니다.

이 예시에서는 쿼리 결과(일치하는 결과 수)를 캐시하고 항목 그룹 버전의 이전 동작을 사용하여 캐시된 값이 최신 버전의 값일 때만 이 값을 사용합니다.

from google.appengine.api import memcache
from google.appengine.ext import db
from google.appengine.ext.db import metadata

def count_entity_group(entity_group_key):
  """Count the entities in the specified entity group."""
  # Check if we have a cached version of the current entity group count
  cached = memcache.get(str(entity_group_key))
  if cached:
    (version, count) = cached
    # Is the cached value for the current version?
    if version == metadata.get_entity_group_version(entity_group_key):
      return count

  def tx():
    # Need to actually count entities. Using a transaction to get a consistent
    # count and entity group version.
    count = db.Query(keys_only=True).ancestor(entity_group_key).count(limit=5000)
    # Cache the count and the entity group version
    version = metadata.get_entity_group_version(entity_group_key)
    memcache.set(str(entity_group_key), (version, count))
    return count

  return db.run_in_transaction(tx)

get_entity_group_version()은 한 번도 기록되지 않은 항목 그룹에 None을 반환할 수 있습니다.

__version__ 속성이 포함된 특별한 유사 항목에서 get()을 호출하여 항목 그룹 버전을 가져올 수 있습니다. 자세한 내용은 EntityGroup에 대한 참조 문서를 확인하세요.

메타데이터 쿼리

이전 섹션에서 설명한 도우미 함수가 요구사항을 충족하지 않으면 명시적인 메타데이터 쿼리를 사용하여 보다 정교하거나 유연한 메타데이터 요청을 실행할 수 있습니다. Python에서 이러한 쿼리의 모델 클래스는 google.appengine.ext.db.metadata 패키지에서 정의됩니다. 이러한 모델은 메타데이터 쿼리용으로 예약된 특수한 항목 종류를 제공합니다.

모델 클래스 항목 종류
Namespace __namespace__
Kind __kind__
Property __property__

이러한 모델과 종류는 이미 애플리케이션에 존재할 가능성이 있는 이름이 같은 다른 모델 및 종류와 충돌하지 않습니다. 이러한 특수 종류를 쿼리하면 원하는 메타데이터가 포함된 항목을 검색할 수 있습니다.

메타데이터 쿼리에서 반환하는 항목은 Datastore의 현재 상태에 따라 동적으로 생성됩니다. Namespace, Kind 또는 Property 모델 클래스의 로컬 인스턴스를 만들 수 있지만 이러한 인스턴스를 Datastore에 저장하려고 시도하면 BadRequestError 예외가 발생하고 실패합니다.

두 클래스 중 하나에 속한 쿼리 객체를 사용하여 메타데이터 쿼리를 실행할 수 있습니다.

  • 슈퍼클래스 메서드 Model.all()에서 상속된 클래스 메서드 Namespace.all(), Kind.all() 또는 Property.all()에서 반환된 Query 객체
  • GQL 스타일 쿼리의 GqlQuery 객체

다음 예는 애플리케이션의 모든 항목 종류의 이름을 반환합니다.

from google.appengine.ext import db
from google.appengine.ext.db.metadata import Kind

for k in Kind.all():
  print "kind: '%s'" % k.kind_name

네임스페이스 쿼리

애플리케이션에서 Namespaces API를 사용할 경우 네임스페이스 쿼리를 사용하여 애플리케이션 항목에서 사용되는 모든 네임스페이스를 찾을 수 있습니다. 이 방법으로 여러 네임스페이스에 대한 관리 기능과 같은 작업을 수행할 수 있습니다.

네임스페이스 쿼리는 키 이름이 네임스페이스 이름인 특수한 종류의 __namespace__ 항목을 반환합니다. 단, 빈 문자열 ""로 지정된 기본 네임스페이스는 예외입니다. 빈 문자열은 유효한 키 이름이 아니므로 이 네임스페이스는 대신 숫자 ID 1로 키가 지정됩니다. 이 유형의 쿼리는 값이 항목 키인 __key__ 특수 유사 속성을 범위로 하는 필터링만 지원합니다. 결과는 __key__ 값의 오름차순으로 정렬될 수 있습니다(내림차순 불가). __namespace__ 항목에는 속성이 없으므로 키 전용 쿼리와 키 전용이 아닌 쿼리 모두 동일한 정보를 반환합니다.

네임스페이스 항목은 모델 클래스 google.appengine.ext.db.metadata.Namespace의 인스턴스입니다. 항목 키에서 계산된 문자열 속성 namespace_name은 해당 네임스페이스의 이름을 반환합니다. 키에 숫자 ID 1이 있으면 이 속성은 빈 문자열을 반환합니다. 쿼리를 용이하게 하도록 Namespace 모델은 다음과 같은 클래스 메서드를 제공합니다.

예를 들어 다음은 도우미 함수 get_namespaces()의 구현입니다. 이 함수는 모든 애플리케이션 네임스페이스의 이름(또는 지정한 이름 두 개(startend) 사이의 범위에 있는 이름)이 포함된 목록을 반환합니다.

from google.appengine.ext import db
from google.appengine.ext.db.metadata import Namespace

def get_namespaces(start=None, end=None):

  # Start with unrestricted namespace query
  q = Namespace.all()

  # Limit to specified range, if any
  if start is not None:
    q.filter('__key__ >=', Namespace.key_for_namespace(start))
  if end is not None:
    q.filter('__key__ <', Namespace.key_for_namespace(end))

  # Return list of query results
  return [ns.namespace_name for ns in q]

종류 쿼리

종류 쿼리는 키 이름이 항목 종류 이름인 __kind__ 종류의 항목을 반환합니다. 이 유형의 쿼리는 암시적으로 현재 네임스페이스로 제한되며 __key__ 유사 속성을 범위로 하는 필터링만 지원합니다. 결과는 __key__ 값의 오름차순으로 정렬될 수 있습니다(내림차순 불가). __kind__ 항목에는 속성이 없으므로 키 전용 쿼리와 키 전용이 아닌 쿼리 모두 동일한 정보를 반환합니다.

종류 항목은 모델 클래스 google.appengine.ext.db.metadata.Kind의 인스턴스입니다. 항목 키에서 계산된 문자열 속성 kind_name은 해당하는 항목 종류의 이름을 반환합니다. 쿼리를 용이하게 하도록 Kind 모델은 다음과 같은 클래스 메서드를 제공합니다.

예를 들어 다음은 도우미 함수 get_kinds()의 구현입니다. 이 함수는 모든 애플리케이션 항목 종류의 이름(또는 지정한 이름 두 개(startend) 사이의 범위에 있는 이름)이 포함된 목록을 반환합니다.

from google.appengine.ext import db
from google.appengine.ext.db.metadata import Kind

def get_kinds(start=None, end=None):

  # Start with unrestricted kind query
  q = Kind.all()

  # Limit to specified range, if any
  if start is not None and start != '':
    q.filter('__key__ >=', Kind.key_for_kind(start))
  if end is not None:
    if end == '':
      return []        # Empty string is not a valid kind name, so can't filter
    q.filter('__key__ <', Kind.key_for_kind(end))

  # Return list of query results
  return [k.kind_name for k in q]

다음 예시는 이름이 소문자로 시작하는 모든 종류를 표시합니다.

from google.appengine.ext import db
from google.appengine.ext.db.metadata import Kind

# Start with unrestricted kind query
q = Kind.all()

# Limit to lowercase initial letters
q.filter('__key__ >=', Kind.key_for_kind('a'))
endChar = chr(ord('z') + 1)                        # Character after 'z'
q.filter('__key__ <', Kind.key_for_kind(endChar))

# Print query results
for k in q:
  print k.kind_name

속성 쿼리

속성 쿼리는 항목 종류와 연관된 속성을 나타내는 __property__ 종류의 항목을 반환합니다(이 속성이 현재 이 종류의 모델에 정의되어 있는지 여부와 관계없이). K 종류의 P 속성을 나타내는 항목은 다음과 같이 빌드됩니다.

  • 항목 키에는 __property__ 종류 및 키 이름 P가 포함됩니다.
  • 상위 항목 키에는 __kind__ 종류 및 키 이름 K가 포함됩니다.

속성 항목은 모델 클래스 google.appengine.ext.db.metadata.Property의 인스턴스입니다. 문자열 속성 kind_nameproperty_name은 해당 키에서 계산되고 해당 종류와 속성의 이름을 반환합니다. Property 모델은 __property__ 키 빌드 및 검토를 단순화하는 4가지 클래스 메서드를 제공합니다.

다음 예는 이러한 메소드를 보여줍니다.

from google.appengine.ext import db
from google.appengine.ext.db.metadata import Property

class Employee(db.Model):
  name = db.StringProperty()
  ssn = db.IntegerProperty()

employee_key = Property.key_for_kind("Employee")
employee_name_key = Property.key_for_property("Employee", "Name")

Property.key_to_kind(employee_key)           # Returns "Employee"
Property.key_to_property(employee_name_key)  # Returns "Name"

속성 쿼리 동작은 아래 하위 섹션의 설명처럼 키 전용 쿼리 또는 키 전용이 아닌(속성 표현) 쿼리인지에 따라 달라집니다.

속성 쿼리: 키 전용

키 전용 속성 쿼리는 지정된 항목 종류의 색인이 생성된 각 속성의 키를 반환합니다. (색인이 생성되지 않은 속성은 포함되지 않습니다.) 다음 예에서는 애플리케이션의 모든 항목 종류와 각 항목과 연관된 속성의 이름을 보여줍니다.

from google.appengine.ext import db
from google.appengine.ext.db.metadata import Property

# Create unrestricted keys-only property query
q = Property.all(keys_only=True)

# Print query results
for p in q:
  print "%s: %s" % (Property.key_to_kind(p), Property.key_to_property(p))

이 유형의 쿼리는 암시적으로 현재 네임스페이스로 제한되고 유사 속성 __key__ 범위의 필터링만 지원합니다. 여기서 키는 __kind__ 또는 __property__ 항목을 나타냅니다. 결과는 __key__ 값의 오름차순으로 정렬될 수 있습니다(내림차순 불가). 필터링은 종류-속성 쌍에 적용되며, 처음에는 종류에 따라 그리고 두 번째는 속성에 따라 정렬됩니다. 예를 들어 다음과 같은 속성을 가진 항목이 있다고 가정합니다.

  • 다음 속성이 있는 Account 종류
    • balance
    • company
  • 다음 속성이 있는 Employee 종류
    • name
    • ssn
  • 다음 속성이 있는 Invoice 종류
    • date
    • amount
  • 다음 속성이 있는 Manager 종류
    • name
    • title
  • 다음 속성이 있는 Product 종류
    • description
    • price

속성 데이터를 반환하는 쿼리는 다음과 같습니다.

from google.appengine.ext import db
from google.appengine.ext.db.metadata import Property

# Start with unrestricted keys-only property query
q = Property.all(keys_only=True)

# Limit range
q.filter('__key__ >=', Property.key_for_property("Employee", "salary"))
q.filter('__key__ <=', Property.key_for_property("Manager", "salary"))

# Print query results
for p in q:
  print "%s: %s" % (Property.key_to_kind(p), Property.key_to_property(p))

위 쿼리는 다음을 반환합니다.

Employee: ssn
Invoice: date
Invoice: amount
Manager: name

결과에는 Employee 종류의 name 속성과 Manager 종류의 title 속성, AccountProduct 종류의 속성이 포함되지 않습니다. 이러한 속성은 쿼리에 지정된 범위 밖에 있기 때문입니다.

속성 쿼리는 __kind__ 또는 __property__ 키에서 상위 필터링도 지원하여 단일 종류 또는 속성으로 쿼리 결과를 제한합니다. 다음 예와 같이 이 점을 사용하여, 예를 들면 지정된 항목 종류와 연관된 속성을 가져올 수 있습니다.

(도우미 함수 get_properties_of_kind() 구현)

from google.appengine.ext import db
from google.appengine.ext.db.metadata import Property

def get_properties_of_kind(kind, start=None, end=None):

  # Start with unrestricted keys-only property query
  q = Property.all(keys_only=True)

  # Limit to specified kind
  q.ancestor(Property.key_for_kind(kind))

  # Limit to specified range, if any
  if start is not None and start != '':
    q.filter('__key__ >=', Property.key_for_property(kind, start))
  if end is not None:
    if end == '':
      return []     # Empty string is not a valid property name, so can't filter
    q.filter('__key__ <', Property.key_for_property(kind, end))

  # Return list of query results
  return [Property.key_to_property(p) for p in q]

속성 쿼리: 키 전용이 아닌 쿼리(속성 표현)

키 전용이 아닌 속성 쿼리(속성 표현 쿼리라고 함)는 각 종류-속성 쌍에 사용된 표현에 대한 추가 정보를 반환합니다. (색인이 생성되지 않은 속성은 포함되지 않습니다.) K 종류의 P 속성에 반환된 항목에는 속성의 표현을 반환하는 추가 property_representation 속성과 함께 해당 키 전용 쿼리와 동일한 키가 포함됩니다. 이 속성 값은 K 종류의 항목에서 발견된 P 속성의 각 표현에 대한 문자열 하나를 포함하는 StringListProperty 클래스의 인스턴스입니다.

표현은 속성 클래스와 동일하지 않으며, 여러 속성 클래스가 동일 표현에 매핑될 수 있습니다. 예를 들어 StringPropertyPhoneNumberProperty는 모두 STRING 표현을 사용합니다.

다음 표는 속성 클래스와 해당 표현을 보여줍니다.

속성 클래스 표현
IntegerProperty INT64
FloatProperty DOUBLE
BooleanProperty BOOLEAN
StringProperty STRING
ByteStringProperty STRING
DateProperty INT64
TimeProperty INT64
DateTimeProperty INT64
GeoPtProperty POINT
PostalAddressProperty STRING
PhoneNumberProperty STRING
EmailProperty STRING
UserProperty USER
IMProperty STRING
LinkProperty STRING
CategoryProperty STRING
RatingProperty INT64
ReferenceProperty
SelfReferenceProperty
REFERENCE
blobstore.BlobReferenceProperty STRING
ListProperty 목록 요소의 표현
StringListProperty 목록 요소의 표현

예를 들어 다음은 도우미 함수 get_representations_of_kind()의 구현입니다. 이 함수는 지정한 항목 종류와 관련된 색인이 생성된 모든 애플리케이션 속성의 표현(또는 지정한 이름 두 개(startend) 사이의 범위에 있는 표현)이 포함된 사전을 반환합니다. 이 사전은 각 속성의 이름을 해당 속성의 표현으로 이루어진 목록에 매핑합니다.

from google.appengine.ext import db
from google.appengine.ext.db.metadata import Property

def get_representations_of_kind(kind, start=None, end=None):

  # Start with unrestricted non-keys-only property query
  q = Property.all()

  # Limit to specified kind
  q.ancestor(Property.key_for_kind(kind))

  # Limit to specified range, if any
  if start is not None and start != '':
    q.filter('__key__ >=', Property.key_for_property(kind, start))
  if end is not None:
    if end == '':
      return []     # Empty string is not a valid property name, so can't filter
    q.filter('__key__ <', Property.key_for_property(kind, end))

  # Initialize result dictionary
  result = {}

  # Add query results to dictionary
  for p in q:
    result[p.property_name] = p.property_representation

  # Return dictionary
  return result