package gorm import ( "fmt" "strings" ) // Define callbacks for creating func init() { DefaultCallback.Create().Register("gorm:begin_transaction", beginTransactionCallback) DefaultCallback.Create().Register("gorm:before_create", beforeCreateCallback) DefaultCallback.Create().Register("gorm:save_before_associations", saveBeforeAssociationsCallback) DefaultCallback.Create().Register("gorm:update_time_stamp", updateTimeStampForCreateCallback) DefaultCallback.Create().Register("gorm:create", createCallback) DefaultCallback.Create().Register("gorm:force_reload_after_create", forceReloadAfterCreateCallback) DefaultCallback.Create().Register("gorm:save_after_associations", saveAfterAssociationsCallback) DefaultCallback.Create().Register("gorm:after_create", afterCreateCallback) DefaultCallback.Create().Register("gorm:commit_or_rollback_transaction", commitOrRollbackTransactionCallback) } // beforeCreateCallback will invoke `BeforeSave`, `BeforeCreate` method before creating func beforeCreateCallback(scope *Scope) { if !scope.HasError() { scope.CallMethod("BeforeSave") } if !scope.HasError() { scope.CallMethod("BeforeCreate") } } // updateTimeStampForCreateCallback will set `CreatedAt`, `UpdatedAt` when creating func updateTimeStampForCreateCallback(scope *Scope) { if !scope.HasError() { now := NowFunc() scope.SetColumn("CreatedAt", now) scope.SetColumn("UpdatedAt", now) } } // createCallback the callback used to insert data into database func createCallback(scope *Scope) { if !scope.HasError() { defer scope.trace(NowFunc()) var ( columns, placeholders []string blankColumnsWithDefaultValue []string ) for _, field := range scope.Fields() { if scope.changeableField(field) { if field.IsNormal { if !field.IsPrimaryKey || !field.IsBlank { if field.IsBlank && field.HasDefaultValue { blankColumnsWithDefaultValue = append(blankColumnsWithDefaultValue, field.DBName) scope.InstanceSet("gorm:blank_columns_with_default_value", blankColumnsWithDefaultValue) } else { columns = append(columns, scope.Quote(field.DBName)) placeholders = append(placeholders, scope.AddToVars(field.Field.Interface())) } } } else if field.Relationship != nil && field.Relationship.Kind == "belongs_to" { for _, foreignKey := range field.Relationship.ForeignDBNames { if foreignField, ok := scope.FieldByName(foreignKey); ok && !scope.changeableField(foreignField) { columns = append(columns, scope.Quote(foreignField.DBName)) placeholders = append(placeholders, scope.AddToVars(foreignField.Field.Interface())) } } } } } var ( returningColumn = "*" quotedTableName = scope.QuotedTableName() primaryField = scope.PrimaryField() extraOption string ) if str, ok := scope.Get("gorm:insert_option"); ok { extraOption = fmt.Sprint(str) } if primaryField != nil { returningColumn = scope.Quote(primaryField.DBName) } lastInsertIDReturningSuffix := scope.Dialect().LastInsertIDReturningSuffix(quotedTableName, returningColumn) if len(columns) == 0 { scope.Raw(fmt.Sprintf( "INSERT INTO %v DEFAULT VALUES%v%v", quotedTableName, addExtraSpaceIfExist(extraOption), addExtraSpaceIfExist(lastInsertIDReturningSuffix), )) } else { scope.Raw(fmt.Sprintf( "INSERT INTO %v (%v) VALUES (%v)%v%v", scope.QuotedTableName(), strings.Join(columns, ","), strings.Join(placeholders, ","), addExtraSpaceIfExist(extraOption), addExtraSpaceIfExist(lastInsertIDReturningSuffix), )) } // execute create sql if lastInsertIDReturningSuffix == "" || primaryField == nil { if result, err := scope.SQLDB().Exec(scope.SQL, scope.SQLVars...); scope.Err(err) == nil { // set rows affected count scope.db.RowsAffected, _ = result.RowsAffected() // set primary value to primary field if primaryField != nil && primaryField.IsBlank { if primaryValue, err := result.LastInsertId(); scope.Err(err) == nil { scope.Err(primaryField.Set(primaryValue)) } } } } else { if err := scope.SQLDB().QueryRow(scope.SQL, scope.SQLVars...).Scan(primaryField.Field.Addr().Interface()); scope.Err(err) == nil { scope.db.RowsAffected = 1 } } } } // forceReloadAfterCreateCallback will reload columns that having default value, and set it back to current object func forceReloadAfterCreateCallback(scope *Scope) { if blankColumnsWithDefaultValue, ok := scope.InstanceGet("gorm:blank_columns_with_default_value"); ok { scope.DB().New().Select(blankColumnsWithDefaultValue.([]string)).First(scope.Value) } } // afterCreateCallback will invoke `AfterCreate`, `AfterSave` method after creating func afterCreateCallback(scope *Scope) { if !scope.HasError() { scope.CallMethod("AfterCreate") } if !scope.HasError() { scope.CallMethod("AfterSave") } }