diff --git a/chain.go b/chain.go index 9e184334..3b95caad 100644 --- a/chain.go +++ b/chain.go @@ -24,6 +24,7 @@ type Chain struct { offsetStr string limitStr string specifiedTableName string + unscoped bool } func (s *Chain) err(err error) error { @@ -47,6 +48,7 @@ func (s *Chain) do(value interface{}) *Do { do.offsetStr = s.offsetStr do.limitStr = s.limitStr do.specifiedTableName = s.specifiedTableName + do.unscoped = s.unscoped s.value = value do.setModel(value) @@ -177,6 +179,11 @@ func (s *Chain) CreateTable(value interface{}) *Chain { return s } +func (s *Chain) Unscoped() *Chain { + s.unscoped = true + return s +} + func (s *Chain) Table(name string) *Chain { s.specifiedTableName = name return s diff --git a/do.go b/do.go index 9dcc2f3c..17f29b2c 100644 --- a/do.go +++ b/do.go @@ -7,6 +7,7 @@ import ( "reflect" "regexp" "strconv" + "time" "strings" ) @@ -25,13 +26,13 @@ type Do struct { sql string sqlVars []interface{} - whereClause []map[string]interface{} - orClause []map[string]interface{} - selectStr string - orderStrs []string - offsetStr string - limitStr string - + whereClause []map[string]interface{} + orClause []map[string]interface{} + selectStr string + orderStrs []string + offsetStr string + limitStr string + unscoped bool updateAttrs map[string]interface{} ignoreProtectedAttrs bool } @@ -184,9 +185,15 @@ func (s *Do) prepareDeleteSql() { func (s *Do) delete() { s.err(s.model.callMethod("BeforeDelete")) - s.prepareDeleteSql() if !s.hasError() { - s.exec() + if s.model.hasColumn("DeletedAt") { + delete_sql := "deleted_at=" + s.addToVars(time.Now()) + s.sql = fmt.Sprintf("UPDATE %v SET %v %v", s.tableName(), delete_sql, s.combinedSql()) + s.exec() + } else { + s.prepareDeleteSql() + s.exec() + } if !s.hasError() { s.err(s.model.callMethod("AfterDelete")) } diff --git a/gorm_test.go b/gorm_test.go index 2d9e1e49..334cfa68 100644 --- a/gorm_test.go +++ b/gorm_test.go @@ -53,7 +53,6 @@ func init() { if err != nil { fmt.Printf("Got error when try to delete table uses, %+v\n", err) } - db.Exec("drop table products;") orm := db.CreateTable(&User{}) @@ -691,3 +690,28 @@ func TestUpdates(t *testing.T) { t.Errorf("We should have Product fgh") } } + +func TestSoftDelete(t *testing.T) { + type Order struct { + Id int64 + Amount int64 + DeletedAt time.Time + } + db.Exec("drop table orders;") + db.CreateTable(&Order{}) + + order := Order{Amount: 1234} + db.Save(&order) + if db.Find(&Order{}, "amount = 1234").Error != nil { + t.Errorf("No errors should happen when save an order") + } + + db.Delete(&order) + if db.Find(&Order{}, "amount = 1234").Error == nil { + t.Errorf("Can't find the user because it is soft deleted") + } + + if db.Unscoped().Find(&Order{}, "amount = 1234").Error != nil { + t.Errorf("Should be able to find out the soft deleted user with unscoped") + } +} diff --git a/main.go b/main.go index c93efe7c..a27f8332 100644 --- a/main.go +++ b/main.go @@ -57,6 +57,10 @@ func (s *DB) Delete(value interface{}) *Chain { return s.buildChain().Delete(value) } +func (s *DB) Unscoped() *Chain { + return s.buildChain().Unscoped() +} + func (s *DB) Exec(sql string) *Chain { return s.buildChain().Exec(sql) } diff --git a/model.go b/model.go index 18db1385..a844b863 100644 --- a/model.go +++ b/model.go @@ -104,6 +104,15 @@ func (m *Model) columnsAndValues(operation string) map[string]interface{} { return results } +func (m *Model) hasColumn(name string) bool { + if m.data == nil { + return false + } + + value := reflect.ValueOf(m.data).Elem().FieldByName(name) + return value.IsValid() +} + func (m *Model) tableName() (str string, err error) { if m.data == nil { err = errors.New("Model haven't been set")