1
0
mirror of https://github.com/astaxie/beego.git synced 2024-11-05 05:40:54 +00:00
Beego/orm/models_boot.go

346 lines
9.2 KiB
Go
Raw Normal View History

2014-08-18 08:41:43 +00:00
// Copyright 2014 beego Author. All Rights Reserved.
2014-07-03 15:40:21 +00:00
//
2014-08-18 08:41:43 +00:00
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
2014-07-03 15:40:21 +00:00
//
2014-08-18 08:41:43 +00:00
// http://www.apache.org/licenses/LICENSE-2.0
2014-07-03 15:40:21 +00:00
//
2014-08-18 08:41:43 +00:00
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
2013-07-30 12:32:38 +00:00
package orm
import (
"fmt"
"os"
"reflect"
"strings"
)
2014-01-17 15:28:54 +00:00
// register models.
2016-09-06 15:05:41 +00:00
// PrefixOrSuffix means table name prefix or suffix.
// isPrefix whether the prefix is prefix or suffix
func registerModel(PrefixOrSuffix string, model interface{}, isPrefix bool) {
val := reflect.ValueOf(model)
2016-08-30 16:07:19 +00:00
typ := reflect.Indirect(val).Type()
if val.Kind() != reflect.Ptr {
2013-10-09 03:37:16 +00:00
panic(fmt.Errorf("<orm.RegisterModel> cannot use non-ptr model struct `%s`", getFullName(typ)))
}
2016-08-30 16:07:19 +00:00
// For this case:
// u := &User{}
// registerModel(&u)
2016-08-30 14:02:11 +00:00
if typ.Kind() == reflect.Ptr {
panic(fmt.Errorf("<orm.RegisterModel> only allow ptr model struct, it looks you use two reference to the struct `%s`", typ))
}
2013-08-19 14:37:39 +00:00
table := getTableName(val)
2016-09-06 15:05:41 +00:00
if PrefixOrSuffix != "" {
if isPrefix {
table = PrefixOrSuffix + table
} else {
table = table + PrefixOrSuffix
}
2013-08-19 14:37:39 +00:00
}
2016-08-30 16:07:19 +00:00
// models's fullname is pkgpath + struct name
name := getFullName(typ)
2016-08-30 16:07:19 +00:00
if _, ok := modelCache.getByFullName(name); ok {
2013-08-19 14:37:39 +00:00
fmt.Printf("<orm.RegisterModel> model `%s` repeat register, must be unique\n", name)
os.Exit(2)
}
2013-07-30 12:32:38 +00:00
if _, ok := modelCache.get(table); ok {
2013-08-19 14:37:39 +00:00
fmt.Printf("<orm.RegisterModel> table name `%s` repeat register, must be unique\n", table)
2013-07-30 12:32:38 +00:00
os.Exit(2)
}
2016-09-01 15:28:34 +00:00
mi := newModelInfo(val)
if mi.fields.pk == nil {
outFor:
2016-09-01 15:28:34 +00:00
for _, fi := range mi.fields.fieldsDB {
2015-09-12 13:46:43 +00:00
if strings.ToLower(fi.name) == "id" {
switch fi.addrValue.Elem().Kind() {
case reflect.Int, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint32, reflect.Uint64:
fi.auto = true
fi.pk = true
2016-09-01 15:28:34 +00:00
mi.fields.pk = fi
2015-09-12 13:46:43 +00:00
break outFor
}
}
}
2016-09-01 15:28:34 +00:00
if mi.fields.pk == nil {
fmt.Printf("<orm.RegisterModel> `%s` need a primary key field, default use 'id' if not set\n", name)
os.Exit(2)
}
2013-07-30 12:32:38 +00:00
}
2016-09-01 15:28:34 +00:00
mi.table = table
mi.pkg = typ.PkgPath()
mi.model = model
mi.manual = true
2013-08-19 14:37:39 +00:00
2016-09-01 15:28:34 +00:00
modelCache.set(table, mi)
2013-07-30 12:32:38 +00:00
}
2014-01-17 15:28:54 +00:00
// boostrap models
2013-08-07 11:11:44 +00:00
func bootStrap() {
if modelCache.done {
return
}
2013-07-30 12:32:38 +00:00
var (
err error
models map[string]*modelInfo
)
if dataBaseCache.getDefault() == nil {
2013-08-19 14:37:39 +00:00
err = fmt.Errorf("must have one register DataBase alias named `default`")
2013-07-30 12:32:38 +00:00
goto end
}
2016-09-22 15:14:49 +00:00
// set rel and reverse model
// RelManyToMany set the relTable
2013-07-30 12:32:38 +00:00
models = modelCache.all()
for _, mi := range models {
for _, fi := range mi.fields.columns {
if fi.rel || fi.reverse {
elm := fi.addrValue.Type().Elem()
2016-09-01 15:28:34 +00:00
if fi.fieldType == RelReverseMany || fi.fieldType == RelManyToMany {
2013-07-30 12:32:38 +00:00
elm = elm.Elem()
}
2016-09-01 15:28:34 +00:00
// check the rel or reverse model already register
name := getFullName(elm)
2016-08-30 16:07:19 +00:00
mii, ok := modelCache.getByFullName(name)
2016-09-01 15:28:34 +00:00
if !ok || mii.pkg != elm.PkgPath() {
2016-12-29 17:24:27 +00:00
err = fmt.Errorf("can not find rel in field `%s`, `%s` may be miss register", fi.fullName, elm.String())
2013-07-30 12:32:38 +00:00
goto end
}
fi.relModelInfo = mii
switch fi.fieldType {
case RelManyToMany:
if fi.relThrough != "" {
if i := strings.LastIndex(fi.relThrough, "."); i != -1 && len(fi.relThrough) > (i+1) {
pn := fi.relThrough[:i]
2016-08-30 16:07:19 +00:00
rmi, ok := modelCache.getByFullName(fi.relThrough)
2013-07-30 12:32:38 +00:00
if ok == false || pn != rmi.pkg {
2016-09-01 15:28:34 +00:00
err = fmt.Errorf("field `%s` wrong rel_through value `%s` cannot find table", fi.fullName, fi.relThrough)
2013-07-30 12:32:38 +00:00
goto end
}
fi.relThroughModelInfo = rmi
fi.relTable = rmi.table
} else {
2016-09-01 15:28:34 +00:00
err = fmt.Errorf("field `%s` wrong rel_through value `%s`", fi.fullName, fi.relThrough)
2013-07-30 12:32:38 +00:00
goto end
}
} else {
i := newM2MModelInfo(mi, mii)
if fi.relTable != "" {
i.table = fi.relTable
}
if v := modelCache.set(i.table, i); v != nil {
err = fmt.Errorf("the rel table name `%s` already registered, cannot be use, please change one", fi.relTable)
goto end
}
fi.relTable = i.table
fi.relThroughModelInfo = i
}
fi.relThroughModelInfo.isThrough = true
2013-07-30 12:32:38 +00:00
}
}
}
}
2016-09-22 15:14:49 +00:00
// check the rel filed while the relModelInfo also has filed point to current model
// if not exist, add a new field to the relModelInfo
2013-07-30 12:32:38 +00:00
models = modelCache.all()
for _, mi := range models {
for _, fi := range mi.fields.fieldsRel {
switch fi.fieldType {
case RelForeignKey, RelOneToOne, RelManyToMany:
inModel := false
for _, ffi := range fi.relModelInfo.fields.fieldsReverse {
if ffi.relModelInfo == mi {
inModel = true
break
}
}
if inModel == false {
rmi := fi.relModelInfo
ffi := new(fieldInfo)
ffi.name = mi.name
ffi.column = ffi.name
ffi.fullName = rmi.fullName + "." + ffi.name
ffi.reverse = true
ffi.relModelInfo = mi
ffi.mi = rmi
if fi.fieldType == RelOneToOne {
ffi.fieldType = RelReverseOne
} else {
ffi.fieldType = RelReverseMany
}
if rmi.fields.Add(ffi) == false {
added := false
for cnt := 0; cnt < 5; cnt++ {
ffi.name = fmt.Sprintf("%s%d", mi.name, cnt)
ffi.column = ffi.name
ffi.fullName = rmi.fullName + "." + ffi.name
if added = rmi.fields.Add(ffi); added {
break
}
}
if added == false {
2013-10-09 03:37:16 +00:00
panic(fmt.Errorf("cannot generate auto reverse field info `%s` to `%s`", fi.fullName, ffi.fullName))
2013-07-30 12:32:38 +00:00
}
}
}
}
}
}
models = modelCache.all()
2013-07-30 12:32:38 +00:00
for _, mi := range models {
for _, fi := range mi.fields.fieldsRel {
switch fi.fieldType {
case RelManyToMany:
for _, ffi := range fi.relThroughModelInfo.fields.fieldsRel {
switch ffi.fieldType {
case RelOneToOne, RelForeignKey:
if ffi.relModelInfo == fi.relModelInfo {
fi.reverseFieldInfoTwo = ffi
}
if ffi.relModelInfo == mi {
fi.reverseField = ffi.name
fi.reverseFieldInfo = ffi
}
}
}
if fi.reverseFieldInfoTwo == nil {
err = fmt.Errorf("can not find m2m field for m2m model `%s`, ensure your m2m model defined correct",
fi.relThroughModelInfo.fullName)
goto end
}
}
}
2013-10-18 09:36:10 +00:00
}
models = modelCache.all()
for _, mi := range models {
for _, fi := range mi.fields.fieldsReverse {
switch fi.fieldType {
case RelReverseOne:
2013-07-30 12:32:38 +00:00
found := false
mForA:
for _, ffi := range fi.relModelInfo.fields.fieldsByType[RelOneToOne] {
if ffi.relModelInfo == mi {
found = true
fi.reverseField = ffi.name
fi.reverseFieldInfo = ffi
ffi.reverseField = fi.name
ffi.reverseFieldInfo = fi
2013-07-30 12:32:38 +00:00
break mForA
}
}
if found == false {
err = fmt.Errorf("reverse field `%s` not found in model `%s`", fi.fullName, fi.relModelInfo.fullName)
goto end
}
case RelReverseMany:
2013-07-30 12:32:38 +00:00
found := false
mForB:
for _, ffi := range fi.relModelInfo.fields.fieldsByType[RelForeignKey] {
if ffi.relModelInfo == mi {
found = true
fi.reverseField = ffi.name
fi.reverseFieldInfo = ffi
ffi.reverseField = fi.name
ffi.reverseFieldInfo = fi
2013-07-30 12:32:38 +00:00
break mForB
}
}
if found == false {
mForC:
for _, ffi := range fi.relModelInfo.fields.fieldsByType[RelManyToMany] {
2015-07-08 14:42:00 +00:00
conditions := fi.relThrough != "" && fi.relThrough == ffi.relThrough ||
fi.relTable != "" && fi.relTable == ffi.relTable ||
fi.relThrough == "" && fi.relTable == ""
if ffi.relModelInfo == mi && conditions {
2013-07-30 12:32:38 +00:00
found = true
fi.reverseField = ffi.reverseFieldInfoTwo.name
fi.reverseFieldInfo = ffi.reverseFieldInfoTwo
fi.relThroughModelInfo = ffi.relThroughModelInfo
fi.reverseFieldInfoTwo = ffi.reverseFieldInfo
fi.reverseFieldInfoM2M = ffi
ffi.reverseFieldInfoM2M = fi
2013-07-30 12:32:38 +00:00
break mForC
}
}
}
if found == false {
err = fmt.Errorf("reverse field for `%s` not found in model `%s`", fi.fullName, fi.relModelInfo.fullName)
2013-07-30 12:32:38 +00:00
goto end
}
}
}
}
end:
if err != nil {
fmt.Println(err)
os.Exit(2)
}
2013-08-07 11:11:44 +00:00
}
2013-07-30 12:32:38 +00:00
2015-09-12 13:46:43 +00:00
// RegisterModel register models
func RegisterModel(models ...interface{}) {
2016-09-06 15:05:41 +00:00
if modelCache.done {
panic(fmt.Errorf("RegisterModel must be run before BootStrap"))
}
2014-05-15 03:34:44 +00:00
RegisterModelWithPrefix("", models...)
2013-08-19 14:37:39 +00:00
}
2015-09-12 13:46:43 +00:00
// RegisterModelWithPrefix register models with a prefix
2013-08-19 14:37:39 +00:00
func RegisterModelWithPrefix(prefix string, models ...interface{}) {
if modelCache.done {
2016-09-06 15:05:41 +00:00
panic(fmt.Errorf("RegisterModelWithPrefix must be run before BootStrap"))
}
for _, model := range models {
registerModel(prefix, model, true)
}
}
// RegisterModelWithSuffix register models with a suffix
func RegisterModelWithSuffix(suffix string, models ...interface{}) {
if modelCache.done {
panic(fmt.Errorf("RegisterModelWithSuffix must be run before BootStrap"))
2013-08-07 11:11:44 +00:00
}
for _, model := range models {
2016-09-06 15:05:41 +00:00
registerModel(suffix, model, false)
2013-08-07 11:11:44 +00:00
}
}
2015-09-12 13:46:43 +00:00
// BootStrap bootrap models.
2014-01-17 15:28:54 +00:00
// make all model parsed and can not add more models
2013-08-07 11:11:44 +00:00
func BootStrap() {
if modelCache.done {
return
}
modelCache.Lock()
defer modelCache.Unlock()
bootStrap()
modelCache.done = true
2013-07-30 12:32:38 +00:00
}