我怎样才能在 google 应用引擎数据存储中拥有动态属性
How can I have dynamic properties in go on the google app engine datastore
我想做一些类似于 python 在 App Engine 上支持的 Expando Model 的事情。
Sometimes you don't want to declare your properties ahead of time. A
special model subclass, Expando, changes the behavior of its entities
so that any attribute assigned (as long as it doesn't start with an
underscore) is saved to the Datastore.
我如何在 Go 中执行此操作?
事先注意:
有 2 个 API。导入路径 appengine/datastore
的那个使用通道作为参数。导入路径为 google.golang.org/appengine/datastore
的另一个使用切片。根据您的情况调整以下示例。有关详细信息,请参阅此问题:
具有动态属性的实体的关键是 PropertyLoadSaver
界面。通过实现此接口,您可以在保存时动态地构造要保存的实体的属性。
此外,Go AppEngine 平台提供了一个 PropertyList
type which is basically a list (a slice) of properties Property
并且还实现了 PropertyLoadSaver
.
,因此您不必自己执行此操作
所以 Go 中的 Expando 模型是 PropertyList
。只需添加您希望实体拥有的属性,然后保存此 PropertyList
值。
这是一个例子:
c := appengine.NewContext(r)
props := datastore.PropertyList{
datastore.Property{Name: "time", Value: time.Now()},
datastore.Property{Name: "email", Value: "me@myhost.com"},
}
k := datastore.NewIncompleteKey(c, "DynEntity", nil)
key, err := datastore.Put(c, k, &props)
c.Infof("%v %v", key, err)
此示例保存一个名为 "DynEntity"
的实体,该实体具有 2 个动态属性:"time"
和 "email"
。
由于 PropertyList
类型是一个切片,你也可以使用内置的 append()
函数为其添加属性,所以你也可以像这样初始化 props
:
var props datastore.PropertyList
props = append(props, datastore.Property{Name:"time", Value: time.Now()})
props = append(props, datastore.Property{Name:"email", Value: "me@myhost.com"})
将 map
变成动态实体
PropertyLoadSaver
接口并不复杂,我们可以自己实现。在下面的例子中,我在一个简单的自定义类型上实现它 map
:
type DynEnt map[string]interface{}
func (d *DynEnt) Load(props []datastore.Property) error {
// Note: you might want to clear current values from the map or create a new map
for _, p := range props {
(*d)[p.Name] = p.Value
}
return nil
}
func (d *DynEnt) Save() (props []datastore.Property, err error) {
for k, v := range *d {
props = append(props, datastore.Property{Name: k, Value: v})
}
return
}
下面是使用通道而不是切片的“旧”界面的实现方式:
type DynEnt map[string]interface{}
func (d *DynEnt) Load(ch <-chan datastore.Property) error {
// Note: you might want to clear current values from the map or create a new map
for p := range ch { // Read until channel is closed
(*d)[p.Name] = p.Value
}
return nil
}
func (d *DynEnt) Save(ch chan<- datastore.Property) error {
defer close(ch) // Channel must be closed
for k, v := range *d {
ch <- datastore.Property{Name: k, Value: v}
}
return nil
}
现在我们可以像 Go 中的任何其他映射一样使用我们的 DynEnt
类型,并且由于它实现了 PropertyLoadSaver
,它可以保存为一个实体(并且 any实体可以加载进去):
c := appengine.NewContext(r)
d := DynEnt{"email": "me@myhost.com", "time": time.Now()}
k := datastore.NewIncompleteKey(c, "DynEntity", nil)
key, err := datastore.Put(c, k, &d)
c.Infof("%v %v", key, err)
我想做一些类似于 python 在 App Engine 上支持的 Expando Model 的事情。
Sometimes you don't want to declare your properties ahead of time. A special model subclass, Expando, changes the behavior of its entities so that any attribute assigned (as long as it doesn't start with an underscore) is saved to the Datastore.
我如何在 Go 中执行此操作?
事先注意:
有 2 个 API。导入路径 appengine/datastore
的那个使用通道作为参数。导入路径为 google.golang.org/appengine/datastore
的另一个使用切片。根据您的情况调整以下示例。有关详细信息,请参阅此问题:
具有动态属性的实体的关键是 PropertyLoadSaver
界面。通过实现此接口,您可以在保存时动态地构造要保存的实体的属性。
此外,Go AppEngine 平台提供了一个 PropertyList
type which is basically a list (a slice) of properties Property
并且还实现了 PropertyLoadSaver
.
所以 Go 中的 Expando 模型是 PropertyList
。只需添加您希望实体拥有的属性,然后保存此 PropertyList
值。
这是一个例子:
c := appengine.NewContext(r)
props := datastore.PropertyList{
datastore.Property{Name: "time", Value: time.Now()},
datastore.Property{Name: "email", Value: "me@myhost.com"},
}
k := datastore.NewIncompleteKey(c, "DynEntity", nil)
key, err := datastore.Put(c, k, &props)
c.Infof("%v %v", key, err)
此示例保存一个名为 "DynEntity"
的实体,该实体具有 2 个动态属性:"time"
和 "email"
。
由于 PropertyList
类型是一个切片,你也可以使用内置的 append()
函数为其添加属性,所以你也可以像这样初始化 props
:
var props datastore.PropertyList
props = append(props, datastore.Property{Name:"time", Value: time.Now()})
props = append(props, datastore.Property{Name:"email", Value: "me@myhost.com"})
将 map
变成动态实体
PropertyLoadSaver
接口并不复杂,我们可以自己实现。在下面的例子中,我在一个简单的自定义类型上实现它 map
:
type DynEnt map[string]interface{}
func (d *DynEnt) Load(props []datastore.Property) error {
// Note: you might want to clear current values from the map or create a new map
for _, p := range props {
(*d)[p.Name] = p.Value
}
return nil
}
func (d *DynEnt) Save() (props []datastore.Property, err error) {
for k, v := range *d {
props = append(props, datastore.Property{Name: k, Value: v})
}
return
}
下面是使用通道而不是切片的“旧”界面的实现方式:
type DynEnt map[string]interface{}
func (d *DynEnt) Load(ch <-chan datastore.Property) error {
// Note: you might want to clear current values from the map or create a new map
for p := range ch { // Read until channel is closed
(*d)[p.Name] = p.Value
}
return nil
}
func (d *DynEnt) Save(ch chan<- datastore.Property) error {
defer close(ch) // Channel must be closed
for k, v := range *d {
ch <- datastore.Property{Name: k, Value: v}
}
return nil
}
现在我们可以像 Go 中的任何其他映射一样使用我们的 DynEnt
类型,并且由于它实现了 PropertyLoadSaver
,它可以保存为一个实体(并且 any实体可以加载进去):
c := appengine.NewContext(r)
d := DynEnt{"email": "me@myhost.com", "time": time.Now()}
k := datastore.NewIncompleteKey(c, "DynEntity", nil)
key, err := datastore.Put(c, k, &d)
c.Infof("%v %v", key, err)