检索模型(结构体)列表的通用方法

huangapple go评论81阅读模式
英文:

Generic method for retrieving list of models (structs)

问题

我正在尝试为我的服务创建基本的CRUD操作。它基于在结构体中创建的数据模型。问题是我真的不想为CRUD方法重复编写代码。例如,我定义了ModelA和ModelB作为结构体:

type ModelA struct {
    ID              bson.ObjectId     `json:"ID,omitempty" bson:"_id,omitempty"`
    Slug            string            `json:"slug" bson:"slug,omitempty"`
    Creator         string            `json:"-" bson:"creator,omitempty"`
    DefaultLanguage string            `json:"defaultLanguage" bson:"defaultLanguage,omitempty"`
}

type ModelB struct {
    ID              bson.ObjectId     `json:"ID,omitempty" bson:"_id,omitempty"`
    Type            string            `json:"type" bson:"type,omitempty"`
}

我想要创建一个通用的方法,用于检索给定模型的数组。对我来说,使用模型是很重要的。我可以使用纯粹的interface{}类型快速完成,但会失去模型的功能,例如在JSON输出中隐藏某些属性(例如ModelA.Creator)。

到目前为止,我已经为创建新数据和检索单个模型创建了通用方法。以下是示例代码:

// GET: /modelsa/{:slug}
func (r *Routes) GetModelA(w rest.ResponseWriter, req *rest.Request) {
    // 将模型设置为ModelA
    var model models.ModelA
    r.GetBySlug(w, req, &model, "models")
}

// GET: /modelsb/{:slug}
func (r *Routes) GetModelB(w rest.ResponseWriter, req *rest.Request) {
    // 将模型设置为ModelB
    var model models.ModelB
    r.GetBySlug(w, req, &model, "models")
}

func (r *Routes) GetBySlug(w rest.ResponseWriter, req *rest.Request, m interface{}, collection string) {
    slug := req.PathParam("slug")

    if err := r.GetDocumentBySlug(slug, collection, m, w, req); err != nil {
        rest.Error(w, err.Error(), http.StatusInternalServerError)
        return
    }

    w.WriteJson(m)
}

GetModelAGetModelB是路由处理程序,它们使用通用方法GetBySlug返回由给定模型格式化的JSON。

我想要做同样的事情,但是使用给定模型的数组。到目前为止,我遇到了将结果转换为结构体的问题:

// GET /modelsa/
func (r *Routes) GetModels(w rest.ResponseWriter, req *rest.Request) {
    // 在这种情况下,我认为我不需要传递一个结构体数组
    // 因为给定的结构体只是一个引用。也可以是:
    // var result models.ModelA。将其转换为数组可以在GetList()方法中完成
    var result []models.ModelA
    r.GetList(w, req, &result, "models")
}

func (r *Routes) GetList(w rest.ResponseWriter, req *rest.Request, res interface{}, col string) {

}

我无法将res参数设置为interface{}的数组。如果我尝试在GetList()方法中将结果转换为[]interface{},然后无法将其转换为res参数,因为它不是一个数组。

有没有一种好的方法来做到这一点?也许我想错了,应该重新设计这些方法?任何建议都将不胜感激。

英文:

I'm trying to create basic CRUD for my service. It is based on data models created in structs. The problem is that I really dont want to repeat code for CRUD methods. For example I have ModelA and ModelB defined as structs:

type ModelA struct {
	ID              bson.ObjectId     `json:"ID,omitempty" bson:"_id,omitempty"`
	Slug            string            `json:"slug" bson:"slug,omitempty"`
	Creator         string            `json:"-" bson:"creator,omitempty"`
	DefaultLanguage string            `json:"defaultLanguage" bson:"defaultLanguage,omitempty"`
}

type ModelB struct {
	ID              bson.ObjectId     `json:"ID,omitempty" bson:"_id,omitempty"`
	Type            string            `json:"type" bson:"type,omitempty"`
}

What I want is to make generic method which retrieves an array of given model. It is important for me to use models. I can do it quick with pure interface{} types but will loose model functionality, for example hiding some properties in JSON output (ex. ModelA.Creator).

So far I've created generic methods for creating new data and retrieving single model. Here is example code:

// GET: /modelsa/{:slug}
func (r *Routes) GetModelA(w rest.ResponseWriter, req *rest.Request) {
    // set model as ModelA
	var model models.ModelA
	r.GetBySlug(w, req, &model, "models")
}

// GET: /modelsb/{:slug}
func (r *Routes) GetModelB(w rest.ResponseWriter, req *rest.Request) {
    // set model as ModelB
    var model models.ModelB
    r.GetBySlug(w, req, &model, "models")
}

func (r *Routes) GetBySlug(w rest.ResponseWriter, req *rest.Request, m interface{}, collection string) {
	slug := req.PathParam("slug")

	if err := r.GetDocumentBySlug(slug, collection, m, w, req); err != nil {
		rest.Error(w, err.Error(), http.StatusInternalServerError)
		return
	}

	w.WriteJson(m)
}

GetModelA and GetModelB are route handlers which uses generic method GetBySlug that returns a JSON formatted by given model.

I want to do the same but with the array of given models. So far I ve got problem to cast the result into the struct:

// GET /modelsa/
func (r *Routes) GetModels(w rest.ResponseWriter, req *rest.Request) {
    // I think in this case I don't have to pass an array of struct
    // because the given struct is only reference. It could be:
    // var result models.ModelA as well. Converting it into array could 
    // be done in GetList() method
	var result []models.ModelA
	r.GetList(w, req, &result, "models")
}

func (r *Routes) GetList(w rest.ResponseWriter, req *rest.Request, res interface{}, col string) {

}

I cant set res argument as an array of interface{}. Also I if i try to cast result to the []interface{} within GetList() method, I cant then cast it to the res argument as it is not an array.

Is there a nice way to do this? Maybe I think wrong and should redesign the methods? Any advice would be appreciated.

答案1

得分: 1

你可以声明新的类型来表示你的模型的切片。例如,

type ModelAList []ModelA
type ModelBList []ModelB

然后,当你将这些新类型的变量传递给r.GetDocumentBySlug()时,encoding/json包中的函数将相应地解组切片。

你可以在这里(编组)这里(解组)找到可工作的示例。

英文:

You can declare new types which represent slice of your models. For example,

type ModelAList []ModelA
type ModelBList []ModelB

Then when you pass variables of these new types into your r.GetDocumentBySlug(), the functions in the encoding/json package will unmarshal the slices accordingly.

You can find working examples here (marshaling) and here (unmarshaling).

huangapple
  • 本文由 发表于 2017年8月12日 21:30:15
  • 转载请务必保留本文链接:https://go.coder-hub.com/45650875.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定