forked from mirror/gorm
fix: support zeroValue tag on DeletedAt (#6011)
* fix: support zeroValue tag on DeletedAt Signed-off-by: qiankunli <qiankun.li@qq.com> * Update soft_delete_test.go * Update tests_test.go * Update soft_delete.go --------- Signed-off-by: qiankunli <qiankun.li@qq.com> Co-authored-by: Jinzhu <wosmvp@gmail.com>
This commit is contained in:
parent
d834dd60b7
commit
cfbcedbf03
|
@ -6,6 +6,7 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
|
||||||
|
"github.com/jinzhu/now"
|
||||||
"gorm.io/gorm/clause"
|
"gorm.io/gorm/clause"
|
||||||
"gorm.io/gorm/schema"
|
"gorm.io/gorm/schema"
|
||||||
)
|
)
|
||||||
|
@ -45,11 +46,21 @@ func (n *DeletedAt) UnmarshalJSON(b []byte) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (DeletedAt) QueryClauses(f *schema.Field) []clause.Interface {
|
func (DeletedAt) QueryClauses(f *schema.Field) []clause.Interface {
|
||||||
return []clause.Interface{SoftDeleteQueryClause{Field: f}}
|
return []clause.Interface{SoftDeleteQueryClause{Field: f, ZeroValue: parseZeroValueTag(f)}}
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseZeroValueTag(f *schema.Field) sql.NullString {
|
||||||
|
if v, ok := f.TagSettings["ZEROVALUE"]; ok {
|
||||||
|
if _, err := now.Parse(v); err == nil {
|
||||||
|
return sql.NullString{String: v, Valid: true}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sql.NullString{Valid: false}
|
||||||
}
|
}
|
||||||
|
|
||||||
type SoftDeleteQueryClause struct {
|
type SoftDeleteQueryClause struct {
|
||||||
Field *schema.Field
|
ZeroValue sql.NullString
|
||||||
|
Field *schema.Field
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sd SoftDeleteQueryClause) Name() string {
|
func (sd SoftDeleteQueryClause) Name() string {
|
||||||
|
@ -78,18 +89,19 @@ func (sd SoftDeleteQueryClause) ModifyStatement(stmt *Statement) {
|
||||||
}
|
}
|
||||||
|
|
||||||
stmt.AddClause(clause.Where{Exprs: []clause.Expression{
|
stmt.AddClause(clause.Where{Exprs: []clause.Expression{
|
||||||
clause.Eq{Column: clause.Column{Table: clause.CurrentTable, Name: sd.Field.DBName}, Value: nil},
|
clause.Eq{Column: clause.Column{Table: clause.CurrentTable, Name: sd.Field.DBName}, Value: sd.ZeroValue},
|
||||||
}})
|
}})
|
||||||
stmt.Clauses["soft_delete_enabled"] = clause.Clause{}
|
stmt.Clauses["soft_delete_enabled"] = clause.Clause{}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (DeletedAt) UpdateClauses(f *schema.Field) []clause.Interface {
|
func (DeletedAt) UpdateClauses(f *schema.Field) []clause.Interface {
|
||||||
return []clause.Interface{SoftDeleteUpdateClause{Field: f}}
|
return []clause.Interface{SoftDeleteUpdateClause{Field: f, ZeroValue: parseZeroValueTag(f)}}
|
||||||
}
|
}
|
||||||
|
|
||||||
type SoftDeleteUpdateClause struct {
|
type SoftDeleteUpdateClause struct {
|
||||||
Field *schema.Field
|
ZeroValue sql.NullString
|
||||||
|
Field *schema.Field
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sd SoftDeleteUpdateClause) Name() string {
|
func (sd SoftDeleteUpdateClause) Name() string {
|
||||||
|
@ -109,11 +121,12 @@ func (sd SoftDeleteUpdateClause) ModifyStatement(stmt *Statement) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (DeletedAt) DeleteClauses(f *schema.Field) []clause.Interface {
|
func (DeletedAt) DeleteClauses(f *schema.Field) []clause.Interface {
|
||||||
return []clause.Interface{SoftDeleteDeleteClause{Field: f}}
|
return []clause.Interface{SoftDeleteDeleteClause{Field: f, ZeroValue: parseZeroValueTag(f)}}
|
||||||
}
|
}
|
||||||
|
|
||||||
type SoftDeleteDeleteClause struct {
|
type SoftDeleteDeleteClause struct {
|
||||||
Field *schema.Field
|
ZeroValue sql.NullString
|
||||||
|
Field *schema.Field
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sd SoftDeleteDeleteClause) Name() string {
|
func (sd SoftDeleteDeleteClause) Name() string {
|
||||||
|
|
|
@ -7,6 +7,7 @@ import (
|
||||||
"regexp"
|
"regexp"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/jinzhu/now"
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
. "gorm.io/gorm/utils/tests"
|
. "gorm.io/gorm/utils/tests"
|
||||||
)
|
)
|
||||||
|
@ -98,3 +99,71 @@ func TestDeletedAtOneOr(t *testing.T) {
|
||||||
t.Fatalf("invalid sql generated, got %v", actualSQL)
|
t.Fatalf("invalid sql generated, got %v", actualSQL)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestSoftDeleteZeroValue(t *testing.T) {
|
||||||
|
type SoftDeleteBook struct {
|
||||||
|
ID uint
|
||||||
|
Name string
|
||||||
|
Pages uint
|
||||||
|
DeletedAt gorm.DeletedAt `gorm:"zeroValue:'1970-01-01 00:00:01'"`
|
||||||
|
}
|
||||||
|
DB.Migrator().DropTable(&SoftDeleteBook{})
|
||||||
|
if err := DB.AutoMigrate(&SoftDeleteBook{}); err != nil {
|
||||||
|
t.Fatalf("failed to auto migrate soft delete table")
|
||||||
|
}
|
||||||
|
|
||||||
|
book := SoftDeleteBook{Name: "jinzhu", Pages: 10}
|
||||||
|
DB.Save(&book)
|
||||||
|
|
||||||
|
var count int64
|
||||||
|
if DB.Model(&SoftDeleteBook{}).Where("name = ?", book.Name).Count(&count).Error != nil || count != 1 {
|
||||||
|
t.Errorf("Count soft deleted record, expects: %v, got: %v", 1, count)
|
||||||
|
}
|
||||||
|
|
||||||
|
var pages uint
|
||||||
|
if DB.Model(&SoftDeleteBook{}).Select("pages").Where("name = ?", book.Name).Scan(&pages).Error != nil || pages != book.Pages {
|
||||||
|
t.Errorf("Pages soft deleted record, expects: %v, got: %v", 0, pages)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := DB.Delete(&book).Error; err != nil {
|
||||||
|
t.Fatalf("No error should happen when soft delete user, but got %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
zeroTime, _ := now.Parse("1970-01-01 00:00:01")
|
||||||
|
if book.DeletedAt.Time.Equal(zeroTime) {
|
||||||
|
t.Errorf("book's deleted at should not be zero, DeletedAt: %v", book.DeletedAt)
|
||||||
|
}
|
||||||
|
|
||||||
|
if DB.First(&SoftDeleteBook{}, "name = ?", book.Name).Error == nil {
|
||||||
|
t.Errorf("Can't find a soft deleted record")
|
||||||
|
}
|
||||||
|
|
||||||
|
count = 0
|
||||||
|
if DB.Model(&SoftDeleteBook{}).Where("name = ?", book.Name).Count(&count).Error != nil || count != 0 {
|
||||||
|
t.Errorf("Count soft deleted record, expects: %v, got: %v", 0, count)
|
||||||
|
}
|
||||||
|
|
||||||
|
pages = 0
|
||||||
|
if err := DB.Model(&SoftDeleteBook{}).Select("pages").Where("name = ?", book.Name).Scan(&pages).Error; err != nil || pages != 0 {
|
||||||
|
t.Fatalf("Age soft deleted record, expects: %v, got: %v, err %v", 0, pages, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := DB.Unscoped().First(&SoftDeleteBook{}, "name = ?", book.Name).Error; err != nil {
|
||||||
|
t.Errorf("Should find soft deleted record with Unscoped, but got err %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
count = 0
|
||||||
|
if DB.Unscoped().Model(&SoftDeleteBook{}).Where("name = ?", book.Name).Count(&count).Error != nil || count != 1 {
|
||||||
|
t.Errorf("Count soft deleted record, expects: %v, count: %v", 1, count)
|
||||||
|
}
|
||||||
|
|
||||||
|
pages = 0
|
||||||
|
if DB.Unscoped().Model(&SoftDeleteBook{}).Select("pages").Where("name = ?", book.Name).Scan(&pages).Error != nil || pages != book.Pages {
|
||||||
|
t.Errorf("Age soft deleted record, expects: %v, got: %v", 0, pages)
|
||||||
|
}
|
||||||
|
|
||||||
|
DB.Unscoped().Delete(&book)
|
||||||
|
if err := DB.Unscoped().First(&SoftDeleteBook{}, "name = ?", book.Name).Error; !errors.Is(err, gorm.ErrRecordNotFound) {
|
||||||
|
t.Errorf("Can't find permanently deleted record")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue