Add Method UpdateColumn, UpdateColumns

This commit is contained in:
Jinzhu 2013-11-17 21:39:50 +08:00
parent 5411291173
commit 1a2eef181a
5 changed files with 94 additions and 29 deletions

View File

@ -320,7 +320,7 @@ db.Where("role = ?", "admin").Or("role = ?", "super_admin").Not("name = ?", "jin
user.Name = "jinzhu 2"
user.Age = 100
db.Save(&user)
//// UPDATE users SET name='jinzhu 2', age=100 WHERE id=111;
//// UPDATE users SET name='jinzhu 2', age=100, updated_at = '2013-11-17 21:34:10' WHERE id=111;
```
### Update one attribute with `Update`
@ -328,12 +328,12 @@ db.Save(&user)
```go
// Update existing user's name if it is changed
db.Model(&user).Update("name", "hello")
//// UPDATE users SET name='hello' WHERE id=111;
//// UPDATE users SET name='hello', updated_at = '2013-11-17 21:34:10' WHERE id=111;
// Find out a user, and update the name if it is changed
db.First(&user, 111).Update("name", "hello")
//// SELECT * FROM users LIMIT 1;
//// UPDATE users SET name='hello' WHERE id=111;
//// UPDATE users SET name='hello', updated_at = '2013-11-17 21:34:10' WHERE id=111;
// Update name with search condiation and specified table name
db.Table("users").Where(10).Update("name", "hello")
@ -345,7 +345,7 @@ db.Table("users").Where(10).Update("name", "hello")
```go
// Update user's name and age if they are changed
db.Model(&user).Updates(User{Name: "hello", Age: 18})
//// UPDATE users SET name='hello', age=18 WHERE id = 111;
//// UPDATE users SET name='hello', age=18, updated_at = '2013-11-17 21:34:10' WHERE id = 111;
// Updates with Map
db.Table("users").Where(10).Updates(map[string]interface{}{"name": "hello", "age": 18})
@ -356,6 +356,16 @@ db.Model(User{}).Updates(User{Name: "hello", Age: 18})
//// UPDATE users SET name='hello', age=18;
```
### Update attributes without callbacks
```go
db.Model(&user).UpdateColumn("name", "hello")
//// UPDATE users SET name='hello' WHERE id = 111;
db.Model(&user).UpdateColumns(User{Name: "hello", Age: 18})
//// UPDATE users SET name='hello', age=18 WHERE id = 111;
```
## Delete
### Delete an existing struct
@ -780,7 +790,6 @@ db.Where("email = ?", "x@example.org").Attrs(User{RegisteredIp: "111.111.111.111
```
## TODO
* UpdateColumn/Columns
* Scopes
* Joins
* Scan

65
do.go
View File

@ -199,42 +199,51 @@ func (s *Do) create() (i interface{}) {
return
}
func (s *Do) convertToMapInterface(values interface{}) map[string]interface{} {
attrs := map[string]interface{}{}
switch value := values.(type) {
case map[string]interface{}:
attrs = value
case []interface{}:
for _, v := range value {
for key, value := range s.convertToMapInterface(v) {
attrs[key] = value
}
}
case interface{}:
m := &Model{data: values, do: s}
for _, field := range m.columnsHasValue("other") {
attrs[field.dbName] = field.Value
}
}
return attrs
}
func (s *Do) updateAttrs(values interface{}, ignore_protected_attrs ...bool) *Do {
ignore_protected := len(ignore_protected_attrs) > 0 && ignore_protected_attrs[0]
s.usingUpdate = true
switch value := values.(type) {
case map[string]interface{}:
if len(value) > 0 {
results, has_update := s.model.updatedColumnsAndValues(value, ignore_protected)
if len(results) > 0 {
s.update_attrs = results
}
s.hasUpdate = has_update
if maps := s.convertToMapInterface(values); len(maps) > 0 {
results, has_update := s.model.updatedColumnsAndValues(maps, ignore_protected)
if len(results) > 0 {
s.update_attrs = results
}
case []interface{}:
for _, v := range value {
s.updateAttrs(v)
}
case interface{}:
m := &Model{data: values, do: s}
attrs := map[string]interface{}{}
for _, field := range m.columnsHasValue("other") {
attrs[field.dbName] = field.Value
}
s.updateAttrs(attrs)
s.hasUpdate = has_update
}
return s
}
func (s *Do) prepareUpdateSql() {
func (s *Do) prepareUpdateSql(include_self bool) {
var sqls []string
for key, value := range s.update_attrs {
sqls = append(sqls, fmt.Sprintf("%v = %v", key, s.addToVars(value)))
}
for key, value := range s.model.columnsAndValues("update") {
sqls = append(sqls, fmt.Sprintf("%v = %v", key, s.addToVars(value)))
if include_self {
for key, value := range s.model.columnsAndValues("update") {
sqls = append(sqls, fmt.Sprintf("%v = %v", key, s.addToVars(value)))
}
}
s.sql = fmt.Sprintf(
@ -246,6 +255,16 @@ func (s *Do) prepareUpdateSql() {
return
}
func (s *Do) updateColumns(value interface{}) *Do {
s.update_attrs = s.convertToMapInterface(value)
s.prepareUpdateSql(false)
if !s.db.hasError() {
s.exec()
s.updateAttrs(s.update_attrs)
}
return s
}
func (s *Do) update() *Do {
if s.usingUpdate && !s.hasUpdate {
return s
@ -255,7 +274,7 @@ func (s *Do) update() *Do {
s.model.callMethod("BeforeSave")
s.saveBeforeAssociations()
s.prepareUpdateSql()
s.prepareUpdateSql(true)
if !s.db.hasError() {
s.exec()

View File

@ -900,6 +900,31 @@ func TestUpdates(t *testing.T) {
}
}
func TestUpdateColumn(t *testing.T) {
product1 := Product{Code: "update_column 1", Price: 10}
product2 := Product{Code: "update_column 2", Price: 20}
db.Save(&product1).Save(&product2).UpdateColumn(map[string]interface{}{"code": "update_column 3", "price": 100})
if product2.Code != "update_column 3" || product2.Price != 100 {
t.Errorf("product 2 should be updated with update column")
}
var product3 Product
db.First(&product3, product1.Id)
if product3.Code != "update_column 1" || product3.Price != 10 {
t.Errorf("product 1 should not be updated")
}
var product4, product5 Product
db.First(&product4, product2.Id)
updated_at1 := product4.UpdatedAt
db.Model(Product{}).Where(product2.Id).UpdateColumn("code", "update_column_new")
db.First(&product5, product2.Id)
if updated_at1.Format(time.RFC3339Nano) != product5.UpdatedAt.Format(time.RFC3339Nano) {
t.Errorf("updated_at should not be updated with update column")
}
}
func TestSoftDelete(t *testing.T) {
type Order struct {
Id int64
@ -1444,7 +1469,7 @@ func BenchmarkGorm(b *testing.B) {
// Query
db.First(&BigEmail{}, "email = ?", e)
// Update
db.Model(&email).Update("email", "new-"+e)
db.Model(&email).UpdateColumn("email", "new-"+e)
// Delete
db.Delete(&email)
}

View File

@ -153,6 +153,14 @@ func (s *DB) Updates(values interface{}, ignore_protected_attrs ...bool) *DB {
return s.clone().do(s.data).begin().updateAttrs(values, ignore_protected_attrs...).update().commit_or_rollback().db
}
func (s *DB) UpdateColumn(attrs ...interface{}) *DB {
return s.UpdateColumns(toSearchableMap(attrs...), true)
}
func (s *DB) UpdateColumns(values interface{}, ignore_protected_attrs ...bool) *DB {
return s.clone().do(s.data).begin().updateColumns(values).commit_or_rollback().db
}
func (s *DB) Save(value interface{}) *DB {
return s.clone().do(value).begin().save().commit_or_rollback().db
}

View File

@ -134,6 +134,10 @@ func (m *Model) updatedColumnsAndValues(values map[string]interface{}, ignore_pr
}
data := m.reflectData()
if !data.CanAddr() {
return
}
for key, value := range values {
if field := data.FieldByName(snakeToUpperCamel(key)); field.IsValid() {
if field.Interface() != value {