众所周知,google数据存储使用的是BigTable,而非传统意义上的数据库,Google AppEngine也不例外,但为了方便程序员使用,Google提供了数据存储API,来使得大家可以传统意义上的SQL来使用Google的BigTable分布式数据存储系统。
- from google.appengine.ext import db
- from google.appengine.api import users
- #数据库模型
- 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()
- #创建数据对象
- pet = Pet(name="Fluffy",
- type="cat",
- owner=users.get_current_user())
- pet.weight_in_pounds = 24
- #将数据提交到数据存储系统
- pet.put()
- if users.get_current_user():
- user_pets = db.GqlQuery("SELECT * FROM Pet WHERE pet.owner = :1",
- users.get_current_user())
- for pet in user_pets:
- pet.spayed_or_neutered = True
- db.put(user_pets)
google AppEngine中每个数据对象被称为实体,每个实体具有几个属性。
每个实体有一个唯一的关键词,最简单的关键词是数字ID,ID也可以是应用所指定的字符串。
应用可以通过关键词,或根据对象的属性来提取数据。
二 模型接口
应用通过模型来定义所需要使用的数据,模型都需要定义为db.Model类的子类。模型定义了数据实体以及对应的属性,每个属性都是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)
基于python的动态性,google app engine提供了不同于传统数据库的功能,该功能基于expando类,使用该类可以动态设置属性,例如:
- 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
- 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
字符串、长字符串、二进制数据
appengine支持Unicode字符串,因此不需要为中文问题而担忧。
- 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")
- 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")
- class MyModel(db.Model):
- blob = db.BlobProperty()
- obj = MyModel()
- obj.blob = db.Blob(open("image.png").read())
列表:
- 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.
- # 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")
- 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()
五 创建、更新记录(想了半天,还是觉得用记录还描述比较贴切,实体...太奇怪了)
- pet = Pet(name="Fluffy",
- type="cat",
- owner=users.get_current_user())
- pet.put()
- db.put(pet)
- if users.get_current_user():
- user_pets = db.GqlQuery("SELECT * FROM Pet WHERE pet.owner = :1",
- users.get_current_user())
- for pet in user_pets:
- pet.spayed_or_neutered = True
- db.put(user_pets)
六 查询记录集
对象查询:
- class Story(db.Model):
- title = db.StringProperty()
- date = db.DateTimeProperty()
- query = Story.all()
- query.filter('title =', 'Foo')
- query.order('-date')
- query.ancestor(key)#这句没明白做什么的?先留着吧。
- # These methods can be chained together on one line.
- query.filter('title =', 'Foo').order('-date').ancestor(key)
- # Parameters can be bound with positional arguments.
- query = db.GqlQuery("SELECT * FROM Story WHERE title = :1 "
- "AND ANCESTOR IS :2 "
- "ORDER BY date DESC",
- 'Foo', key)
- # Or, parameters can be bound with keyword arguments.
- query = db.GqlQuery("SELECT * FROM Story WHERE title = :title "
- "AND ANCESTOR IS :parent "
- "ORDER BY date DESC",
- title='Foo', parent=key)
- # String, number and Boolean values can be literal values in the string.
- query = db.GqlQuery("SELECT * FROM Story WHERE title = 'Foo' "
- "AND ANCESTOR IS :parent "
- "ORDER BY date DESC",
- parent=key)
query = Story.gql("WHERE title = :title "
"AND ANCESTOR IS :parent "
"ORDER BY date DESC",
title='Foo', parent=key)
- results = query.fetch(10)
- for result in results:
- print "Title: " + result.title
- for result in query:
- print "Title: " + result.title
注意:Google AppEngine中每个查询最多返回1000条数据,不管limit和offset设置的值为多少。例如,对于大于1000条的数据,如果offset设置为100,那么实际返回900条数据。 我想知道的是,如果有1万条数据,那么每次返回100条,不知道是否可以将1万条数据全部遍历,如果不可以的话,那么岂不是非常不妙啊?
要获取一条数据也很简单,如下:
- entity.put()
- key = entity.key()
- # ...
- entity = db.get(key)
- pets = GqlQuery("SELECT * FROM Pet WHERE name = :1", "Fluffy")
- pet = pets.get()
- owner_name = pet.owner.name
- #在模板中如下使用
- obj = MyModel(name="Foo")
- self.response.write('%s' % (str(obj.key()),
- obj.name()))
- # ...
- #在页面中如下使用
- key_name = self.request.get('key')
- obj = db.get(db.Key(key_name))
q = db.GqlQuery("SELECT * FROM Message WHERE create_date < :1", earliest_date)results = q.fetch(10)for result in results:result.delete()
# or...
q = db.GqlQuery("SELECT * FROM Message WHERE create_date < :1", earliest_date)results = q.fetch(10)db.delete(results) 九 ID Or Key_name
如果没有设置ID的话,可以使用obj.key()获得某条记录的ID
可以使用db.Key(ID)或者Entity.Key(ID)获取obj。
十 索引的配置 Index.yaml 例子:
- indexes:
- - kind: Cat
- ancestor: no
- properties:
- - name: name
- - name: age
- direction: desc
- - kind: Cat
- properties:
- - name: name
- direction: asc
- - name: whiskers
- direction: desc
- - kind: Store
- ancestor: yes
- properties:
- - name: business
- direction: asc
- - name: owner
- direction: asc
注意:实际上,如果你没有对该文件进行配置,google appengine会对它进行默认的配置,当然是为了优化查询了。
附录:
一 Model Class:(文档:http://code.google.com/intl/zh-CN/appengine/docs/datastore/modelclass.html)
类方法:
- Model.get()
- Model.get_by_id()
- Model.get_by_key_name()
- Model.get_or_insert()
- Model.all()
- Model.gql()
- Model.kind()
- Model.properties()
- key()
- put()
- delete()
- is_saved()
- parent()
- parent_key()
- to_xml()
实例方法:
- filter()
- order()
- ancestor()
- get()
- fetch()
- count()
Property class | Value type | Sort order |
---|---|---|
StringProperty | str unicode |
Unicode (str is treated as ASCII) |
BooleanProperty | bool | False < True |
IntegerProperty | int long |
Numeric |
FloatProperty | float | Numeric |
DateTimeProperty DateProperty TimeProperty |
datetime.datetime | Chronological |
ListProperty StringListProperty |
list of a supported type | If ascending, by least element; if descending, by greatest element |
ReferenceProperty SelfReferenceProperty |
db.Key | By path elements (kind, ID or name, kind, ID or name...) |
UserProperty | users.User | By email address (Unicode) |
BlobProperty | db.Blob | (not orderable) |
TextProperty | db.Text | (not orderable) |
CategoryProperty | db.Category | Unicode |
LinkProperty | db.Link | Unicode |
EmailProperty | db.Email | Unicode |
GeoPtProperty | db.GeoPt | By latitude, then longitude |
IMProperty | db.IM | Unicode |
PhoneNumberProperty | db.PhoneNumber | Unicode |
PostalAddressProperty | db.PostalAddress | Unicode |
RatingProperty | db.Rating | Numeric |
exception Error() exception BadArgumentError() exception BadFilterError() exception BadKeyError() exception BadPropertyError() exception BadQueryError() exception BadRequestError() exception BadValueError() exception ConfigurationError() exception DuplicatePropertyError() exception InternalError() exception KindError() exception NotSavedError() exception PropertyError() exception ReservedWordError() exception Rollback() exception Timeout() exception TransactionFailedError() exception CapabilityDisabledError()五 事务处理
http://code.google.com/intl/zh-CN/appengine/docs/datastore/transactions.html