diff --git a/README.md b/README.md index 21707a8c..262925f5 100644 --- a/README.md +++ b/README.md @@ -84,6 +84,7 @@ type User struct{} // struct User's database table name is "users" by default, w * Use `CreatedAt` to store record's created time if field exists * Use `UpdatedAt` to store record's updated time if field exists * Use `DeletedAt` to store record's deleted time if field exists [Soft Delete](#soft-delete) +* Use nullable `Alive` to store record's aliveness if field exists [Soft Delete](#soft-delete) * Gorm provide a default model struct, you could embed it in your struct ```go @@ -446,10 +447,32 @@ db.Where("email LIKE ?", "%jinzhu%").Delete(Email{}) //// DELETE from emails where email LIKE "%jinhu%"; ``` -### Soft Delete +### Soft-Delete -If struct has `DeletedAt` field, it will get soft delete ability automatically! -Then it won't be deleted from database permanently when call `Delete`. +If a struct has a `DeletedAt` field, it will get soft-delete ability automatically! +Then it won't be deleted from database permanently when `Delete()` is called. + +Or alternatively.. + +If a struct has an `Alive` field it will get soft-delete ability automatically! +Then it won't be deleted from the database permanently when `Delete()` is called. +`Alive` will be set to `null`. This makes it possible to still enforce unique +constraints (e.g. "name", "alive"), but not have the unique constraint enforced +for "deleted" records. + +```go +type Friend struct { + ID int64 + Name string `sql:"type:varchar(255)` + Alive int `json:"-" sql:"type:integer;DEFAULT: 1;"` +} + +db.Delete(&friend) +//// UPDATE friends SET alive=null WHERE id = 42; +``` + +Note: Choose only one or the other. It won't work right if both `DeletedAt` and +`Alive` columns are defined. ```go db.Delete(&user) diff --git a/callback_delete.go b/callback_delete.go index 72236659..b806520e 100644 --- a/callback_delete.go +++ b/callback_delete.go @@ -8,13 +8,20 @@ func BeforeDelete(scope *Scope) { func Delete(scope *Scope) { if !scope.HasError() { - if !scope.Search.Unscoped && scope.HasColumn("DeletedAt") { + unscoped := scope.Search.Unscoped + if !unscoped && scope.HasColumn("DeletedAt") { scope.Raw( fmt.Sprintf("UPDATE %v SET deleted_at=%v %v", scope.QuotedTableName(), scope.AddToVars(NowFunc()), scope.CombinedConditionSql(), )) + } else if !unscoped && scope.HasColumn("Alive") { + scope.Raw( + fmt.Sprintf("UPDATE %v SET alive=null %v", + scope.QuotedTableName(), + scope.CombinedConditionSql(), + )) } else { scope.Raw(fmt.Sprintf("DELETE FROM %v %v", scope.QuotedTableName(), scope.CombinedConditionSql())) } diff --git a/scope_private.go b/scope_private.go index 63fcea46..d4d1343e 100644 --- a/scope_private.go +++ b/scope_private.go @@ -158,9 +158,14 @@ func (scope *Scope) buildSelectQuery(clause map[string]interface{}) (str string) func (scope *Scope) whereSql() (sql string) { var primaryConditions, andConditions, orConditions []string - if !scope.Search.Unscoped && scope.Fields()["deleted_at"] != nil { - sql := fmt.Sprintf("(%v.deleted_at IS NULL OR %v.deleted_at <= '0001-01-02')", scope.QuotedTableName(), scope.QuotedTableName()) - primaryConditions = append(primaryConditions, sql) + if !scope.Search.Unscoped { + if scope.Fields()["deleted_at"] != nil { + sql := fmt.Sprintf("(%v.deleted_at IS NULL OR %v.deleted_at <= '0001-01-02')", scope.QuotedTableName(), scope.QuotedTableName()) + primaryConditions = append(primaryConditions, sql) + } else if scope.Fields()["alive"] != nil { + sql := fmt.Sprintf("(%v.alive IS NOT NULL)", scope.QuotedTableName()) + primaryConditions = append(primaryConditions, sql) + } } if !scope.PrimaryKeyZero() {