Referência de propriedade da entidade

O Firestore no modo Datastore (Datastore) é compatível com vários tipos de dados para valores de propriedade. Estes são alguns deles:

  • Números inteiros
  • Números de ponto flutuante
  • Strings
  • Datas
  • dados binários

Para uma lista completa de tipos, consulte Propriedades e tipos de valor.

Propriedades e tipos de valor

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

Estes são os tipos de valor compatíveis:

Quando uma consulta envolve um campo com valores de tipos mistos, o Firestore usa uma ordem determinista 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 ponto flutuante
  6. Chaves do armazenamento de dados

Como strings de texto e de bytes longas não são indexadas, elas não têm uma ordem definida.

Tipos de propriedade

O NDB aceita os seguintes tipos de propriedade:

Tipo de propriedadeDescrição
IntegerProperty Inteiro assinado de 64 bits
FloatProperty Número de ponto flutuante de precisão dupla
BooleanProperty Booleano
StringProperty String Unicode, até 1.500 bytes indexados
TextProperty String Unicode, comprimento ilimitado, não indexado
BlobProperty String de bytes não interpretada:
se você definir indexed=True, até 1.500 bytes, indexado;
se indexed for False (o padrão), comprimento ilimitado, não indexado.
Argumento de palavra-chave opcional: compressed.
DateTimeProperty Data e hora (consulte Propriedades de data e hora)
DateProperty Data (consulte Propriedades de data e hora)
TimeProperty Hora (consulte Propriedades de data e hora)
GeoPtProperty Localização geográfica Este é um objeto ndb.GeoPt. O objeto tem atributos lat e lon, ambos flutuantes. Crie um com dois flutuantes como ndb.GeoPt(52.37, 4.88) ou com uma string ndb.GeoPt("52.37, 4.88"). Na verdade, essa é a mesma classe de db.GeoPt.
KeyProperty Chave do Datastore
Argumento de palavra-chave opcional: kind=kind, para exigir que chaves atribuídas a essa propriedade sempre tenham o tipo indicado. Pode ser uma string ou uma subclasse Model.
BlobKeyProperty Chave do Blobstore
Corresponde a BlobReferenceProperty na API db anterior, mas o valor da propriedade é BlobKey em vez de uma BlobInfo, é possível criar um BlobInfo com base nele usando BlobInfo(blobkey)
UserProperty Objeto do usuário
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 Value é um objeto do Python (como uma lista ou um dict ou uma string) serializável usando-se o módulo json do Python. O Datastore armazena a serialização JSON como um blob. Não indexado por padrão.
Argumento de palavra-chave opcional: compressed.
PickleProperty Value é um objeto do Python (como uma lista ou um dict ou uma string) serializável usando-se o protocolo de seleção do Python serializável. O Datastore armazena a serialização de seleção como um blob. Não indexado por padrão.
Argumento de palavra-chave opcional: compressed.
GenericProperty Valor genérico
mais usada pela classe Expando, mas também utilizável de maneira explícita. O tipo pode ser qualquer int, long, float, bool, str, unicode, datetime, Key, BlobKey, GeoPt, User, None.
ComputedProperty Valor computado com base em outras propriedades por uma função definida pelo usuário. Consulte Propriedades computadas.

Algumas propriedades têm um argumento de palavra-chave opcional, compressed. Caso a propriedade tenha compressed=True, os dados dela são compactados com gzip em disco. Ele ocupa menos espaço, mas precisa de CPU para codificar/decodificar em operações de gravação e leitura.

A compactação e a descompactação são "vagarosas". Um valor de propriedade compactado só será descompactado na primeira vez em que você acessá-lo. Se você ler uma entidade que contiver um valor de propriedade compactado e reescrevê-lo sem acessar a propriedade compactada, ela não será descompactada e compactada. O cache no contexto também participa desse esquema vagaroso, mas o memcache sempre armazena o valor compactado para propriedades compactadas.

Por causa do tempo de CPU extra necessário para compactação, normalmente seria melhor usar apenas propriedades compactadas se os dados fossem grandes demais para caber nelas. Lembre-se de que a compactação baseada em gzip não costuma ser eficiente para imagens e outros dados de mídia, porque esses formatos já estão compactados usando-se um algoritmo de compactação específico de mídia (por exemplo, JPEG para imagens).

Opções de propriedade

A maioria dos tipos de propriedade aceita alguns argumentos padrão. O primeiro é um argumento posicional opcional que especifica o nome do Datastore da propriedade. Você pode usá-lo para dar à propriedade um nome diferente no Datastore do que o aquele usado do ponto de vista do aplicativo. Um uso comum para isso é reduzir o espaço no Datastore, permitindo que ele use nomes de propriedade abreviados enquanto o código usa nomes mais longos e significativos. Por exemplo:

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

Isso é especialmente útil para propriedades repetidas para as quais você espera muitos valores por entidade.

Além disso, a maioria dos tipos de propriedade aceita os seguintes argumentos de palavra-chave:

ArgumentoTipoPadrãoDescrição
indexed bool Normalmente True Inclua a propriedade nos índices do Datastore. Caso False, os valores não podem ser consultados, mas as gravações são mais rápidas. Nem todos os tipos de propriedade aceitam indexação. A configuração de indexed como True falha para eles.
As propriedades não indexadas custam menos operações de gravação do que as propriedades indexadas.
repeated bool False O valor da propriedade é uma lista do Python que contém valores do tipo subjacente (consulte Propriedades repetidas).
Não pode ser combinado com required=True ou default=True.
required bool False A propriedade precisa ter um valor especificado.
default Tipo subjacente da propriedadeNoneValor padrão da propriedade, caso nenhum tenha sido especificado explicitamente.
choices Lista de valores de tipo subjacenteNone Lista opcional de valores permitidos.
validator FunçãoNone

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

Será chamada com argumentos (prop, value) e precisará retornar o valor (possivelmente forçado) ou gerar uma exceção. Chamar a função novamente em um valor forçado não precisa modificar o valor ainda mais. Por exemplo, retornar value.strip() ou value.lower() é aceitável, mas não value + '$'. Também pode retornar None, o que significa "sem alterações". Consulte também Como escrever subclasses de propriedade

verbose_name stringNone

Rótulo HTML opcional a ser usado em bibliotecas de formulário da Web como jinja2.

Propriedades repetidas

Qualquer propriedade com repeated=True torna-se uma propriedade repetida. A propriedade utiliza 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. Um registro de índice separado é criado para cada valor. Isso afeta a semântica de consulta. Consulte Como 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()

Isso cria uma entidade do Datastore com o seguinte conteúdo:

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

Ao consultar a propriedade tags, essa entidade atenderá a uma consulta para 'python' ou 'ruby'.

Ao atualizar uma propriedade repetida, você poderá atribuir a ela uma nova lista ou alterar a lista existente. Quando você atribui uma nova lista, os tipos dos itens de lista são validados imediatamente. Os tipos de item inválidos (por exemplo, atribuição de [1, 2] a art.tags acima) geram uma exceção. Quando você altera a lista, a mudança não é validada imediatamente. Em vez disso, o valor será validado quando você gravar a entidade no Datastore.

O Datastore preserva a ordem dos itens da lista em uma propriedade repetida. Dessa maneira, você atribui um significado à ordem.

Propriedades de data e hora

Três tipos de propriedade estão disponíveis para armazenar valores relacionados a data e hora:

  • DateProperty
  • TimeProperty
  • DateTimeProperty

Eles utilizam valores pertencentes às classes correspondentes (date, time, datetime) do módulo datetime do Python padrão. O mais geral dos três é DateTimeProperty, que denota uma data do calendário e uma hora do dia. Às vezes, outros são úteis para fins especiais que exigem apenas uma data (como uma data de nascimento) ou apenas uma hora (como um horário de reunião). Por motivos técnicos, DateProperty e TimeProperty são subclasses de DateTimeProperty, mas você não precisa depender desse relacionamento de herança (e observe que ele difere dos relacionamentos de herança entre as classes subjacentes definidas pelo próprio módulo datetime).

Observação: as horas do App Engine são sempre expressas na Hora universal coordenada (UTC, na sigla em inglês). Isso ganha relevância caso você use a data ou a hora atual (datetime.datetime.now()) como um valor ou converta entre objetos de data e hora e carimbos de data/hora POSIX ou tuplas de hora. No entanto, nenhuma informação de fuso horário explícito é armazenada no Datastore. Dessa maneira, caso tenha cuidado, você pode usá-la para representar horas locais em qualquer fuso horário, caso você use a hora atual ou as conversões.

Cada uma dessas propriedades tem duas opções de palavra-chave booleanas extras:

OpçãoDescrição
auto_now_add Defina a propriedade como data/hora atual quando a entidade é criada. Substitua manualmente essa propriedade. Quando a entidade é atualizada, a propriedade não é alterada. Para esse comportamento, use auto_now.
auto_now Defina a propriedade como data/hora atual quando a entidade é criada e sempre que ela é atualizada.

Essas opções não podem ser combinadas com repeated=True. As duas assumem como padrão False. Se ambas estiverem configuradas como True, auto_now terá precedência. É possível modificar o valor de uma propriedade com auto_now_add=True, mas não para um com auto_now=True. O valor automático não será gerado até a entidade ser gravada, ou seja, essas opções não fornecem padrões dinâmicos. Esses detalhes são diferentes da db API anterior.

Observação: quando uma transação que grava uma propriedade com auto_now_add=True falhar e for repetida posteriormente, ela reutilizará o mesmo valor de hora da original, em vez de atualizá-la para a hora da nova tentativa. Se a transação falhar permanentemente, o valor da propriedade ainda será definido na cópia da entidade na memória.

Propriedades estruturadas

Estruture as propriedades de um modelo. Por exemplo, defina uma classe de modelo Contact que contém uma lista de endereços, cada um com estrutura interna. Propriedades estruturadas, aquelas do tipo StructuredProperty, permitem fazer isso. 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()

Isso 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 desse tipo recria a entidade original Contact exatamente. As instâncias de Address são definidas usando-se a mesma sintaxe das classes de modelo, mas elas não são entidades completas. Elas não têm as próprias chaves no Datastore. Elas não podem ser recuperadas independentemente da entidade Contact à qual pertencem. Porém, um aplicativo pode consultar os valores dos campos individuais. Consulte Como filtrar valores de propriedade estruturada. Observe que address.type, address.street e address.city são exibidos como matrizes paralelas do ponto de vista do Datastore, mas a biblioteca NDB oculta esse aspecto e cria a lista correspondente de instâncias Address.

É possível especificar as opções de propriedade comuns para propriedades estruturadas (exceto indexed). O nome do Datastore é o segundo argumento posicional nesse caso. O primeiro é a classe de modelo usada para definir a subestrutura.

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

A entidade será lida corretamente. Como propriedades desse tipo nunca são indexadas, não é possível consultar valores de endereço.

Observação: uma StructuredProperty com uma propriedade aninhada (independentemente de ser estruturada ou não) só aceita uma única camada de propriedades repetidas. A StructuredProperty pode ser repetida, ou a propriedade aninhada pode ser repetida, mas não ambas. Uma solução alternativa é usar LocalStructuredProperty, que não tem essa restrição (mas não permite consultas em valores da propriedade).

Propriedades computadas

Propriedades computadas (ComputedProperty) são propriedades somente leitura com um valor computado com base em outros valores de propriedade por uma função fornecida pelo aplicativo. Uma propriedade computada só aceita os tipos compatíveis por propriedades genéricas. O valor computado é gravado no Datastore. Dessa maneira, ele pode ser consultado e exibido no visualizador do Datastore, mas o valor armazenado é ignorado quando a entidade é lida do Datastore. Em vez disso, o valor é recomputado chamando-se a função sempre que o valor é solicitado. Exemplo:

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

Isso armazena uma entidade com os seguintes valores de propriedade:

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

Se mudarmos o nome para "Nickie" e pedirmos o valor de name_lower, ele retornará "nickie":

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

Observação: use ComputedProperty caso o aplicativo consulte o valor computado. Caso você queira apenas usar a versão derivada no código do Python, defina um método regular ou use a @property interna do Python.

Observação: se você criar um modelo sem uma chave especificada manualmente e, em vez disso, depender do Datastore para gerar automaticamente o ID da entidade, o primeiro put() em um ComputedProperty não poderá ler o campo do ID porque esse campo é computado antes da geração do ID. Se você precisar de um ComputedProperty que use o ID da entidade, use o método allocate_ids para gerar um ID e uma chave para criar a entidade. Assim, o ComputedProperty criará uma referência ao ID no primeiro put() da entidade.

Propriedades da mensagem RPC do protocolo do Google

A biblioteca de RPC do Protocolo do Google usa objetos Message em dados estruturados. Esses objetos podem representar solicitações, respostas ou outros elementos do RPC. O NDB fornece uma API para armazenar objetos Message RPC do protocolo do Google como propriedades da entidade. Suponha que você defina uma subclasse Message:

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

Armazene objetos Note no Datastore como valores de propriedade da entidade 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 você quiser consultar nomes de campo, eles precisarão ser indexados. É possível especificar uma lista de nomes de campos que serão indexados com o parâmetro indexed_fields para MessageProperty.

MessageProperty aceita muitas, mas não todas as opções de propriedade. Ela aceita estas opções:

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

As propriedades de mensagem não aceitam a opção da propriedade indexed. Não é possível indexar valores Message. Você pode indexar campos de uma mensagem conforme descrito acima.

As mensagens aninhadas (usando 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 protocolo usados pela classe protorpc.remote.Protocols. Os nomes de protocolo compatíveis são protobuf e protojson. O padrão é protobuf.

msgprop também define EnumProperty, um tipo de propriedade que pode ser usado para armazenar um valor protorpc.messages.Enum em uma 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 inteiro. Na verdade, EnumProperty é uma subclasse de IntegerProperty. Isso significa que você pode renomear os valores de enum sem precisar modificar entidades já armazenadas, mas não pode renumerá-los.

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

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

Sobre modelos de entidade NDB

Um modelo de entidade NDB pode definir propriedades. As propriedades de entidade são parecidas com membros de dados de classes do Python, uma maneira estruturada de armazenar dados. Elas também são parecidas com campos em um esquema de banco de dados.

Um aplicativo típico define um modelo de dados especificando uma classe herdada de Model com alguns atributos da 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.

Há diversos outros tipos de propriedade. Alguns são úteis para representar datas e horas e têm recursos de atualização automática práticos.

Um aplicativo pode ajustar o comportamento de uma propriedade especificando opções na propriedade. Elas podem facilitar a validação, definir os padrões ou alterar a indexação de consultas.

Um modelo pode ter propriedades mais complexas. As propriedades repetidas são semelhantes a uma lista. As propriedades estruturadas são semelhantes a um objeto. As propriedades computadas somente leitura são definidas por meio de funções. Isso facilita definir uma propriedade em termos de uma ou mais outras propriedades. Os modelos Expando podem definir propriedades de maneira dinâmica.