mirror of https://github.com/go-gorm/gorm.git
Add FirstOrCreate support
This commit is contained in:
parent
ac8708b500
commit
dca5244387
|
@ -129,7 +129,7 @@ func (db *DB) FirstOrInit(dest interface{}, conds ...interface{}) (tx *DB) {
|
|||
|
||||
// initialize with attrs, conds
|
||||
if len(tx.Statement.attrs) > 0 {
|
||||
exprs := tx.Statement.BuildCondtion(tx.Statement.attrs[0], tx.Statement.attrs[1:])
|
||||
exprs := tx.Statement.BuildCondtion(tx.Statement.attrs[0], tx.Statement.attrs[1:]...)
|
||||
tx.assignExprsToValue(exprs)
|
||||
}
|
||||
tx.Error = nil
|
||||
|
@ -137,19 +137,54 @@ func (db *DB) FirstOrInit(dest interface{}, conds ...interface{}) (tx *DB) {
|
|||
|
||||
// initialize with attrs, conds
|
||||
if len(tx.Statement.assigns) > 0 {
|
||||
exprs := tx.Statement.BuildCondtion(tx.Statement.assigns[0], tx.Statement.assigns[1:])
|
||||
exprs := tx.Statement.BuildCondtion(tx.Statement.assigns[0], tx.Statement.assigns[1:]...)
|
||||
tx.assignExprsToValue(exprs)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (db *DB) FirstOrCreate(dest interface{}, where ...interface{}) (tx *DB) {
|
||||
func (db *DB) FirstOrCreate(dest interface{}, conds ...interface{}) (tx *DB) {
|
||||
tx = db.getInstance()
|
||||
// if err := tx.First(dest, conds...).Error; errors.Is(err, ErrRecordNotFound) {
|
||||
// // initialize with attrs, conds
|
||||
// }
|
||||
if err := tx.First(dest, conds...).Error; errors.Is(err, ErrRecordNotFound) {
|
||||
tx.Error = nil
|
||||
|
||||
if c, ok := tx.Statement.Clauses["WHERE"]; ok {
|
||||
if where, ok := c.Expression.(clause.Where); ok {
|
||||
tx.assignExprsToValue(where.Exprs)
|
||||
}
|
||||
}
|
||||
|
||||
// initialize with attrs, conds
|
||||
if len(tx.Statement.attrs) > 0 {
|
||||
exprs := tx.Statement.BuildCondtion(tx.Statement.attrs[0], tx.Statement.attrs[1:]...)
|
||||
tx.assignExprsToValue(exprs)
|
||||
}
|
||||
|
||||
// initialize with attrs, conds
|
||||
if len(tx.Statement.assigns) > 0 {
|
||||
exprs := tx.Statement.BuildCondtion(tx.Statement.assigns[0], tx.Statement.assigns[1:]...)
|
||||
tx.assignExprsToValue(exprs)
|
||||
}
|
||||
|
||||
return tx.Create(dest)
|
||||
} else if len(tx.Statement.assigns) > 0 {
|
||||
exprs := tx.Statement.BuildCondtion(tx.Statement.assigns[0], tx.Statement.assigns[1:])
|
||||
assigns := map[string]interface{}{}
|
||||
for _, expr := range exprs {
|
||||
if eq, ok := expr.(clause.Eq); ok {
|
||||
switch column := eq.Column.(type) {
|
||||
case string:
|
||||
assigns[column] = eq.Value
|
||||
case clause.Column:
|
||||
assigns[column.Name] = eq.Value
|
||||
default:
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return tx.Model(dest).Updates(assigns)
|
||||
}
|
||||
|
||||
// assign dest
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -307,3 +342,7 @@ func (db *DB) Exec(sql string, values ...interface{}) (tx *DB) {
|
|||
tx.callbacks.Raw().Execute(tx)
|
||||
return
|
||||
}
|
||||
|
||||
func (db *DB) RecordNotFound() bool {
|
||||
return errors.Is(db.Error, ErrRecordNotFound)
|
||||
}
|
||||
|
|
18
statement.go
18
statement.go
|
@ -203,6 +203,8 @@ func (stmt Statement) BuildCondtion(query interface{}, args ...interface{}) (con
|
|||
query = i
|
||||
} else if len(args) == 0 || (len(args) > 0 && strings.Contains(sql, "?")) || strings.Contains(sql, "@") {
|
||||
return []clause.Expression{clause.Expr{SQL: sql, Vars: args}}
|
||||
} else if len(args) == 1 {
|
||||
return []clause.Expression{clause.Eq{Column: sql, Value: args[0]}}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -238,16 +240,24 @@ func (stmt Statement) BuildCondtion(query interface{}, args ...interface{}) (con
|
|||
if s, err := schema.Parse(arg, stmt.DB.cacheStore, stmt.DB.NamingStrategy); err == nil {
|
||||
switch reflectValue.Kind() {
|
||||
case reflect.Struct:
|
||||
for _, field := range s.FieldsByDBName {
|
||||
for _, field := range s.Fields {
|
||||
if v, isZero := field.ValueOf(reflectValue); !isZero {
|
||||
conds = append(conds, clause.Eq{Column: field.DBName, Value: v})
|
||||
if field.DBName == "" {
|
||||
conds = append(conds, clause.Eq{Column: field.Name, Value: v})
|
||||
} else {
|
||||
conds = append(conds, clause.Eq{Column: field.DBName, Value: v})
|
||||
}
|
||||
}
|
||||
}
|
||||
case reflect.Slice, reflect.Array:
|
||||
for i := 0; i < reflectValue.Len(); i++ {
|
||||
for _, field := range s.FieldsByDBName {
|
||||
for _, field := range s.Fields {
|
||||
if v, isZero := field.ValueOf(reflectValue.Index(i)); !isZero {
|
||||
conds = append(conds, clause.Eq{Column: field.DBName, Value: v})
|
||||
if field.DBName == "" {
|
||||
conds = append(conds, clause.Eq{Column: field.Name, Value: v})
|
||||
} else {
|
||||
conds = append(conds, clause.Eq{Column: field.DBName, Value: v})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package tests_test
|
|||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
. "github.com/jinzhu/gorm/tests"
|
||||
)
|
||||
|
@ -53,4 +54,59 @@ func TestFindOrInitialize(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestFindOrCreate(t *testing.T) {
|
||||
var user1, user2, user3, user4, user5, user6, user7, user8 User
|
||||
DB.Where(&User{Name: "find or create", Age: 33}).FirstOrCreate(&user1)
|
||||
if user1.Name != "find or create" || user1.ID == 0 || user1.Age != 33 {
|
||||
t.Errorf("user should be created with search value")
|
||||
}
|
||||
|
||||
DB.Where(&User{Name: "find or create", Age: 33}).FirstOrCreate(&user2)
|
||||
if user1.ID != user2.ID || user2.Name != "find or create" || user2.ID == 0 || user2.Age != 33 {
|
||||
t.Errorf("user should be created with search value")
|
||||
}
|
||||
|
||||
DB.FirstOrCreate(&user3, map[string]interface{}{"name": "find or create 2"})
|
||||
if user3.Name != "find or create 2" || user3.ID == 0 {
|
||||
t.Errorf("user should be created with inline search value")
|
||||
}
|
||||
|
||||
DB.Where(&User{Name: "find or create 3"}).Attrs("age", 44).FirstOrCreate(&user4)
|
||||
if user4.Name != "find or create 3" || user4.ID == 0 || user4.Age != 44 {
|
||||
t.Errorf("user should be created with search value and attrs")
|
||||
}
|
||||
|
||||
updatedAt1 := user4.UpdatedAt
|
||||
DB.Where(&User{Name: "find or create 3"}).Assign("age", 55).FirstOrCreate(&user4)
|
||||
if updatedAt1.Format(time.RFC3339Nano) == user4.UpdatedAt.Format(time.RFC3339Nano) {
|
||||
t.Errorf("UpdateAt should be changed when update values with assign")
|
||||
}
|
||||
|
||||
DB.Where(&User{Name: "find or create 4"}).Assign(User{Age: 44}).FirstOrCreate(&user4)
|
||||
if user4.Name != "find or create 4" || user4.ID == 0 || user4.Age != 44 {
|
||||
t.Errorf("user should be created with search value and assigned attrs")
|
||||
}
|
||||
|
||||
DB.Where(&User{Name: "find or create"}).Attrs("age", 44).FirstOrInit(&user5)
|
||||
if user5.Name != "find or create" || user5.ID == 0 || user5.Age != 33 {
|
||||
t.Errorf("user should be found and not initialized by Attrs")
|
||||
}
|
||||
|
||||
DB.Where(&User{Name: "find or create"}).Assign(User{Age: 44}).FirstOrCreate(&user6)
|
||||
if user6.Name != "find or create" || user6.ID == 0 || user6.Age != 44 {
|
||||
t.Errorf("user should be found and updated with assigned attrs")
|
||||
}
|
||||
|
||||
DB.Where(&User{Name: "find or create"}).Find(&user7)
|
||||
if user7.Name != "find or create" || user7.ID == 0 || user7.Age != 44 {
|
||||
t.Errorf("user should be found and updated with assigned attrs")
|
||||
}
|
||||
|
||||
DB.Where(&User{Name: "find or create embedded struct"}).Assign(User{Age: 44, Account: Account{Number: "1231231231"}, Pets: []*Pet{{Name: "first_or_create_pet1"}, {Name: "first_or_create_pet2"}}}).FirstOrCreate(&user8)
|
||||
if DB.Where("name = ?", "first_or_create_pet1").First(&Pet{}).RecordNotFound() {
|
||||
t.Errorf("has many association should be saved")
|
||||
}
|
||||
|
||||
if DB.Where("number = ?", "1231231231").First(&Account{}).RecordNotFound() {
|
||||
t.Errorf("belongs to association should be saved")
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue