Nota: gli sviluppatori che creano nuove applicazioni sono vivamente incoraggiati a utilizzare la libreria client NDB, che offre diversi vantaggi rispetto a questa libreria client, ad esempio la memorizzazione nella cache automatica delle entità tramite l'API Memcache. Se al momento utilizzi la libreria client DB precedente, leggi la guida alla migrazione da DB a NDB
Panoramica
Un'entità di Datastore ha una chiave e un insieme di proprietà. Un'applicazione utilizza l'API DataStore per definire i modelli di dati e creare istanze di questi modelli da archiviare come entità. I modelli forniscono una struttura comune alle entità create dall'API e possono definire regole per la convalida dei valori delle proprietà.
Classi modello
La classe Model
Un'applicazione descrive i tipi di dati che utilizza con i modelli. Un modello è una classe Python che eredita dalla classe Model. La classe del modello definisce un nuovo tipo di entità del datastore e le proprietà che deve avere. Il nome del tipo è definito dal nome della classe istanziata che eredita da db.Model
.
Le proprietà del modello vengono definite utilizzando gli attributi della classe nella classe del modello. Ogni attributo della classe è un'istanza di una sottoclasse della classe Property, in genere una delle classi di proprietà fornite. Un'istanza di proprietà contiene la configurazione della proprietà, ad esempio se la proprietà è obbligatoria per la validità dell'istanza o un valore predefinito da utilizzare per l'istanza, se non ne viene fornito nessuno.
from google.appengine.ext import db class Pet(db.Model): name = db.StringProperty(required=True) type = db.StringProperty(required=True, choices=set(["cat", "dog", "bird"])) birthdate = db.DateProperty() weight_in_pounds = db.IntegerProperty() spayed_or_neutered = db.BooleanProperty()
Un'entità di uno dei tipi di entità definiti è rappresentata nell'API da un'istanza della classe del modello corrispondente. L'applicazione può creare una nuova entità chiamando il costruttore della classe. L'applicazione accede e manipola le proprietà dell'entità utilizzando gli attributi dell'istanza. Il costruttore dell'istanza del modello accetta i valori iniziali per le proprietà come argomenti delle parole chiave.
from google.appengine.api import users pet = Pet(name="Fluffy", type="cat") pet.weight_in_pounds = 24
Nota:gli attributi della classe del modello sono la configurazione per le proprietà del modello, i cui valori sono istanze di Property. Gli attributi dell'istanza del modello sono i valori effettivi delle proprietà, i cui valori sono del tipo accettato dalla classe Property.
La classe Model utilizza le istanze Property per convalidare i valori assegnati agli attributi dell'istanza del modello. La convalida del valore della proprietà avviene quando viene creata per la prima volta un'istanza del modello e quando a un attributo dell'istanza viene assegnato un nuovo valore. In questo modo, una proprietà non può mai avere un valore non valido.
Poiché la convalida avviene al momento della creazione dell'istanza, qualsiasi proprietà configurata come obbligatoria deve essere inizializzata nel costruttore. In questo esempio, name
e type
sono valori obbligatori, pertanto i relativi valori iniziali sono specificati nel costruttore. weight_in_pounds
non è richiesto dal modello, quindi inizialmente non è assegnato, ma in un secondo momento viene assegnato un valore.
Un'istanza di un modello creato utilizzando il costruttore non esiste nel datastore finché non viene "inserita" per la prima volta.
Nota:come per tutti gli attributi della classe Python, la configurazione delle proprietà del modello viene inizializzata al primo importazione dello script o del modulo. Poiché App Engine memorizza nella cache i moduli importati tra una richiesta e l'altra, la configurazione del modulo può essere inizializzata durante una richiesta per un utente e riutilizzata durante una richiesta per un altro. Non inizializzare la configurazione delle proprietà del modello, ad esempio i valori predefiniti, con dati specifici per la richiesta o per l'utente corrente. Per saperne di più, consulta Memorizzazione nella cache delle app.
The Expando Class
Un modello definito utilizzando la classe Model stabilisce un insieme fisso di proprietà che ogni istanza della classe deve avere (forse con valori predefiniti). Si tratta di un modo utile per modellare gli oggetti dati, ma il data store non richiede che ogni entità di un determinato tipo abbia lo stesso insieme di proprietà.
A volte è utile che un'entità abbia proprietà che non sono necessariamente simili a quelle di altre entità dello stesso tipo. Questa entità è rappresentata nell'API Datastore da un modello "expando". Una classe di modello expando è una sottoclasse della superclasse Expando. Qualsiasi valore assegnato a un attributo di un'istanza di un modello expando diventa una proprietà dell'entità del datastore, utilizzando il nome dell'attributo. Queste proprietà sono note come proprietà dinamiche. Le proprietà definite utilizzando istanze di classe Property negli attributi di classe sono proprietà fisse.
Un modello expando può avere proprietà sia fisse che dinamiche. La classe del modello imposta semplicemente gli attributi della classe con oggetti di configurazione della proprietà per le proprietà fisse. L'applicazione crea proprietà dinamiche quando assegna loro valori.
class Person(db.Expando): first_name = db.StringProperty() last_name = db.StringProperty() hobbies = db.StringListProperty() p = Person(first_name="Albert", last_name="Johnson") p.hobbies = ["chess", "travel"] p.chess_elo_rating = 1350 p.travel_countries_visited = ["Spain", "Italy", "USA", "Brazil"] p.travel_trip_count = 13
Poiché le proprietà dinamiche non hanno definizioni di proprietà del modello, non vengono convalidate. Qualsiasi proprietà dinamica può avere un valore di uno dei tipi di base del data store, incluso None
. Due entità dello stesso tipo possono avere tipi di valori diversi per la stessa proprietà dinamica e una può lasciare una proprietà non impostata che l'altra imposta.
A differenza delle proprietà fisse, le proprietà dinamiche non devono necessariamente esistere. Una proprietà dinamica con un valore None
è diversa da una proprietà dinamica inesistente. Se un'istanza di modello expando non ha un attributo per una proprietà, l'entità di dati corrispondente non ha quella proprietà. Puoi eliminare una proprietà dinamica eliminando l'attributo.
Gli attributi i cui nomi iniziano con un trattino basso (_
) non vengono salvati nell'entità del datastore. In questo modo, puoi archiviare i valori nell'istanza del modello per uso interno temporaneo senza influire sui dati salvati con l'entità.
Nota: le proprietà statiche verranno sempre salvate nell'entità del datastore, indipendentemente dal fatto che si tratti di Expando, Model o che inizino con un'underscore (_
).
del p.chess_elo_rating
Una query che utilizza una proprietà dinamica in un filtro restituisce solo le entità il cui valore per la proprietà è dello stesso tipo del valore utilizzato nella query. Analogamente, la query restituisce solo le entità con quella proprietà impostata.
p1 = Person() p1.favorite = 42 p1.put() p2 = Person() p2.favorite = "blue" p2.put() p3 = Person() p3.put() people = db.GqlQuery("SELECT * FROM Person WHERE favorite < :1", 50) # people has p1, but not p2 or p3 people = db.GqlQuery("SELECT * FROM Person WHERE favorite > :1", 50) # people has no results
Nota:l'esempio riportato sopra utilizza query su gruppi di entità, che potrebbero restituire risultati obsoleti. Per risultati fortemente coerenti, utilizza le query sugli antenati all'interno dei gruppi di entità.
La classe Expando è una sottoclasse della classe Model e ne eredita tutti i metodi.
La classe PolyModel
L'API Python include un'altra classe per la definizione dei modelli di dati che consente di definire gerarchie di classi ed eseguire query che possono restituire entità di una determinata classe o di una delle sue sottoclassi. Questi modelli e query sono chiamati "polimorfi" perché consentono alle istanze di una classe di essere risultati per una query di una classe principale.
L'esempio seguente definisce una classe Contact
con le sottoclassi Person
e Company
:
from google.appengine.ext import db from google.appengine.ext.db import polymodel class Contact(polymodel.PolyModel): phone_number = db.PhoneNumberProperty() address = db.PostalAddressProperty() class Person(Contact): first_name = db.StringProperty() last_name = db.StringProperty() mobile_number = db.PhoneNumberProperty() class Company(Contact): name = db.StringProperty() fax_number = db.PhoneNumberProperty()
Questo modello garantisce che tutte le entità Person
e tutte le entità Company
abbiano proprietà phone_number
e address
e che le query per le entità Contact
possano restituire entità Person
o Company
. Solo le entità Person
hanno proprietà mobile_number
.
Le sottoclassi possono essere create come qualsiasi altra classe di modello:
p = Person(phone_number='1-206-555-9234', address='123 First Ave., Seattle, WA, 98101', first_name='Alfred', last_name='Smith', mobile_number='1-206-555-0117') p.put() c = Company(phone_number='1-503-555-9123', address='P.O. Box 98765, Salem, OR, 97301', name='Data Solutions, LLC', fax_number='1-503-555-6622') c.put()
Una query per entità Contact
può restituire istanze di Contact
, Person
o Company
. Il seguente codice stampa le informazioni per entrambe le entità create sopra:
for contact in Contact.all(): print 'Phone: %s\nAddress: %s\n\n' % (contact.phone_number, contact.address)
Una query per le entità Company
restituisce solo istanze di Company
:
for company in Company.all() # ...
Per il momento, i modelli polimorfici non devono essere passati direttamente al costruttore della classe Query. Utilizza invece il metodo all()
, come nell'esempio riportato sopra.
Per ulteriori informazioni su come utilizzare i modelli polimorfici e su come vengono implementati, consulta la classe PolyModel.
Classi e tipi di proprietà
Il data store supporta un insieme fisso di tipi di valori per le proprietà delle entità, tra cui stringhe Unicode, numeri interi, numeri in virgola mobile, date, chiavi di entità, stringhe di byte (blob) e vari tipi di GData. Ciascuno dei tipi di valore di Datastore ha una classe Property corrispondente fornita dal modulo google.appengine.ext.db
.
Tipi e classi di proprietà descrive tutti i tipi di valori supportati e le relative classi di proprietà. Di seguito sono descritti diversi tipi di valori speciali.
Stringhe e blob
Datastore supporta due tipi di valori per l'archiviazione di testo: stringhe di testo brevi con una lunghezza massima di 1500 byte e stringhe di testo lunghe con una lunghezza massima di un megabyte. Le stringhe brevi vengono indicizzate e possono essere utilizzate nelle condizioni di filtro delle query e negli ordini di ordinamento. Le stringhe lunghe non vengono indicizzate e non possono essere utilizzate nelle condizioni dei filtri o negli ordini di ordinamento.
Un valore di stringa breve può essere un valore unicode
o un valore str
. Se il valore è str
, si presume una codifica di 'ascii'
. Per specificare una codifica diversa per un valore str
, puoi convertirlo in un valore unicode
con il costruttore di tipo unicode()
, che accetta str
e il nome della codifica come argomenti. Le stringhe brevi possono essere modellate utilizzando la classe StringProperty.
class MyModel(db.Model): string = db.StringProperty() obj = MyModel() # Python Unicode literal syntax fully describes characters in a text string. obj.string = u"kittens" # unicode() converts a byte string to a Unicode string using the named codec. obj.string = unicode("kittens", "latin-1") # A byte string is assumed to be text encoded as ASCII (the 'ascii' codec). obj.string = "kittens" # Short string properties can be used in query filters. results = db.GqlQuery("SELECT * FROM MyModel WHERE string = :1", u"kittens")
Un valore di stringa lungo è rappresentato da un'istanza db.Text. Il relativo costruttore accetta un valore unicode
o str
e, facoltativamente, il nome della codifica utilizzata in str
. Le stringhe lunghe possono essere modellate utilizzando la classe TextProperty.
class MyModel(db.Model): text = db.TextProperty() obj = MyModel() # Text() can take a Unicode string. obj.text = u"lots of kittens" # Text() can take a byte string and the name of an encoding. obj.text = db.Text("lots of kittens", "latin-1") # If no encoding is specified, a byte string is assumed to be ASCII text. obj.text = "lots of kittens" # Text properties can store large values. obj.text = db.Text(open("a_tale_of_two_cities.txt").read(), "utf-8")
Il datastore supporta anche due tipi simili per le stringhe di byte non di testo: db.ByteString e db.Blob. Questi valori sono stringhe di byte non elaborati e non vengono trattati come testo codificato (ad esempio UTF-8).
Come i valori db.StringProperty
, anche i valori db.ByteString
sono indicizzati. Come per le proprietà db.TextProperty
, i valori db.ByteString
sono limitati a 1500 byte. Un'istanza ByteString
rappresenta una breve stringa di byte e accetta un valore str
come argomento del relativo costruttore. Le stringhe di byte vengono modellate utilizzando la classe ByteStringProperty.
Come db.Text
, un valore db.Blob
può avere una dimensione massima di 1 megabyte, ma non è indicizzato e non può essere utilizzato nei filtri delle query o negli ordini di ordinamento. La classe db.Blob
accetta un valore str
come argomento del suo costruttore oppure puoi assegnare il valore direttamente. I blob vengono modellati utilizzando la classe BlobProperty.
class MyModel(db.Model): blob = db.BlobProperty() obj = MyModel() obj.blob = open("image.png").read()
Elenchi
Una proprietà può avere più valori, rappresentati nell'API datastore come un list
di Python. L'elenco può contenere valori di qualsiasi tipo supportato dal data store. Una singola proprietà elenco può anche avere valori di tipi diversi.
L'ordine viene generalmente mantenuto, quindi quando le entità vengono restituite dalle query e da get(), i valori delle proprietà dell'elenco sono nello stesso ordine in cui sono stati archiviati. Esiste un'eccezione: i valori Blob
e Text
vengono spostati alla fine dell'elenco, ma mantengono il loro ordine originale rispetto all'altro.
La classe ListProperty modella un elenco e impone che tutti i valori dell'elenco siano di un determinato tipo. Per comodità, la libreria fornisce anche StringListProperty, simile a ListProperty(basestring)
.
class MyModel(db.Model): numbers = db.ListProperty(long) obj = MyModel() obj.numbers = [2, 4, 6, 8, 10] obj.numbers = ["hello"] # ERROR: MyModel.numbers must be a list of longs.
Una query con filtri su una proprietà elenco testa ciascun valore dell'elenco individualmente. L'entità corrisponderà alla query solo se un valore nell'elenco supera tutti i filtri per quella proprietà. Per ulteriori informazioni, consulta la pagina Query Datastore.
# Get all entities where numbers contains a 6. results = db.GqlQuery("SELECT * FROM MyModel WHERE numbers = 6") # Get all entities where numbers contains at least one element less than 10. results = db.GqlQuery("SELECT * FROM MyModel WHERE numbers < 10")
I filtri delle query agiscono solo sui membri dell'elenco. Non è possibile verificare la somiglianza di due elenchi in un filtro di query.
All'interno, il data store rappresenta un valore della proprietà elenco come più valori per la proprietà. Se il valore di una proprietà elenco è l'elenco vuoto, la proprietà non ha alcuna rappresentazione nel data store. L'API Datastore tratta questa situazione in modo diverso per le proprietà statiche (con ListProperty) e quelle dinamiche:
- A una proprietà List statica può essere assegnato l'elenco vuoto come valore. La proprietà non esiste nel datastore, ma l'istanza del modello si comporta come se il valore fosse l'elenco vuoto. Una proprietà ListProperty statica non può avere un valore
None
. - A una proprietà dinamica con un valore
list
non è possibile assegnare un valore di elenco vuoto. Tuttavia, può avere un valoreNone
e può essere eliminato (utilizzandodel
).
Il modello ListProperty verifica che un valore aggiunto all'elenco sia del tipo corretto e genera un errore BadValueError se non lo è. Questo test viene eseguito (e potenzialmente non va a buon fine) anche quando un'entità memorizzata in precedenza viene recuperata e caricata nel modello. Poiché i valori str
vengono convertiti in valori unicode
(come testo ASCII) prima dell'archiviazione, ListProperty(str)
viene trattato come ListProperty(basestring)
, il tipo di dati Python che accetta sia i valori str
che unicode
. Per farlo, puoi utilizzare anche StringListProperty()
.
Per archiviare stringhe di byte non di testo, utilizza i valori db.Blob. I byte di una stringa BLOB vengono conservati quando vengono archiviati e recuperati. Puoi dichiarare una proprietà che è un elenco di blob come ListProperty(db.Blob)
.
Le proprietà di elenco possono interagire con gli ordini di ordinamento in modi insoliti. Per maggiori dettagli, consulta la pagina Query Datastore.
Riferimenti
Un valore di proprietà può contenere la chiave di un'altra entità. Il valore è un'istanza Key.
La classe ReferenceProperty modella un valore chiave e impone che tutti i valori facciano riferimento a entità di un determinato tipo. Per comodità, la libreria fornisce anche SelfReferenceProperty, equivalente a una proprietà ReferenceProperty che fa riferimento allo stesso tipo dell'entità con la proprietà.
Se assegni un'istanza di modello a una proprietà ReferenceProperty, la relativa chiave viene utilizzata automaticamente come valore.
class FirstModel(db.Model): prop = db.IntegerProperty() class SecondModel(db.Model): reference = db.ReferenceProperty(FirstModel) obj1 = FirstModel() obj1.prop = 42 obj1.put() obj2 = SecondModel() # A reference value is the key of another entity. obj2.reference = obj1.key() # Assigning a model instance to a property uses the entity's key as the value. obj2.reference = obj1 obj2.put()
Un valore della proprietà ReferenceProperty può essere utilizzato come se fosse l'istanza del modello dell'entità a cui si fa riferimento. Se l'entità a cui si fa riferimento non è in memoria, l'utilizzo della proprietà come istanza recupera automaticamente l'entità dal datastore. Una proprietà ReferenceProperty memorizza anche una chiave, ma il suo utilizzo comporta il caricamento dell'entità correlata.
obj2.reference.prop = 999 obj2.reference.put() results = db.GqlQuery("SELECT * FROM SecondModel") another_obj = results.fetch(1)[0] v = another_obj.reference.prop
Se una chiave rimanda a un'entità inesistente, l'accesso alla proprietà genera un errore. Se un'applicazione prevede che un riferimento possa non essere valido, può verificare l'esistenza dell'oggetto utilizzando un blocco try/except
:
try: obj1 = obj2.reference except db.ReferencePropertyResolveError: # Referenced entity was deleted or never existed.
ReferenceProperty ha un'altra funzionalità utile: i riferimenti inversi. Quando un modello ha una proprietà ReferenceProperty a un altro modello, a ogni entità a cui viene fatto riferimento viene assegnata una proprietà il cui valore è una query che restituisce tutte le entità del primo modello che fanno riferimento a questa.
# To fetch and iterate over every SecondModel entity that refers to the # FirstModel instance obj1: for obj in obj1.secondmodel_set: # ...
Il nome della proprietà di riferimento a ritroso è modelname_set
per impostazione predefinita (con il nome della classe del modello in lettere minuscole e "_set" aggiunto alla fine) e può essere modificato utilizzando l'argomento collection_name
del costruttore ReferenceProperty.
Se hai più valori ReferenceProperty che fanno riferimento alla stessa classe di modello, la costruzione predefinita della proprietà di riferimento a ritroso genera un errore:
class FirstModel(db.Model): prop = db.IntegerProperty() # This class raises a DuplicatePropertyError with the message # "Class Firstmodel already has property secondmodel_set" class SecondModel(db.Model): reference_one = db.ReferenceProperty(FirstModel) reference_two = db.ReferenceProperty(FirstModel)
Per evitare questo errore, devi impostare esplicitamente l'argomento collection_name
:
class FirstModel(db.Model): prop = db.IntegerProperty() # This class runs fine class SecondModel(db.Model): reference_one = db.ReferenceProperty(FirstModel, collection_name="secondmodel_reference_one_set") reference_two = db.ReferenceProperty(FirstModel, collection_name="secondmodel_reference_two_set")
Il riferimento e il dereferenziamento automatici delle istanze del modello, il controllo del tipo e i riferimenti inversi sono disponibili solo utilizzando la classe della proprietà del modello ReferenceProperty. Le chiavi memorizzate come valori delle proprietà dinamiche Expando o dei valori ListProperty non dispongono di queste funzionalità.