Gin Practice serial 10 Custom GORM callbacks

Source: Internet
Author: User
Tags sprintf
This is a creation in Article, where the information may have evolved or changed.

Custom GORM Callbacks

GORM itself is powered by callbacks, so we could fully
customize GORM as you want

Project Address: Https://github.com/EDDYCJY/go ...

The GORM itself is driven by callbacks, so we can fully customize the GORM as needed to achieve our goal

    • Sign up for a new callback
    • To delete an existing callback
    • Replace the existing callback
    • Order of registration Callbacks

Including the above four categories of callbacks in GORM, we use the project "Replace existing callbacks" to solve a small pain point

Problem

In the models directory, we contain tag.go and article.go two files, they have a problem, that is, beforecreate, BeforeUpdate repeated appear, that is 100 files, will write 100 times?

1, Tag.go

2, Article.go

Obviously this is not possible, if you have been aware of this problem before, it is OK, but there is no, now it is necessary to change

Solve

Here we use callbacks to implement the function, do not need a file to write

Implement callbacks

Open the Models.go file in the models directory to implement the following two methods:

1, Updatetimestampforcreatecallback

// updateTimeStampForCreateCallback will set `CreatedOn`, `ModifiedOn` when creatingfunc updateTimeStampForCreateCallback(scope *gorm.Scope) {    if !scope.HasError() {        nowTime := time.Now().Unix()        if createTimeField, ok := scope.FieldByName("CreatedOn"); ok {            if createTimeField.IsBlank {                createTimeField.Set(nowTime)            }        }        if modifyTimeField, ok := scope.FieldByName("ModifiedOn"); ok {            if modifyTimeField.IsBlank {                modifyTimeField.Set(nowTime)            }        }    }}

In this method, the following functions are completed

    • Check if there is an error (db. Error)
    • scope.FieldByNamescope.Fields()Determine whether the field you want is currently included by getting all fields
for _, field := range scope.Fields() {    if field.Name == name || field.DBName == name {        return field, true    }    if field.DBName == dbName {        mostMatchedField = field    }}
    • field.IsBlankTo determine whether the value of the field is empty
func isBlank(value reflect.Value) bool {    switch value.Kind() {    case reflect.String:        return value.Len() == 0    case reflect.Bool:        return !value.Bool()    case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:        return value.Int() == 0    case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:        return value.Uint() == 0    case reflect.Float32, reflect.Float64:        return value.Float() == 0    case reflect.Interface, reflect.Ptr:        return value.IsNil()    }    return reflect.DeepEqual(value.Interface(), reflect.Zero(value.Type()).Interface())}
    • Null to field.Set set the value for the field, the parameter isinterface{}

2, Updatetimestampforupdatecallback

// updateTimeStampForUpdateCallback will set `ModifyTime` when updatingfunc updateTimeStampForUpdateCallback(scope *gorm.Scope) {    if _, ok := scope.Get("gorm:update_column"); !ok {        scope.SetColumn("ModifiedOn", time.Now().Unix())    }}
    • scope.Get(...)Gets the parameter that sets the literal value based on the entry parameter, for example, in this article gorm:update_column , it looks for field properties with this literal value
    • scope.SetColumn(...)Assuming there are no update_column fields specified, we default in updating the ModifiedOn value of the callback setting

Register callbacks

In the above section I have written the callback method, then I need to register it in the GORM hook, but it comes with the Create and Update callback itself, so the call substitution can be

In the init function of Models.go, add the following statement

db.Callback().Create().Replace("gorm:update_time_stamp", updateTimeStampForCreateCallback)db.Callback().Update().Replace("gorm:update_time_stamp", updateTimeStampForUpdateCallback)

Verify

Access Addtag interface, check database after success, discoverable created_on and modified_on field are current execution time

Access the Edittag interface to discover the modified_on last time the update was performed

Expand

We think that the hard deletion in the actual project is less exist, then whether it can be done through the callbacks to complete this function?

The answer is yes, we added the Model struct DeletedOn variable earlier

type Model struct {    ID int `gorm:"primary_key" json:"id"`    CreatedOn int `json:"created_on"`    ModifiedOn int `json:"modified_on"`    DeletedOn int `json:"deleted_on"`}

Implement callbacks

Open the Models.go file in the models directory to implement the following methods:

Func deletecallback (Scope *gorm. Scope) {if!scope. Haserror () {var extraoption string if str, OK: = scope. Get ("Gorm:delete_option"); OK {extraoption = fmt. Sprint (str)} Deletedonfield, Hasdeletedonfield: = scope. Fieldbyname ("Deletedon") if!scope. search.unscoped && Hasdeletedonfield {scope. Raw (FMT. Sprintf ("UPDATE%v SET%v=%v%v%v", scope. Quotedtablename (), scope. Quote (deletedonfield.dbname), scope. Addtovars (time. Now (). Unix ()), addextraspaceifexist (scope. Combinedconditionsql ()), Addextraspaceifexist (Extraoption),). Exec ()} else {scope. Raw (FMT. Sprintf ("DELETE from%v%v%v", scope. Quotedtablename (), addextraspaceifexist (scope. Combinedconditionsql ()), Addextraspaceifexist (Extraoption),). Exec ()}}}func AddextraspaceifExist (str string) string {if str! = "" {return "" + str} return "} 
    • scope.Get("gorm:delete_option")Check whether the delete_option is manually specified
    • scope.FieldByName("DeletedOn")Get our agreed delete field, soft delete if present, UPDATE DELETE hard Delete if not present
    • scope.QuotedTableName()Returns the name of the referenced table, which GORM the table name according to its own logic
    • scope.CombinedConditionSql()Return to the combination of good conditions SQL, look at the method prototype is very clear
func (scope *Scope) CombinedConditionSql() string {    joinSQL := scope.joinsSQL()    whereSQL := scope.whereSQL()    if scope.Search.raw {        whereSQL = strings.TrimSuffix(strings.TrimPrefix(whereSQL, "WHERE ("), ")")    }    return joinSQL + whereSQL + scope.groupSQL() +        scope.havingSQL() + scope.orderSQL() + scope.limitAndOffsetSQL()}
    • scope.AddToVarsThe method can add values as arguments to SQL, and can be used to prevent SQL injection
func (scope *Scope) AddToVars(value interface{}) string {    _, skipBindVar := scope.InstanceGet("skip_bindvar")    if expr, ok := value.(*expr); ok {        exp := expr.expr        for _, arg := range expr.args {            if skipBindVar {                scope.AddToVars(arg)            } else {                exp = strings.Replace(exp, "?", scope.AddToVars(arg), 1)            }        }        return exp    }    scope.SQLVars = append(scope.SQLVars, value)    if skipBindVar {        return "?"    }    return scope.Dialect().BindVar(len(scope.SQLVars))}

Register callbacks

In the init function of Models.go, add the following deleted callback

db.Callback().Delete().Replace("gorm:delete", deleteCallback)

Verify

Restart the service, access the Deletetag interface, you can find the Deleted_on field has value after success

Summary

In this section, we have completed new, updated, and queried callbacks in conjunction with GORM, which is often used in real-world projects.

After all, a hook thing, there is no need to write too many unnecessary code

(Note that adding a soft delete after the previous code requires increased deleted_on judgment)

Reference

Sample code for this series

    • Go-gin-example

Catalog of this series

    • Gin Practice Serial One Golang introduction and environment installation
    • Gin Practice Two Build blog API ' s (i)
    • Gin Practice three Build blog API ' s (ii)
    • Gin Practice Four Build blog API ' s (iii)
    • Gin Practice Five Build blog API ' s (iv)
    • Gin Practice Six Build blog API ' s (v)
    • Gin Practice serial Seven Golang graceful restart HTTP service
    • Gin practice serial Eight for it plus swagger
    • Gin practice nine deploying Golang applications to Docker
    • Gin Practice serial 10 Custom GORM callbacks
    • Gin Practice Golang Cross-compiling

Document

    • Gorm
Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.