我正在尝试使用我刚刚发现的gcloud
api 查询一些数据。 我想查询KeyPropery
. 例如:
from google.appengine.ext import ndb
class User(ndb.Model):
email = ndb.StringProperty()
class Data(ndb.Model):
user = ndb.KeyProperty('User')
data = ndb.JsonProperty()
在 GAE 中,假设我有用户的密钥,我可以很容易地查询到这个:
user = User.query(User.email == 'me@domain.com').get()
data_records = Data.query(Data.user == user.key).fetch()
我想使用 gcloud
做类似的事情:
from gcloud import datastore
client = datastore.Client(project='my-project-id')
user_qry = client.query(kind='User')
user_qry.add_filter('email', '=', 'me@domain.com')
users = list(user_qry.fetch())
user = users[0]
data_qry = client.query(kind='Data')
data_qry.add_filter('user', '=', user.key) # This doesn't work ...
results = list(data_qry.fetch()) # results = []
查看add_filter
的文档,似乎Entity.key
不是受支持的类型:
值 (int, str, bool, float, NoneType, :class
datetime.datetime
) – 要筛选的值。
是否可以为关键属性添加筛选器?
我做了更多的调查,试图弄清楚这里到底发生了什么。 我不确定这是否有助于我目前理解这个问题,但也许这对其他人有帮助。
我已经模拟了各个库中的基础调用,以记录正在序列化并发送到服务器的协议缓冲区。 对于 GAE,它似乎Batch.create_async
在datastore_query模块中。
对于gcloud
,这是datastore.Client.connection.run_query
方法。 查看生成的协议缓冲区(匿名),我看到:
云查询 PB.
kind {
name: "Data"
}
filter {
composite_filter {
operator: AND
filter {
property_filter {
property {
name: "user"
}
operator: EQUAL
value {
key_value {
partition_id {
dataset_id: "s~app-id"
}
path_element {
kind: "User"
name: "user_string_id"
}
}
}
}
}
}
}
GAE 查询 pb.
kind: "Data"
Filter {
op: 5
property <
name: "User"
value <
ReferenceValue {
app: "s~app-id"
PathElement {
type: "User"
name: "user_string_id"
}
}
>
multiple: false
>
}
据我所知,这两个库使用的是不同版本的原型,但传递的数据看起来非常相似......
这是您使用ndb库的一个微妙错误:
所有ndb属性都接受单个位置参数,该参数在数据存储中指定属性的名称
查看模型定义,您将看到user = ndb.KeyProperty('User')
.这实际上并不是说 user
属性是User
实体的键,而是说它应该存储在数据存储中,属性名称为 User
。您可以在 gae 协议缓冲区查询中验证这一点,其中属性名称为(区分大小写)User
。
如果要将密钥限制为单个类型,则需要使用 kind 选项指定它。
user = ndb.KeyProperty(kind="User")
该KeyProperty
还支持:
user = ndb.KeyProperty(User) # User is a class here, not a string
这是对所有魔法的描述。
就像现在一样,您的 gcloud 查询正在查询大小写错误的用户,应该是:
data_qry = client.query(kind='Data')
data_qry.add_filter('User', '=', user.key)