fix: serializer use default valueOf in assignInterfacesToValue
This commit is contained in:
		
							parent
							
								
									61b4c31236
								
							
						
					
					
						commit
						fb9233011d
					
				@ -240,6 +240,10 @@ func (tx *DB) assignInterfacesToValue(values ...interface{}) {
 | 
				
			|||||||
						if f.Readable {
 | 
											if f.Readable {
 | 
				
			||||||
							if v, isZero := f.ValueOf(tx.Statement.Context, reflectValue); !isZero {
 | 
												if v, isZero := f.ValueOf(tx.Statement.Context, reflectValue); !isZero {
 | 
				
			||||||
								if field := tx.Statement.Schema.LookUpField(f.Name); field != nil {
 | 
													if field := tx.Statement.Schema.LookUpField(f.Name); field != nil {
 | 
				
			||||||
 | 
														// serializer should use default implementation of ValueOf when assign to value
 | 
				
			||||||
 | 
														if field.Serializer != nil {
 | 
				
			||||||
 | 
															v, _ = field.DefaultValueOf(tx.Statement.Context, reflectValue)
 | 
				
			||||||
 | 
														}
 | 
				
			||||||
									tx.AddError(field.Set(tx.Statement.Context, tx.Statement.ReflectValue, v))
 | 
														tx.AddError(field.Set(tx.Statement.Context, tx.Statement.ReflectValue, v))
 | 
				
			||||||
								}
 | 
													}
 | 
				
			||||||
							}
 | 
												}
 | 
				
			||||||
 | 
				
			|||||||
@ -430,39 +430,7 @@ func (schema *Schema) ParseField(fieldStruct reflect.StructField) *Field {
 | 
				
			|||||||
// create valuer, setter when parse struct
 | 
					// create valuer, setter when parse struct
 | 
				
			||||||
func (field *Field) setupValuerAndSetter() {
 | 
					func (field *Field) setupValuerAndSetter() {
 | 
				
			||||||
	// Setup NewValuePool
 | 
						// Setup NewValuePool
 | 
				
			||||||
	var fieldValue = reflect.New(field.FieldType).Interface()
 | 
						field.setupNewValuePool()
 | 
				
			||||||
	if field.Serializer != nil {
 | 
					 | 
				
			||||||
		field.NewValuePool = &sync.Pool{
 | 
					 | 
				
			||||||
			New: func() interface{} {
 | 
					 | 
				
			||||||
				return &serializer{
 | 
					 | 
				
			||||||
					Field:      field,
 | 
					 | 
				
			||||||
					Serializer: reflect.New(reflect.Indirect(reflect.ValueOf(field.Serializer)).Type()).Interface().(SerializerInterface),
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			},
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	} else if _, ok := fieldValue.(sql.Scanner); !ok {
 | 
					 | 
				
			||||||
		// set default NewValuePool
 | 
					 | 
				
			||||||
		switch field.IndirectFieldType.Kind() {
 | 
					 | 
				
			||||||
		case reflect.String:
 | 
					 | 
				
			||||||
			field.NewValuePool = stringPool
 | 
					 | 
				
			||||||
		case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
 | 
					 | 
				
			||||||
			field.NewValuePool = intPool
 | 
					 | 
				
			||||||
		case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
 | 
					 | 
				
			||||||
			field.NewValuePool = uintPool
 | 
					 | 
				
			||||||
		case reflect.Float32, reflect.Float64:
 | 
					 | 
				
			||||||
			field.NewValuePool = floatPool
 | 
					 | 
				
			||||||
		case reflect.Bool:
 | 
					 | 
				
			||||||
			field.NewValuePool = boolPool
 | 
					 | 
				
			||||||
		default:
 | 
					 | 
				
			||||||
			if field.IndirectFieldType == TimeReflectType {
 | 
					 | 
				
			||||||
				field.NewValuePool = timePool
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if field.NewValuePool == nil {
 | 
					 | 
				
			||||||
		field.NewValuePool = poolInitializer(reflect.PtrTo(field.IndirectFieldType))
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// ValueOf returns field's value and if it is zero
 | 
						// ValueOf returns field's value and if it is zero
 | 
				
			||||||
	fieldIndex := field.StructField.Index[0]
 | 
						fieldIndex := field.StructField.Index[0]
 | 
				
			||||||
@ -954,3 +922,66 @@ func (field *Field) setupValuerAndSetter() {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (field *Field) DefaultValueOf(ctx context.Context, v reflect.Value) (interface{}, bool) {
 | 
				
			||||||
 | 
						fieldIndex := field.StructField.Index[0]
 | 
				
			||||||
 | 
						if len(field.StructField.Index) == 1 && fieldIndex > 0 {
 | 
				
			||||||
 | 
							fieldValue := reflect.Indirect(v).Field(fieldIndex)
 | 
				
			||||||
 | 
							return fieldValue.Interface(), fieldValue.IsZero()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						v = reflect.Indirect(v)
 | 
				
			||||||
 | 
						for _, fieldIdx := range field.StructField.Index {
 | 
				
			||||||
 | 
							if fieldIdx >= 0 {
 | 
				
			||||||
 | 
								v = v.Field(fieldIdx)
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								v = v.Field(-fieldIdx - 1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if !v.IsNil() {
 | 
				
			||||||
 | 
									v = v.Elem()
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
 | 
									return nil, true
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return v.Interface(), v.IsZero()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (field *Field) setupNewValuePool() {
 | 
				
			||||||
 | 
						var fieldValue = reflect.New(field.FieldType).Interface()
 | 
				
			||||||
 | 
						if field.Serializer != nil {
 | 
				
			||||||
 | 
							field.NewValuePool = &sync.Pool{
 | 
				
			||||||
 | 
								New: func() interface{} {
 | 
				
			||||||
 | 
									return &serializer{
 | 
				
			||||||
 | 
										Field:      field,
 | 
				
			||||||
 | 
										Serializer: reflect.New(reflect.Indirect(reflect.ValueOf(field.Serializer)).Type()).Interface().(SerializerInterface),
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						} else if _, ok := fieldValue.(sql.Scanner); !ok {
 | 
				
			||||||
 | 
							field.setupDefaultNewValuePool()
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if field.NewValuePool == nil {
 | 
				
			||||||
 | 
							field.NewValuePool = poolInitializer(reflect.PtrTo(field.IndirectFieldType))
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func (field *Field) setupDefaultNewValuePool() {
 | 
				
			||||||
 | 
						// set default NewValuePool
 | 
				
			||||||
 | 
						switch field.IndirectFieldType.Kind() {
 | 
				
			||||||
 | 
						case reflect.String:
 | 
				
			||||||
 | 
							field.NewValuePool = stringPool
 | 
				
			||||||
 | 
						case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
 | 
				
			||||||
 | 
							field.NewValuePool = intPool
 | 
				
			||||||
 | 
						case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
 | 
				
			||||||
 | 
							field.NewValuePool = uintPool
 | 
				
			||||||
 | 
						case reflect.Float32, reflect.Float64:
 | 
				
			||||||
 | 
							field.NewValuePool = floatPool
 | 
				
			||||||
 | 
						case reflect.Bool:
 | 
				
			||||||
 | 
							field.NewValuePool = boolPool
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							if field.IndirectFieldType == TimeReflectType {
 | 
				
			||||||
 | 
								field.NewValuePool = timePool
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
@ -83,4 +83,52 @@ func TestSerializer(t *testing.T) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	AssertEqual(t, result, data)
 | 
						AssertEqual(t, result, data)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func TestSerializer_AssignFirstOrCreate(t *testing.T) {
 | 
				
			||||||
 | 
						DB.Migrator().DropTable(&SerializerStruct{})
 | 
				
			||||||
 | 
						if err := DB.Migrator().AutoMigrate(&SerializerStruct{}); err != nil {
 | 
				
			||||||
 | 
							t.Fatalf("no error should happen when migrate scanner, valuer struct, got error %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						createdAt := time.Date(2020, 1, 1, 0, 0, 0, 0, time.UTC)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						data := SerializerStruct{
 | 
				
			||||||
 | 
							Name:            []byte("ag9920"),
 | 
				
			||||||
 | 
							Roles:           []string{"r1", "r2"},
 | 
				
			||||||
 | 
							Contracts:       map[string]interface{}{"name": "jing1", "age": 11},
 | 
				
			||||||
 | 
							EncryptedString: EncryptedString("pass"),
 | 
				
			||||||
 | 
							CreatedTime:     createdAt.Unix(),
 | 
				
			||||||
 | 
							JobInfo: Job{
 | 
				
			||||||
 | 
								Title:    "programmer",
 | 
				
			||||||
 | 
								Number:   9920,
 | 
				
			||||||
 | 
								Location: "Shadyside",
 | 
				
			||||||
 | 
								IsIntern: false,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// first time insert record
 | 
				
			||||||
 | 
						out := SerializerStruct{}
 | 
				
			||||||
 | 
						if err := DB.Assign(data).FirstOrCreate(&out).Error; err != nil {
 | 
				
			||||||
 | 
							t.Fatalf("failed to FirstOrCreate Assigned data, got error %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						var result SerializerStruct
 | 
				
			||||||
 | 
						if err := DB.First(&result, out.ID).Error; err != nil {
 | 
				
			||||||
 | 
							t.Fatalf("failed to query data, got error %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						AssertEqual(t, result, out)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						//update record
 | 
				
			||||||
 | 
						data.Roles = append(data.Roles, "r3")
 | 
				
			||||||
 | 
						data.JobInfo.Location = "Gates Hillman Complex"
 | 
				
			||||||
 | 
						if err := DB.Assign(data).FirstOrCreate(&out).Error; err != nil {
 | 
				
			||||||
 | 
							t.Fatalf("failed to FirstOrCreate Assigned data, got error %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if err := DB.First(&result, out.ID).Error; err != nil {
 | 
				
			||||||
 | 
							t.Fatalf("failed to query data, got error %v", err)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						AssertEqual(t, result.Roles, data.Roles)
 | 
				
			||||||
 | 
						AssertEqual(t, result.JobInfo.Location, data.JobInfo.Location)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user