Google Code 提供下列語言介面: English - Español - 日本語 - 한국어 - Português - Pусский - 中文(简体) - 中文(繁體)
資料存放區具有金鑰和一組屬性。應用程式使用 Datastore API (資料存放區 API) 來定義資料模型,以及建立這些要儲存為實體之模型的實例。模型提供 API 所建立之實體的常見架構,而且可以定義驗證屬性值的規則。
應用程式描述與模型一起使用的資料種類。模型是一種 Python 類別,它繼承自 Model 類別。模型類別定義資料存放區實體的新種類,以及該種類必須要有的屬性。
Model 屬性使用模型類別上的類別屬性 (attribute) 來定義。每個類別屬性 (attribute) 是 Property 類別之子類別的實例,通常是一個提供的屬性類別。屬性實例帶有屬性的設定 (例如實例是否一定要帶有該屬性才是有效的),或者帶有實例使用的預設值 (若未提供)。
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() owner = db.UserProperty(required=True)
藉由相應模型類別的實體,一種定義之實體種類的實體會呈現在 API 中。 應用程式可以透過類別的建構函式來建立新實體。應用程式會使用實例的屬性 (attribute) 來存取和操作實體的屬性。模型實例建構函式接受屬性的初始值做為 keyword 引數。
from google.appengine.api import users
pet = Pet(name="Fluffy",
type="cat",
owner=users.get_current_user())
pet.weight_in_pounds = 24
注意:模型類別的屬性 (attribute) 是模型屬性的設定,而這些設定的值是 Property 實例。模型實例的屬性 (attribute) 是實際的屬性值,其值是 Property 類別接受的類型。
Model 類別使用 Property 實例來驗證指派給模型實例屬性 (attribute) 的值。模型實例一開始建構時,以及實例屬性 (attribute) 指派新值時,會驗證 Property 值。這樣可以確定屬性不會具有無效值。
因為驗證是發生在建構實例時,任何設定為必要的屬性必須在建構函式中初始化。在此範例中,name、type 以及 owner 都是必要值,所以必須在建構函式中指定其初始值。weight_in_pounds 不是模型所必需,所以一開始的時候未指派,而是稍後才進行指派。
使用建構函式建立的模型實例,要在第一次「放置」之後,才會存在於資料存放區。請參閱建立、取得和刪除資料。
注意:針對所有 Python 類別屬性 (attribute) 而言,模型屬性設定會在指令碼或模型第一次匯入時就初始化。因為「應用服務引擎」快取會在要求之間匯入模組,模組設定可能在某位使用者要求的期間進行初始化,並於另一位使用者要求的期間重複使用。請勿將模組屬性設定 (例如預設值)以特定要求或目前使用者的資料進行初始化。請參閱 應用程式快取以取得詳細資訊。
使用 Model 類別定義的模型,建立一組固定屬性。該類別的每個實例都必須具備此屬性 (可能是預設值)。這對模型資料物件是很有用的方式,但是資料存放區不會要求指定種類的每個實體都具有同一組的屬性。
有時候,實體具有的屬性不必像其他相同種類實體的屬性,反而較為方便。這樣的實體是由「expando」模型呈現在資料存放區 API。expando 模型類別是 Expando 超級類別下的子類別。任何指派給 expando 模型實例的屬性 (attribute) 值,都會使用該屬性 (attribute) 的名稱,成為資料存放區實體的屬性。這些屬性稱為動態屬性。使用類別屬性 (attribute) 中的 Property 類別實例所定義的屬性,則為固定屬性。
expando 模型可以同時具有固定和動態屬性。model 類別只會以固定屬性的 Property 設定物件,來設定類別屬性 (attribute)。當應用程式指派其值時,會建立動態屬性。
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
因為動態屬性不具有模型屬性定義,所以不會驗證動態屬性。任何動態屬性可以具有資料存放區基礎類型任何值,包括 None。相同種類的兩個實體可以具有相同動態屬性的不同類型值,而且一個可以不設定屬性而另一個有設定。
與固定屬性不同的是,動態屬性不需要存在。具有 None 值的動態屬性與不存在的動態屬性不同。若 expando 模型實例的某屬性不具有屬性 (attribute),則相應的資料實體不會具備該屬性。只要刪除屬性 (attribute),就會刪除動態屬性。
del p.chess_elo_rating
在篩選器中使用動態屬性的查詢,只會傳回與查詢中使用相同值類型的屬性值。同樣地,查詢只會傳回已設定該屬性的實體。
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
Expando 類別是 Model 類別的子類別,並繼承其所有方法。
Python API 包括另一種資料模型類別,可讓您定義類別的階層,以及執行可傳回特定類別或其任一子類別之實體的查詢。這種模型查詢稱為「多型態」的原因,是它們允許一種類別的實例成為父系類別查詢的結果
下列範例定義 Contact 類別,以及 Contact 的子類別 Person 與 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()
此模型會確保所有 Person 實體和所有 Company 實體具備 phone_number 和 address 屬性,且查詢 Contact 實體可傳回 Person 或 Company 實體。只有 Person 實體具備 mobile_number 屬性。
子類別和其他模型類別一樣都可以被具現化:
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()
查詢 Contact 實體可傳回 Contact、Person 或 Company 的例項。下列程式碼會列印上方建立之兩個實體的資訊:
for contact in Contact.all():
print 'Phone: %s\nAddress: %s\n\n'
% (contact.phone,
contact.address))
查詢 Company 實體可傳回 Company 例項:
for company in Company.all() # ...
如需如何使用多型態模型以及如何加以實作的詳細資料,請參閱「PolyModel 類別」。
資料存放區支援實體屬性的固定值類型集,包括 Unicode 字串、整數、浮點數、日期、實體金鑰、字元字串 (blob) 以及各種 GData 類型。每一種資料存放區值類型具有 google.appengine.ext.db 模組提供之相應的 Property 類別。
類型和屬性類別描述所有支援的值類型以及其相應的 Property 類別。幾種特殊值類型描述如下。
資料存放區支援兩種儲存字串的值類型:長度最長可達 500 個位元組的短文字字串,以及長度最長可達 1 MB 的長文字字串。短字串可以進行索引,可以用於查詢篩選條件以及排序順序。長字串無法進行索引,而且無法用於篩選條件或排序順序。
短字串值可以是 unicode 值或 str 值。若值為 str,則會假設編碼為 'ascii'。若要為 str 值指定不同的編碼,您可以將它轉換為 unicode 值 (使用 unicode() 類型建構函式,它將 str 和編碼的名稱當做引數)。短字串可以使用 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 value 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")
長字串值是由 db.Text 實例所代表。其建構函式接受 unicode 值或 str 值,以及 str 中使用的編碼名稱。長字串可以使用 TextProperty 類別來模型化。
class MyModel(db.Model):
text = db.TextProperty()
obj = MyModel()
# Text() can take a Unicode value.
obj.text = db.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 = db.Text("lots of kittens")
# Text properties can store large values.
obj.text = db.Text(open("a_tale_of_two_cities.txt").read(), "utf-8")
資料存放區也對非文字字元組字串提供兩種類似的支援類型:db.ByteString 和 db.Blob。這些值為未經處理的位元祖,所以不會被視為編碼的文字 (如 UTF-8) 來處理。
就像 str 或 unicode 值一樣,db.ByteString 值會建立索引,並以 500 個字元為限制。ByteString 實例代表短字串位元組,並接受 str 值做為其建構函式的引數。位元組字串使用 ByteStringProperty 類別來模型化。
就像 db.Text 一樣,db.Blob 值最常可為 1 MB,但未建立索引,且無法用於查詢篩選器和排序順序中。db.Blob 類別接受 str 值做為其建構函式的引數。Blob 會使用 BlobProperty 類別來模型化。
class MyModel(db.Model):
blob = db.BlobProperty()
obj = MyModel()
obj.blob = db.Blob(open("image.png").read())
可以具有多個值的屬性,在資料存放區 API 中呈現為 Python list。清單包含的值,可以是資料存放區支援的任何值類型。單一清單屬性甚至具有不同類型的值。順序受到保留,因此當查詢和 get() 傳回實體時,清單屬性值的順序會與它們的儲存順序相同。
ListProperty 類別的模型為清單,並且強制清單中的所有值必須是相同的指定類型。為了便利起見,程式庫也提供 StringListProperty,類似於 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.
清單屬性上的查詢篩選器,會針對清單的成員測試指定值。若清單中至少有一個成員符合條件,則該條件為 true。
# 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")
查詢篩選器只能在清單成員上操作。在一個查詢篩選器中,無法測試兩個清單的相似性。
資料存放區在內部會將清單屬性值呈現為該屬性的多個值。若清單屬性值為空白清單,則該屬性在資料存放區中不會有會任何呈現。 資料存放區 API 針對靜態屬性 (具有 ListProperty) 以及動態屬性對這種情況的處理方式不同:
None 值。list 值的動態屬性不能指派空白清單值。不過,它可以具有 None 值,而且可以使用 del 將它刪除。ListProperty 模型會測試加入清單的值是否為正確的類型,若類型不正確,則會發生 BadValueError。即使之前儲存的實體已擷取並載入模型中,仍會發生此測試 (而且可能會失敗)。因為 str 值會在儲存之前轉換為 unicode 值 (ASCII 文字),ListProperty(str) 會視為 ListProperty(basestring),Python 資料類型可接受 str 和 unicode 值。您也可以使用 StringListProperty() 達到此目的。
針對存放非文字字元組字串,使用 db.Blob 值。blob 字串的位元組儲存和抓取時,會受到保留。將可以將 blob 清單的屬性宣告為 ListProperty(db.Blob)。
清單屬性不會以一般方式與排序順序互動。請參閱「查詢和索引:擁有多個值的排序順序和屬性」以取得詳細資料。
屬性值可以包含其他實體的金鑰。該值為 Key 實例。
ReferenceProperty 類別的模型為金鑰值,並且強制所有值必須參考相同指定種類的實體。為了便利起見,程式庫也提供 SelfReferenceProperty,等同於參考相同種類、具有屬性實體的 ReferenceProperty。
將模型實例指派給 ReferenceProperty 屬性時,會自動使用其金鑰做為值。
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()
ReferenceProperty 屬性值可以當做被參考實體的模型實例來使用。若參考的實體不在記憶體中,使用其屬性做為實例時,會自動從資料存放區擷取該實體。
obj2.reference.prop = 999
obj2.reference.put()
results = db.GqlQuery("SELECT * FROM SecondModel")
another_obj = results.fetch(1)[0]
v = another_obj.reference.prop
刪除實體時,其金鑰若為參考屬性的值,則不會變更參考屬性。參考屬性值可以是不再有效的金鑰。若應用程式預期參考可能無效,則應用程式可以使用 if 陳述式來測試物件是否存在:
obj1 = obj2.reference if not obj1: # Referenced entity was deleted.
ReferenceProperty 有另一個好用的功能:反向參考。當模型具有另一個模型的 ReferenceProperty 時,每個參考的實體都會取得屬性,此屬性值為 Query (會傳回參考它的第一個模型的所有實體)。
# To fetch and iterate over every SecondModel entity that refers to the # FirstModel instance obj1: for obj in obj1.secondmodel_set: # ...
反向參考的預設名稱為 modelname_set (模型類別的名稱為小寫字母,並在結尾加上 "_set"),而且可以使用 ReferenceProperty 建構函式的 collection_name 引數來進行調整。
若您有多個 ReferenceProperty 值參考相同的模型類別,則反向參考屬性的預設建構函式會引發錯誤:
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)
若要避免此錯誤,您必須明確地設定 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")
使用 ReferenceProperty 模型屬性類別時,才能使用模型實例的自動參考和解除參考、類型檢查以及反向參考。儲存為 Expando 動態屬性或 ListProperty 值的金鑰不具有這些功能。
資料存放區將所有屬性名稱的開頭和結尾都保留為底線字元 (__*__)。應用程式無法建立這種名稱的屬性。
在 Python API 中,會忽略名稱開頭為底線 (_) 之模型實例的屬性 (attribute),而且這種屬性 (attribute) 不會儲存至資料存放區實體。這樣可以將值儲存在模型實例中,供您在內部暫時使用,而不會影響到實體儲存的資料。
因為 Python API 預設會使用模型實例的屬性 (attribute) 做為屬性名稱,因此實例方法已經使用的任何屬性,都不能直接做為 property attribute 名稱。同樣地,模型建構函式之關鍵字引數使用的任何名稱,也都不能做為 property attribute 名稱使用。請參閱保留的屬性名稱清單。
但資料存放區本身則允許這些名稱。若應用程式需要資料存放區實體的屬性名稱,類似於 Python API 所保留的文字,則應用程式可以使用固定屬性,並將 name 引數傳送給 Property 類別建構函式。請參閱 Property 類別建構函式。
class MyModel(db.Model): obj_key = db.StringProperty(name="key")