Referência da propriedade da entidade

O Firestore no modo Datastore (Datastore) suporta uma variedade de tipos de dados para valores de propriedades. Estas incluem, entre outras:

  • Números inteiros
  • Números de vírgula flutuante
  • Strings
  • Datas
  • Dados binários

Para ver uma lista completa de tipos, consulte Propriedades e tipos de valores.

Propriedades e tipos de valores

Os valores de dados associados a uma entidade consistem numa ou mais propriedades. Cada propriedade tem um nome e um ou mais valores. Uma propriedade pode ter valores de mais do que um tipo, e duas entidades podem ter valores de tipos diferentes para a mesma propriedade. As propriedades podem ser indexadas ou não indexadas (as consultas que ordenam ou filtram uma propriedade P ignoram as entidades em que P não está indexada). Uma entidade pode ter, no máximo, 20 000 propriedades indexadas.

São suportados os seguintes tipos de valores:

Quando uma consulta envolve uma propriedade com valores de tipos mistos, o Datastore usa uma ordenação determinística com base nas representações internas:

  1. Valores nulos
  2. Números de ponto fixo
    • Números inteiros
    • Datas e horas
  3. Valores booleanos
  4. Sequências de bytes
    • String Unicode
    • Chaves do Blobstore
  5. Números de vírgula flutuante
  6. Chaves do Datastore

Uma vez que as strings de texto longas e as strings de bytes longas não são indexadas, não têm uma ordenação definida.

Tipos de propriedades

O NDB suporta os seguintes tipos de propriedades:

Tipo de propriedade Descrição
IntegerProperty Número inteiro com sinal de 64 bits
FloatProperty Número de vírgula flutuante de precisão dupla
BooleanProperty Booleano
StringProperty String Unicode; até 1500 bytes, indexada
TextProperty String Unicode; comprimento ilimitado, não indexada
BlobProperty String de bytes não interpretada:
se definir indexed=True, até 1500 bytes, indexada;
se indexed for False (a predefinição), comprimento ilimitado, não indexada.
Argumento de palavra-chave opcional: compressed.
DateTimeProperty Data e hora (consulte as Propriedades de data e hora)
DateProperty Data (consulte as propriedades de data e hora)
TimeProperty Hora (consulte as propriedades de data e hora)
GeoPtProperty Localização geográfica. Este é um objeto ndb.GeoPt. O objeto tem os atributos lat e lon, ambos flutuantes. Pode construir um com dois números de vírgula flutuante, como ndb.GeoPt(52.37, 4.88) ou com uma string ndb.GeoPt("52.37, 4.88"). (Na verdade, esta é a mesma classe que db.GeoPt)
KeyProperty Datastore key
Argumento de palavra-chave opcional: kind=kind, para exigir que as chaves atribuídas a esta propriedade tenham sempre o tipo indicado. Pode ser uma string ou uma subclasse de modelo.
BlobKeyProperty Chave do Blobstore
Corresponde a BlobReferenceProperty na API db antiga, mas o valor da propriedade é um BlobKey em vez de um BlobInfo; pode construir um BlobInfo a partir dele usando BlobInfo(blobkey)
UserProperty Objeto do utilizador.
StructuredProperty Inclui um tipo de modelo dentro de outro, por valor (consulte Propriedades estruturadas)
LocalStructuredProperty Semelhante a StructuredProperty, mas a representação no disco é um blob opaco e não é indexada (consulte Propriedades estruturadas).
Argumento de palavra-chave opcional: compressed.
JsonProperty O valor é um objeto Python (como uma lista, um dicionário ou uma string) que é serializável através do módulo json do Python; o Datastore armazena a serialização JSON como um blob. Não indexado por predefinição.
Argumento de palavra-chave opcional: compressed.
PickleProperty O valor é um objeto Python (como uma lista, um dict ou uma string) que é serializável através do protocolo pickle do Python. O Datastore armazena a serialização pickle como um blob. Não indexado por predefinição.
Argumento de palavra-chave opcional: compressed.
GenericProperty Valor genérico
Usado principalmente pela classe Expando, mas também pode ser usado explicitamente. O respetivo tipo pode ser qualquer um dos seguintes: int, long, float, bool, str, unicode, datetime, Key, BlobKey, GeoPt, User, None.
ComputedProperty Valor calculado a partir de outras propriedades por uma função definida pelo utilizador. (Consulte Propriedades calculadas.)

Algumas destas propriedades têm um argumento de palavra-chave opcional, compressed. Se a propriedade tiver compressed=True, os respetivos dados são comprimidos com gzip no disco. Ocupa menos espaço, mas precisa de CPU para codificar/descofificar nas operações de escrita e leitura.

A compressão e a descompressão são "preguiçosas"; um valor de propriedade comprimido só é descomprimido na primeira vez que lhe acede. Se ler uma entidade que contenha um valor de propriedade comprimido e escrevê-lo novamente sem aceder à propriedade comprimida, esta não é descomprimida nem comprimida. A cache no contexto também participa neste esquema de carregamento diferido, mas o memcache armazena sempre o valor comprimido para propriedades comprimidas.

Devido ao tempo de CPU adicional necessário para a compressão, é normalmente melhor usar propriedades comprimidas apenas se os dados forem demasiado grandes para caber sem compressão. Lembre-se de que a compressão baseada em gzip não é normalmente eficaz para imagens e outros dados multimédia, uma vez que esses formatos já estão comprimidos através de um algoritmo de compressão específico de multimédia (por exemplo, JPEG para imagens).

Opções de propriedade

A maioria dos tipos de propriedades suporta alguns argumentos padrão. O primeiro é um argumento posicional opcional que especifica o nome do Datastore da propriedade. Pode usar esta opção para dar à propriedade um nome diferente no Datastore do que do ponto de vista da aplicação. Uma utilização comum desta funcionalidade é reduzir o espaço no Datastore, permitindo que o Datastore use nomes de propriedades abreviados enquanto o seu código usa nomes mais longos e significativos. Por exemplo,

class Employee(ndb.Model):
    full_name = ndb.StringProperty('n')
    retirement_age = ndb.IntegerProperty('r')

Isto é particularmente útil para propriedades repetidas para as quais espera muitos valores por entidade.

Além disso, a maioria dos tipos de propriedades suporta os seguintes argumentos de palavras-chave:

Argumento Tipo Predefinição Descrição
indexed bool Normalmente, True Inclua a propriedade nos índices do Datastore; se for False, não é possível consultar os valores, mas as escritas são mais rápidas. Nem todos os tipos de propriedades suportam a indexação. A definição de indexed como True falha para estes.
As propriedades não indexadas custam menos operações de escrita do que as propriedades indexadas.
repeated bool False O valor da propriedade é uma lista Python que contém valores do tipo subjacente (consulte Propriedades repetidas).
Não é possível combinar com required=True nem default=True.
required bool False A propriedade tem de ter um valor especificado.
default Tipo subjacente da propriedade Nenhum Valor predefinido da propriedade se não for especificado explicitamente nenhum valor.
choices Lista de valores do tipo subjacente None Lista opcional de valores permitidos.
validator Função None

Função opcional para validar e, possivelmente, forçar o valor.

Vai ser chamado com os argumentos (prop, value) e deve devolver o valor (possivelmente forçado) ou gerar uma exceção. Chamar a função novamente num valor convertido não deve modificar mais o valor. (Por exemplo, devolver value.strip() ou value.lower() é aceitável, mas não value + '$'.) Também pode devolver None, o que significa "sem alterações". Veja também Escrever subclasses de propriedades

verbose_name de string None

Etiqueta HTML opcional a usar em frameworks de formulários Web, como o jinja2.

Propriedades repetidas

Qualquer propriedade com repeated=True torna-se uma propriedade repetida. A propriedade recebe uma lista de valores do tipo subjacente, em vez de um único valor. Por exemplo, o valor de uma propriedade definida com IntegerProperty(repeated=True) é uma lista de números inteiros.

O Datastore pode ver vários valores para essa propriedade. É criado um registo de índice separado para cada valor. Isto afeta a semântica das consultas. Consulte o artigo Consultar propriedades repetidas para ver um exemplo.

Este exemplo usa uma propriedade repetida:

class Article(ndb.Model):
    title = ndb.StringProperty()
    stars = ndb.IntegerProperty()
    tags = ndb.StringProperty(repeated=True)
...
article = Article(
    title='Python versus Ruby',
    stars=3,
    tags=['python', 'ruby'])
article.put()

Isto cria uma entidade do Datastore com os seguintes conteúdos:

assert article.title == 'Python versus Ruby'
assert article.stars == 3
assert sorted(article.tags) == sorted(['python', 'ruby'])

Quando consulta a propriedade tags, esta entidade satisfaz uma consulta para 'python' ou 'ruby'.

Quando atualiza uma propriedade repetida, pode atribuir-lhe uma nova lista ou alterar a lista existente no local. Quando atribui uma nova lista, os tipos dos itens da lista são validados imediatamente. Os tipos de itens inválidos (por exemplo, atribuir [1, 2] a art.tags acima) geram uma exceção. Quando altera a lista, a alteração não é validada imediatamente. Em alternativa, o valor é validado quando escreve a entidade no Datastore.

O Datastore preserva a ordem dos itens da lista numa propriedade repetida, pelo que pode atribuir algum significado à respetiva ordenação.

Propriedades de data e hora

Estão disponíveis três tipos de propriedades para armazenar valores relacionados com datas e horas:

  • DateProperty
  • TimeProperty
  • DateTimeProperty

Estes assumem valores pertencentes às classes correspondentes (date, time, datetime) do módulo datetime padrão do Python. O mais geral dos três é DateTimeProperty, que denota uma data do calendário e uma hora do dia. Os outros são ocasionalmente úteis para fins especiais que requerem apenas uma data (como uma data de nascimento) ou apenas uma hora (como uma hora de reunião). Por motivos técnicos, DateProperty e TimeProperty são subclasses de DateTimeProperty, mas não deve depender desta relação de herança (e tenha em atenção que difere das relações de herança entre as classes subjacentes definidas pelo próprio módulo datetime).

Nota: as horas do relógio do App Engine são sempre expressas em tempo universal coordenado (UTC). Isto torna-se relevante se usar a data ou a hora atual (datetime.datetime.now()) como um valor ou converter entre objetos datetime e datas/horas POSIX ou tuplos de tempo. No entanto, não são armazenadas informações explícitas de fuso horário no Datastore. Por isso, se tiver cuidado, pode usá-las para representar horas locais em qualquer fuso horário, se usar a hora atual ou as conversões.

Cada uma destas propriedades tem duas opções de palavras-chave booleanas adicionais:

Opção Descrição
auto_now_add Defina a propriedade para a data/hora atual quando a entidade for criada. Pode substituir manualmente esta propriedade. Quando a entidade é atualizada, a propriedade não é alterada. Para esse comportamento, use auto_now.
auto_now Defina a propriedade para a data/hora atual quando a entidade é criada e sempre que é atualizada.

Não é possível combinar estas opções com repeated=True. Ambos têm como predefinição False; se ambos estiverem definidos como True, auto_now tem prioridade. É possível substituir o valor de uma propriedade com auto_now_add=True, mas não de uma com auto_now=True. O valor automático não é gerado até que a entidade seja escrita; ou seja, estas opções não fornecem predefinições dinâmicas. (Estes detalhes diferem da antiga API db.)

Nota: Quando uma transação que escreve uma propriedade com auto_now_add=True falha e é tentada novamente mais tarde, reutiliza o mesmo valor de tempo da tentativa original, em vez de o atualizar para o momento da nova tentativa. Se a transação falhar permanentemente, o valor da propriedade continua definido na cópia da entidade na memória.

Propriedades estruturadas

Pode estruturar as propriedades de um modelo. Por exemplo, pode definir uma classe de modelo Contact que contenha uma lista de moradas, cada uma com uma estrutura interna. As propriedades estruturadas (tipo StructuredProperty) permitem-lhe fazê-lo. Por exemplo:

class Address(ndb.Model):
    type = ndb.StringProperty()  # E.g., 'home', 'work'
    street = ndb.StringProperty()
    city = ndb.StringProperty()
...
class Contact(ndb.Model):
    name = ndb.StringProperty()
    addresses = ndb.StructuredProperty(Address, repeated=True)
...
guido = Contact(
    name='Guido',
    addresses=[
        Address(
            type='home',
            city='Amsterdam'),
        Address(
            type='work',
            street='Spear St',
            city='SF')])

guido.put()

Isto cria uma única entidade do Datastore com as seguintes propriedades:

assert guido.name == 'Guido'
addresses = guido.addresses
assert addresses[0].type == 'home'
assert addresses[1].type == 'work'
assert addresses[0].street is None
assert addresses[1].street == 'Spear St'
assert addresses[0].city == 'Amsterdam'
assert addresses[1].city == 'SF'

A leitura de uma entidade deste tipo reconstrói exatamente a entidade Contact original. Embora as instâncias Address sejam definidas com a mesma sintaxe que para as classes de modelos, não são entidades completas. Não têm as suas próprias chaves no Datastore. Não podem ser obtidos independentemente da entidade Contact à qual pertencem. No entanto, uma aplicação pode consultar os valores dos respetivos campos individuais. Consulte a secção Filtrar valores de propriedades estruturadas. Tenha em atenção que address.type, address.street e address.city são vistos como matrizes paralelas do ponto de vista do Datastore, mas a biblioteca NDB oculta este aspeto e cria a lista correspondente de instâncias Address.

Pode especificar as opções de propriedade habituais para propriedades estruturadas (exceto indexed). O nome do Datastore é o segundo argumento posicional neste caso (o primeiro é a classe de modelo usada para definir a subestrutura).

Quando não precisa de consultar as propriedades internas de uma subestrutura, pode usar uma propriedade estruturada local (LocalStructuredProperty). Se substituir StructuredProperty por LocalStructuredProperty no exemplo acima, o comportamento do código Python é o mesmo, mas o Datastore vê apenas um blob opaco para cada endereço. A entidade guido criada no exemplo seria armazenada da seguinte forma: name = "Guido" address = <opaque blob for {'type': 'home', 'city': 'Amsterdam'}> address = <opaque blob for {'type': 'work', 'city': 'SF', 'street': 'Spear St'}>

A entidade é lida corretamente. Uma vez que as propriedades deste tipo estão sempre não indexadas, não pode consultar valores de morada.

Nota: Um StructuredProperty com uma propriedade aninhada (estruturada ou não) suporta apenas uma camada de propriedades repetidas. O StructuredProperty pode ser repetido ou a propriedade aninhada pode ser repetida, mas não ambos. Uma solução alternativa é usar LocalStructuredProperty, que não tem esta restrição (mas não permite consultas sobre os valores das respetivas propriedades).

Propriedades calculadas

As propriedades calculadas (ComputedProperty) são propriedades de leitura cujo valor é calculado a partir de outros valores de propriedades por uma função fornecida pela aplicação. Tenha em atenção que uma propriedade calculada só suporta os tipos suportados por propriedades genéricas. O valor calculado é escrito no Datastore para que possa ser consultado e apresentado no visualizador do Datastore, mas o valor armazenado é ignorado quando a entidade é lida novamente a partir do Datastore. Em vez disso, o valor é recalculado chamando a função sempre que o valor é pedido. Por exemplo:

class SomeEntity(ndb.Model):
    name = ndb.StringProperty()
    name_lower = ndb.ComputedProperty(lambda self: self.name.lower())
...
entity = SomeEntity(name='Nick')
entity.put()

Isto armazena uma entidade com os seguintes valores de propriedades:

assert entity.name == 'Nick'
assert entity.name_lower == 'nick'

Se alterarmos o nome para "Nickie" e pedirmos o valor de name_lower, é devolvido "nickie":

entity.name = 'Nick'
assert entity.name_lower == 'nick'
entity.name = 'Nickie'
assert entity.name_lower == 'nickie'

Nota: Use ComputedProperty se a aplicação consultar o valor calculado. Se apenas quiser usar a versão derivada no código Python, defina um método normal ou use a função integrada @property do Python.

Nota: se criar um modelo sem uma chave especificada manualmente e, em vez disso, confiar no Datastore para gerar automaticamente o ID da entidade, na primeira put(), um ComputedProperty não vai conseguir ler o campo de ID, uma vez que o campo é calculado antes de o ID ser gerado. Se precisar de um ComputedProperty que use o ID da entidade, pode usar o método allocate_ids para gerar um ID e uma chave para criar a entidade, de modo que o seu ComputedProperty possa referenciar esse ID no primeiro put() da entidade.

Propriedades de mensagens RPC do protocolo Google

A biblioteca Google Protocol RPC usa objetos Message para dados estruturados. Estes podem representar pedidos RPC, respostas ou outras coisas. O NDB fornece uma API para armazenar objetos Message Google Protocol RPC Message como propriedades de entidades. Suponhamos que define uma subclasse Message:

from protorpc import messages
...
class Note(messages.Message):
    text = messages.StringField(1, required=True)
    when = messages.IntegerField(2)

Pode armazenar objetos Note no Datastore como valores de propriedades de entidades usando a API msgprop do NDB.

from google.appengine.ext import ndb
from google.appengine.ext.ndb import msgprop
...
class NoteStore(ndb.Model):
    note = msgprop.MessageProperty(Note, indexed_fields=['when'])
    name = ndb.StringProperty()
...
my_note = Note(text='Excellent note', when=50)

ns = NoteStore(note=my_note, name='excellent')
key = ns.put()

new_notes = NoteStore.query(NoteStore.note.when >= 10).fetch()

Se quiser consultar nomes de campos, estes têm de ser indexados. Pode especificar uma lista de nomes de campos que vão ser indexados com o parâmetro indexed_fields para MessageProperty.

MessageProperty suporta muitas, mas não todas, as opções de propriedades. Suporta:

  • name
  • repeated
  • required
  • default
  • choices
  • validator
  • verbose_name

As propriedades de mensagens não suportam a opção de propriedade indexed; não pode indexar valores Message. (Pode indexar campos de uma mensagem conforme descrito acima.)

As mensagens aninhadas (com MessageField) também funcionam:

class Notebook(messages.Message):
    notes = messages.MessageField(Note, 1, repeated=True)
...
class SignedStorableNotebook(ndb.Model):
    author = ndb.StringProperty()
    nb = msgprop.MessageProperty(
        Notebook, indexed_fields=['notes.text', 'notes.when'])

MessageProperty tem uma opção de propriedade especial, protocol, que especifica como o objeto de mensagem é serializado para o Datastore. Os valores são nomes de protocolos usados pela classe protorpc.remote.Protocols. Os nomes dos protocolos suportados são protobuf e protojson. O protocolo predefinido é protobuf.

msgprop também define EnumProperty, um tipo de propriedade que pode ser usado para armazenar um valor protorpc.messages.Enum numa entidade. Exemplo:

class Color(messages.Enum):
    RED = 620
    GREEN = 495
    BLUE = 450
...
class Part(ndb.Model):
    name = ndb.StringProperty()
    color = msgprop.EnumProperty(Color, required=True)
...
p1 = Part(name='foo', color=Color.RED)
print p1.color  # prints "RED"

EnumProperty armazena o valor como um número inteiro. Na verdade, EnumProperty é uma subclasse de IntegerProperty. Isto implica que pode mudar o nome dos valores de enumeração sem ter de modificar as entidades já armazenadas, mas não pode reenumerá-los.

A EnumProperty suporta as seguintes opções de propriedade:

  • name
  • indexed
  • repeated
  • required
  • default
  • choices
  • validator
  • verbose_name

Acerca dos modelos de entidades NDB

Um modelo de entidade NDB pode definir propriedades. As propriedades das entidades são um pouco semelhantes aos membros de dados das classes Python, uma forma estruturada de armazenar dados. Também são um pouco semelhantes aos campos num esquema de base de dados.

Uma aplicação típica define um modelo de dados definindo uma classe que herda de Model com alguns atributos de classe de propriedade. Por exemplo,


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

Aqui, username, userid e email são propriedades de Account.

Existem vários outros tipos de propriedades. Alguns são úteis para representar datas e horas e têm funcionalidades de atualização automática convenientes.

Uma aplicação pode ajustar o comportamento de uma propriedade especificando opções na propriedade. Estas podem facilitar a validação, definir predefinições ou alterar a indexação de consultas.

Um modelo pode ter propriedades mais complexas. As propriedades repetidas são semelhantes a listas. As propriedades estruturadas são semelhantes a objetos. As propriedades calculadas só de leitura são definidas através de funções, o que facilita a definição de uma propriedade em termos de uma ou mais outras propriedades. Os modelos Expando podem definir propriedades dinamicamente.