forked from mirror/gorm
Add tests model definition and basic fields tests
This commit is contained in:
parent
fd9b688084
commit
14724ddeae
|
@ -22,7 +22,7 @@ var (
|
||||||
// gorm.Model
|
// gorm.Model
|
||||||
// }
|
// }
|
||||||
type Model struct {
|
type Model struct {
|
||||||
ID uint `gorm:"primary_key"`
|
ID uint `gorm:"primarykey"`
|
||||||
CreatedAt time.Time
|
CreatedAt time.Time
|
||||||
UpdatedAt time.Time
|
UpdatedAt time.Time
|
||||||
DeletedAt *time.Time `gorm:"index"`
|
DeletedAt *time.Time `gorm:"index"`
|
||||||
|
|
|
@ -54,7 +54,7 @@ func (schema *Schema) ParseField(fieldStruct reflect.StructField) *Field {
|
||||||
Creatable: true,
|
Creatable: true,
|
||||||
Updatable: true,
|
Updatable: true,
|
||||||
Tag: fieldStruct.Tag,
|
Tag: fieldStruct.Tag,
|
||||||
TagSettings: parseTagSetting(fieldStruct.Tag),
|
TagSettings: ParseTagSetting(fieldStruct.Tag),
|
||||||
}
|
}
|
||||||
|
|
||||||
for field.FieldType.Kind() == reflect.Ptr {
|
for field.FieldType.Kind() == reflect.Ptr {
|
||||||
|
@ -84,7 +84,7 @@ func (schema *Schema) ParseField(fieldStruct reflect.StructField) *Field {
|
||||||
}
|
}
|
||||||
|
|
||||||
// copy tag settings from valuer
|
// copy tag settings from valuer
|
||||||
for key, value := range parseTagSetting(field.FieldType.Field(i).Tag) {
|
for key, value := range ParseTagSetting(field.FieldType.Field(i).Tag) {
|
||||||
if _, ok := field.TagSettings[key]; !ok {
|
if _, ok := field.TagSettings[key]; !ok {
|
||||||
field.TagSettings[key] = value
|
field.TagSettings[key] = value
|
||||||
}
|
}
|
||||||
|
@ -141,7 +141,7 @@ func (schema *Schema) ParseField(fieldStruct reflect.StructField) *Field {
|
||||||
field.DBDataType = val
|
field.DBDataType = val
|
||||||
}
|
}
|
||||||
|
|
||||||
switch fieldValue.Kind() {
|
switch fieldValue.Elem().Kind() {
|
||||||
case reflect.Bool:
|
case reflect.Bool:
|
||||||
field.DataType = Bool
|
field.DataType = Bool
|
||||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||||
|
@ -153,7 +153,7 @@ func (schema *Schema) ParseField(fieldStruct reflect.StructField) *Field {
|
||||||
case reflect.String:
|
case reflect.String:
|
||||||
field.DataType = String
|
field.DataType = String
|
||||||
case reflect.Struct:
|
case reflect.Struct:
|
||||||
if _, ok := fieldValue.Interface().(time.Time); ok {
|
if _, ok := fieldValue.Interface().(*time.Time); ok {
|
||||||
field.DataType = Time
|
field.DataType = Time
|
||||||
}
|
}
|
||||||
case reflect.Array, reflect.Slice:
|
case reflect.Array, reflect.Slice:
|
||||||
|
@ -176,7 +176,7 @@ func (schema *Schema) ParseField(fieldStruct reflect.StructField) *Field {
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, ok := field.TagSettings["EMBEDDED"]; ok || fieldStruct.Anonymous {
|
if _, ok := field.TagSettings["EMBEDDED"]; ok || fieldStruct.Anonymous {
|
||||||
field.EmbeddedSchema, schema.err = Parse(fieldValue, sync.Map{}, schema.namer)
|
field.EmbeddedSchema, schema.err = Parse(fieldValue.Interface(), &sync.Map{}, schema.namer)
|
||||||
for _, ef := range field.EmbeddedSchema.Fields {
|
for _, ef := range field.EmbeddedSchema.Fields {
|
||||||
ef.BindNames = append([]string{fieldStruct.Name}, ef.BindNames...)
|
ef.BindNames = append([]string{fieldStruct.Name}, ef.BindNames...)
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,8 @@ import (
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
"github.com/jinzhu/gorm/logger"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Schema struct {
|
type Schema struct {
|
||||||
|
@ -20,7 +22,7 @@ type Schema struct {
|
||||||
Relationships Relationships
|
Relationships Relationships
|
||||||
err error
|
err error
|
||||||
namer Namer
|
namer Namer
|
||||||
cacheStore sync.Map
|
cacheStore *sync.Map
|
||||||
}
|
}
|
||||||
|
|
||||||
func (schema Schema) String() string {
|
func (schema Schema) String() string {
|
||||||
|
@ -38,7 +40,7 @@ func (schema Schema) LookUpField(name string) *Field {
|
||||||
}
|
}
|
||||||
|
|
||||||
// get data type from dialector
|
// get data type from dialector
|
||||||
func Parse(dest interface{}, cacheStore sync.Map, namer Namer) (*Schema, error) {
|
func Parse(dest interface{}, cacheStore *sync.Map, namer Namer) (*Schema, error) {
|
||||||
modelType := reflect.ValueOf(dest).Type()
|
modelType := reflect.ValueOf(dest).Type()
|
||||||
for modelType.Kind() == reflect.Slice || modelType.Kind() == reflect.Ptr {
|
for modelType.Kind() == reflect.Slice || modelType.Kind() == reflect.Ptr {
|
||||||
modelType = modelType.Elem()
|
modelType = modelType.Elem()
|
||||||
|
@ -62,10 +64,12 @@ func Parse(dest interface{}, cacheStore sync.Map, namer Namer) (*Schema, error)
|
||||||
FieldsByName: map[string]*Field{},
|
FieldsByName: map[string]*Field{},
|
||||||
FieldsByDBName: map[string]*Field{},
|
FieldsByDBName: map[string]*Field{},
|
||||||
cacheStore: cacheStore,
|
cacheStore: cacheStore,
|
||||||
|
namer: namer,
|
||||||
}
|
}
|
||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
if schema.err != nil {
|
if schema.err != nil {
|
||||||
|
logger.Default.Error(schema.err.Error())
|
||||||
cacheStore.Delete(modelType)
|
cacheStore.Delete(modelType)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
|
@ -0,0 +1,78 @@
|
||||||
|
package schema_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"reflect"
|
||||||
|
"sync"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/jinzhu/gorm/schema"
|
||||||
|
"github.com/jinzhu/gorm/tests"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestParseSchema(t *testing.T) {
|
||||||
|
cacheMap := sync.Map{}
|
||||||
|
user, err := schema.Parse(&tests.User{}, &cacheMap, schema.NamingStrategy{})
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("failed to parse user, got error %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
checkSchemaFields(t, user)
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkSchemaFields(t *testing.T, s *schema.Schema) {
|
||||||
|
fields := []schema.Field{
|
||||||
|
schema.Field{
|
||||||
|
Name: "ID", DBName: "id", BindNames: []string{"Model", "ID"}, DataType: schema.Uint,
|
||||||
|
PrimaryKey: true, Tag: `gorm:"primarykey"`, TagSettings: map[string]string{"PRIMARYKEY": "PRIMARYKEY"},
|
||||||
|
},
|
||||||
|
schema.Field{Name: "CreatedAt", DBName: "created_at", BindNames: []string{"Model", "CreatedAt"}, DataType: schema.Time},
|
||||||
|
schema.Field{Name: "UpdatedAt", DBName: "updated_at", BindNames: []string{"Model", "UpdatedAt"}, DataType: schema.Time},
|
||||||
|
schema.Field{Name: "DeletedAt", DBName: "deleted_at", BindNames: []string{"Model", "DeletedAt"}, Tag: `gorm:"index"`, DataType: schema.Time},
|
||||||
|
schema.Field{Name: "Name", DBName: "name", BindNames: []string{"Name"}, DataType: schema.String},
|
||||||
|
schema.Field{Name: "Age", DBName: "age", BindNames: []string{"Age"}, DataType: schema.Uint},
|
||||||
|
schema.Field{Name: "Birthday", DBName: "birthday", BindNames: []string{"Birthday"}, DataType: schema.Time},
|
||||||
|
schema.Field{Name: "CompanyID", DBName: "company_id", BindNames: []string{"CompanyID"}, DataType: schema.Int},
|
||||||
|
schema.Field{Name: "ManagerID", DBName: "manager_id", BindNames: []string{"ManagerID"}, DataType: schema.Uint},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, f := range fields {
|
||||||
|
f.Creatable = true
|
||||||
|
f.Updatable = true
|
||||||
|
if f.TagSettings == nil {
|
||||||
|
if f.Tag != "" {
|
||||||
|
f.TagSettings = schema.ParseTagSetting(f.Tag)
|
||||||
|
} else {
|
||||||
|
f.TagSettings = map[string]string{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if foundField, ok := s.FieldsByName[f.Name]; !ok {
|
||||||
|
t.Errorf("schema %v failed to look up field with name %v", s, f.Name)
|
||||||
|
} else {
|
||||||
|
checkSchemaField(t, foundField, f)
|
||||||
|
|
||||||
|
if field, ok := s.FieldsByDBName[f.DBName]; !ok || foundField != field {
|
||||||
|
t.Errorf("schema %v failed to look up field with dbname %v", s, f.DBName)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, name := range []string{f.DBName, f.Name} {
|
||||||
|
if field := s.LookUpField(name); field == nil || foundField != field {
|
||||||
|
t.Errorf("schema %v failed to look up field with dbname %v", s, f.DBName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkSchemaField(t *testing.T, parsedField *schema.Field, field schema.Field) {
|
||||||
|
equalFieldNames := []string{"Name", "DBName", "BindNames", "DataType", "DBDataType", "PrimaryKey", "AutoIncrement", "Creatable", "Updatable", "HasDefaultValue", "DefaultValue", "NotNull", "Unique", "Comment", "Size", "Precision", "Tag", "TagSettings"}
|
||||||
|
|
||||||
|
for _, name := range equalFieldNames {
|
||||||
|
got := reflect.ValueOf(parsedField).Elem().FieldByName(name).Interface()
|
||||||
|
expects := reflect.ValueOf(field).FieldByName(name).Interface()
|
||||||
|
if !reflect.DeepEqual(got, expects) {
|
||||||
|
t.Errorf("%v is not equal, expects: %v, got %v", name, expects, got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -6,7 +6,7 @@ import (
|
||||||
"strings"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
func parseTagSetting(tags reflect.StructTag) map[string]string {
|
func ParseTagSetting(tags reflect.StructTag) map[string]string {
|
||||||
setting := map[string]string{}
|
setting := map[string]string{}
|
||||||
|
|
||||||
for _, value := range strings.Split(tags.Get("gorm"), ";") {
|
for _, value := range strings.Split(tags.Get("gorm"), ";") {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
package gorm_test
|
package tests_test
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
|
@ -0,0 +1,58 @@
|
||||||
|
package tests
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/jinzhu/gorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
// User has one `Account` (has one), many `Pets` (has many) and `Toys` (has many - polymorphic)
|
||||||
|
// He works in a Company (belongs to), he has a Manager (belongs to - single-table), and also managed a Team (has many - single-table)
|
||||||
|
// He speaks many languages (many to many) and has many friends (many to many - single-table)
|
||||||
|
// His pet also has one Toy (has one - polymorphic)
|
||||||
|
type User struct {
|
||||||
|
gorm.Model
|
||||||
|
Name string
|
||||||
|
Age uint
|
||||||
|
Birthday *time.Time
|
||||||
|
Account Account
|
||||||
|
Pets []*Pet
|
||||||
|
Toys []Toy `gorm:"polymorphic:Owner"`
|
||||||
|
CompanyID *int
|
||||||
|
Company Company
|
||||||
|
ManagerID uint
|
||||||
|
Manager *User
|
||||||
|
Team []User `foreignkey:ManagerID`
|
||||||
|
Friends []*User `gorm:"many2many:user_friends"`
|
||||||
|
Languages []Language `gorm:"many2many:user_speaks"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Account struct {
|
||||||
|
gorm.Model
|
||||||
|
UserID sql.NullInt64
|
||||||
|
Number string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Pet struct {
|
||||||
|
gorm.Model
|
||||||
|
UserID uint
|
||||||
|
Name string
|
||||||
|
Toy Toy `gorm:"polymorphic:Owner;"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Toy struct {
|
||||||
|
gorm.Model
|
||||||
|
OwnerID string
|
||||||
|
OwnerType string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Company struct {
|
||||||
|
ID uint
|
||||||
|
Name string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Language struct {
|
||||||
|
Code string `gorm:primarykey`
|
||||||
|
Name string
|
||||||
|
}
|
Loading…
Reference in New Issue