CanZero specifies fields that you can be set to zero value when creating and updating.
This commit is contained in:
parent
7701c88507
commit
386d981a98
@ -328,9 +328,19 @@ func ConvertToCreateValues(stmt *gorm.Statement) (values clause.Values) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
canZeroColumns := stmt.CanZeroColumns(true, false)
|
||||||
for _, field := range stmt.Schema.FieldsWithDefaultDBValue {
|
for _, field := range stmt.Schema.FieldsWithDefaultDBValue {
|
||||||
if v, ok := selectColumns[field.DBName]; (ok && v) || (!ok && !restricted) {
|
if v, ok := selectColumns[field.DBName]; (ok && v) || (!ok && !restricted) {
|
||||||
if v, isZero := field.ValueOf(stmt.ReflectValue); !isZero {
|
v, isZero := field.ValueOf(stmt.ReflectValue)
|
||||||
|
|
||||||
|
// can zero
|
||||||
|
if isZero {
|
||||||
|
if v, ok := canZeroColumns[field.DBName]; ok && v {
|
||||||
|
isZero = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !isZero {
|
||||||
values.Columns = append(values.Columns, clause.Column{Name: field.DBName})
|
values.Columns = append(values.Columns, clause.Column{Name: field.DBName})
|
||||||
values.Values[0] = append(values.Values[0], v)
|
values.Values[0] = append(values.Values[0], v)
|
||||||
}
|
}
|
||||||
|
@ -222,12 +222,21 @@ func ConvertToAssignments(stmt *gorm.Statement) (set clause.Set) {
|
|||||||
default:
|
default:
|
||||||
switch updatingValue.Kind() {
|
switch updatingValue.Kind() {
|
||||||
case reflect.Struct:
|
case reflect.Struct:
|
||||||
|
canZeroColumns := stmt.CanZeroColumns(false, true)
|
||||||
set = make([]clause.Assignment, 0, len(stmt.Schema.FieldsByDBName))
|
set = make([]clause.Assignment, 0, len(stmt.Schema.FieldsByDBName))
|
||||||
for _, dbName := range stmt.Schema.DBNames {
|
for _, dbName := range stmt.Schema.DBNames {
|
||||||
field := stmt.Schema.LookUpField(dbName)
|
field := stmt.Schema.LookUpField(dbName)
|
||||||
if !field.PrimaryKey || (!updatingValue.CanAddr() || stmt.Dest != stmt.Model) {
|
if !field.PrimaryKey || (!updatingValue.CanAddr() || stmt.Dest != stmt.Model) {
|
||||||
if v, ok := selectColumns[field.DBName]; (ok && v) || (!ok && (!restricted || (!stmt.SkipHooks && field.AutoUpdateTime > 0))) {
|
if v, ok := selectColumns[field.DBName]; (ok && v) || (!ok && (!restricted || (!stmt.SkipHooks && field.AutoUpdateTime > 0))) {
|
||||||
value, isZero := field.ValueOf(updatingValue)
|
value, isZero := field.ValueOf(updatingValue)
|
||||||
|
|
||||||
|
// can zero
|
||||||
|
if isZero {
|
||||||
|
if v, ok := canZeroColumns[field.DBName]; ok && v {
|
||||||
|
isZero = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if !stmt.SkipHooks && field.AutoUpdateTime > 0 {
|
if !stmt.SkipHooks && field.AutoUpdateTime > 0 {
|
||||||
if field.AutoUpdateTime == schema.UnixNanosecond {
|
if field.AutoUpdateTime == schema.UnixNanosecond {
|
||||||
value = stmt.DB.NowFunc().UnixNano()
|
value = stmt.DB.NowFunc().UnixNano()
|
||||||
|
@ -142,6 +142,19 @@ func (db *DB) Omit(columns ...string) (tx *DB) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CanZero specifies fields that you can be set to zero value when creating and updating.
|
||||||
|
// Priority is lower than Select and Omit
|
||||||
|
func (db *DB) CanZero(columns ...string) (tx *DB) {
|
||||||
|
tx = db.getInstance()
|
||||||
|
|
||||||
|
if len(columns) == 1 && strings.ContainsRune(columns[0], ',') {
|
||||||
|
tx.Statement.CanZeros = strings.FieldsFunc(columns[0], utils.IsValidDBNameChar)
|
||||||
|
} else {
|
||||||
|
tx.Statement.CanZeros = columns
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Where add conditions
|
// Where add conditions
|
||||||
func (db *DB) Where(query interface{}, args ...interface{}) (tx *DB) {
|
func (db *DB) Where(query interface{}, args ...interface{}) (tx *DB) {
|
||||||
tx = db.getInstance()
|
tx = db.getInstance()
|
||||||
|
42
statement.go
42
statement.go
@ -30,6 +30,7 @@ type Statement struct {
|
|||||||
Distinct bool
|
Distinct bool
|
||||||
Selects []string // selected columns
|
Selects []string // selected columns
|
||||||
Omits []string // omit columns
|
Omits []string // omit columns
|
||||||
|
CanZeros []string // can zero columns, priority is lower than selected columns and omit columns
|
||||||
Joins []join
|
Joins []join
|
||||||
Preloads map[string][]interface{}
|
Preloads map[string][]interface{}
|
||||||
Settings sync.Map
|
Settings sync.Map
|
||||||
@ -666,3 +667,44 @@ func (stmt *Statement) SelectAndOmitColumns(requireCreate, requireUpdate bool) (
|
|||||||
|
|
||||||
return results, !notRestricted && len(stmt.Selects) > 0
|
return results, !notRestricted && len(stmt.Selects) > 0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CanZeroColumns get can zero columns
|
||||||
|
func (stmt *Statement) CanZeroColumns(requireCreate, requireUpdate bool) map[string]bool {
|
||||||
|
results := map[string]bool{}
|
||||||
|
|
||||||
|
// can zero columns
|
||||||
|
for _, canZero := range stmt.CanZeros {
|
||||||
|
if stmt.Schema == nil {
|
||||||
|
results[canZero] = true
|
||||||
|
} else if canZero == "*" {
|
||||||
|
for _, dbName := range stmt.Schema.DBNames {
|
||||||
|
results[dbName] = true
|
||||||
|
}
|
||||||
|
} else if canZero == clause.Associations {
|
||||||
|
for _, rel := range stmt.Schema.Relationships.Relations {
|
||||||
|
results[rel.Name] = true
|
||||||
|
}
|
||||||
|
} else if field := stmt.Schema.LookUpField(canZero); field != nil && field.DBName != "" {
|
||||||
|
results[field.DBName] = true
|
||||||
|
} else {
|
||||||
|
results[canZero] = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if stmt.Schema != nil {
|
||||||
|
for _, field := range stmt.Schema.FieldsByName {
|
||||||
|
name := field.DBName
|
||||||
|
if name == "" {
|
||||||
|
name = field.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
if requireCreate && !field.Creatable {
|
||||||
|
results[name] = false
|
||||||
|
} else if requireUpdate && !field.Updatable {
|
||||||
|
results[name] = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return results
|
||||||
|
}
|
||||||
|
@ -685,3 +685,32 @@ func TestSaveWithPrimaryValue(t *testing.T) {
|
|||||||
t.Errorf("failed to find created record, got error: %v, result: %+v", err, result4)
|
t.Errorf("failed to find created record, got error: %v, result: %+v", err, result4)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestCanZeroWithUpdate(t *testing.T) {
|
||||||
|
user := *GetUser("can_zero_update", Config{})
|
||||||
|
user.Active = true
|
||||||
|
DB.Create(&user)
|
||||||
|
|
||||||
|
var result User
|
||||||
|
DB.First(&result, user.ID)
|
||||||
|
|
||||||
|
user2 := *GetUser("can_zero_update_new", Config{})
|
||||||
|
result.Name = user2.Name
|
||||||
|
result.Active = false
|
||||||
|
result.Age = 0
|
||||||
|
|
||||||
|
DB.Model(User{}).Where("ID", user.ID).CanZero("Age").Updates(User{
|
||||||
|
Name: user2.Name,
|
||||||
|
Active: false,
|
||||||
|
Age: 0,
|
||||||
|
})
|
||||||
|
|
||||||
|
var result2 User
|
||||||
|
DB.First(&result2, user.ID)
|
||||||
|
|
||||||
|
AssertObjEqual(t, result2, result, "Name", "Age")
|
||||||
|
|
||||||
|
if !result2.Active || result.Active {
|
||||||
|
t.Fatalf("Update struct should only update can zero columns, was %+v, got %+v", result2.Active, result.Active)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user