如何在google app引擎数据存储区中使用动态属性

jsh*_*hen 2 google-app-engine go google-cloud-datastore

我想做一些像python在app引擎上支持的Expando模型.

有时您不希望提前声明您的属性.特殊模型子类Expando会更改其实体的行为,以便分配任何属性(只要它不以下划线开头)将保存到数据存储区.

我怎么能在Go中这样做?

icz*_*cza 6

事先注意:

有2个API.具有导入路径的路径appengine/datastore使用通道作为参数.另一个导入路径google.golang.org/appengine/datastore使用切片.将上面的示例调整为您的案例.有关详细信息,请参阅此问题:如何正确导入Golang appengine?


具有动态属性的实体的关键是PropertyLoadSaver接口.通过实现此接口,您可以动态 - 在节省时间 - 构建要保存的实体的属性.

此外,为了不必自己执行此操作,Go AppEngine平台提供的PropertyList类型基本上是属性的列表(切片),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)
Run Code Online (Sandbox Code Playgroud)

此示例保存"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"})
Run Code Online (Sandbox Code Playgroud)

将a 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
}
Run Code Online (Sandbox Code Playgroud)

现在我们可以DynEnt像Go中的任何其他地图一样使用我们的类型,并且因为它实现了PropertyLoadSaver,它可以保存为实体(并且可以将任何实体加载到其中):

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
}
Run Code Online (Sandbox Code Playgroud)