beego報錯 table name: `xxx` not exists

重遠 發表於 2021-04-08
Go

一、問題

在學習beego中出現了Handler crashed with error <Ormer.QueryTable> table name: 'users' not exists的報錯。

示例程式碼:

models:

func init() {
    orm.RegisterModel(new(User))
}

type User struct {
    Id int64
    Name string
}

controllers:

func (u *UserController) Index() {
    var users []models.User
    orm.NewOrm().QueryTable("users").All(&users)
    fmt.Println(users)
}

二、查詢問題

  1. 首先找到beego報錯的地方,在beego/client/orm/orm.go檔案中QueryTableWithCtx方法。

func (o *ormBase) QueryTableWithCtx(ctx context.Context, ptrStructOrTableName interface{}) (qs QuerySeter) {
    var name string
    if table, ok := ptrStructOrTableName.(string); ok {
        name = nameStrategyMap[defaultNameStrategy](table)
        if mi, ok := modelCache.get(name); ok {
            qs = newQuerySet(o, mi)
        }
    } else {
        name = getFullName(indirectType(reflect.TypeOf(ptrStructOrTableName)))
        if mi, ok := modelCache.getByFullName(name); ok {
            qs = newQuerySet(o, mi)
        }
    }
    if qs == nil {
        panic(fmt.Errorf("<Ormer.QueryTable> table name: `%s` not exists", name))
    }
    return
}

ptrStructOrTableName引數是表名(string型別),由此判斷是在modelCache.get(name)出錯,進入其方法:

// get model info by table name
func (mc *_modelCache) get(table string) (mi *modelInfo, ok bool) {
    mi, ok = mc.cache[table]
    return
}

進一步判斷,傳入的表名不存在_modelCache,而在在QueryTable之前僅有orm.RegisterModel(new(User))是註冊model方法。

  1. 追查orm.RegisterModel方法最終呼叫的是在beego/client/orm/models.go中的register方法:
// register register models to model cache
func (mc *_modelCache) register(prefixOrSuffixStr string, prefixOrSuffix bool, models ...interface{}) (err error) {
    ... // 省略程式碼
    for _, model := range models {
        ...
        table := getTableName(val)
        if prefixOrSuffixStr != "" {
            if prefixOrSuffix {
                table = prefixOrSuffixStr + table
            } else {
                table = table + prefixOrSuffixStr
            }
        }
        ...
        mi.table = table
        mi.pkg = typ.PkgPath()
        mi.model = model
        mi.manual = true
        mc.set(table, mi)
    }
    return
}

register方法會先通過getTableName查詢是否實現TableName方法,如果實現會直接獲取TableName返回的表名,然後判斷是否增加字首或者字尾。

// getTableName get struct table name.
// If the struct implement the TableName, then get the result as tablename
// else use the struct name which will apply snakeString.
func getTableName(val reflect.Value) string {
    if fun := val.MethodByName("TableName"); fun.IsValid() { // 判斷是否實現TableName方法fun.IsValid() 
        vals := fun.Call([]reflect.Value{})
        // has return and the first val is string
        if len(vals) > 0 && vals[0].Kind() == reflect.String {
            return vals[0].String()
        }
    }
    return snakeString(reflect.Indirect(val).Type().Name())
}

三、解決方法

通過程式碼的追蹤,發現報錯的原因是QueryTable(”users“)傳入的引數與註冊的User Model並不匹配,根據原始碼解決辦法有三種:

  1. 修改傳入引數users改為user,並更改資料庫中的表名

    orm.NewOrm().QueryTable("user").All(&users)
  2. User Model中實現TableName方法

    func (u *User) TableName() string {
     return "users"
    }
  3. 註冊model時使用字尾

    orm.RegisterModelWithSuffix("s", new(User))

四、總結

1、beego中QueryTable方法中表名引數是與註冊Model相關聯的。

2、如果資料庫中表名和Model名稱不相同時,可使用實現TableName方法或者註冊model時新增字首RegisterModelWithPrefix、新增字尾RegisterModelWithSuffix時註冊實際表名。

本作品採用《CC 協議》,轉載必須註明作者和本文連結