mirror of https://github.com/go-gorm/gorm.git
Call Query callback chain when preloading many2many (#1622)
When using `Preload` on a `many2many` association, the `Query` callback chain was not being called. This made it difficult to write a plugin that could reliably get called regardless of how objects were being queried. Now `handleManyToManyPreload` will call the `Query` callback chain for each object that is retrieved by following the association. Since the data has already been read by the `handleManyToManyPreload` method, a new scope setting called `gorm:skip_queryCallback` is set to `true` before calling the callbacks. Callbacks can check for the presence of this setting if they should not be run; the default `queryCallback` is an example of this case. Fixes jinzhu/gorm#1621.
This commit is contained in:
parent
2bb1b7c83e
commit
ec72a4cb6b
|
@ -15,6 +15,10 @@ func init() {
|
||||||
|
|
||||||
// queryCallback used to query data from database
|
// queryCallback used to query data from database
|
||||||
func queryCallback(scope *Scope) {
|
func queryCallback(scope *Scope) {
|
||||||
|
if _, skip := scope.Get("gorm:skip_query_callback"); skip {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
defer scope.trace(NowFunc())
|
defer scope.trace(NowFunc())
|
||||||
|
|
||||||
var (
|
var (
|
||||||
|
|
|
@ -324,6 +324,10 @@ func (scope *Scope) handleManyToManyPreload(field *Field, conditions []interface
|
||||||
|
|
||||||
scope.scan(rows, columns, append(fields, joinTableFields...))
|
scope.scan(rows, columns, append(fields, joinTableFields...))
|
||||||
|
|
||||||
|
scope.New(elem.Addr().Interface()).
|
||||||
|
Set("gorm:skip_query_callback", true).
|
||||||
|
callCallbacks(scope.db.parent.callbacks.queries)
|
||||||
|
|
||||||
var foreignKeys = make([]interface{}, len(sourceKeys))
|
var foreignKeys = make([]interface{}, len(sourceKeys))
|
||||||
// generate hashed forkey keys in join table
|
// generate hashed forkey keys in join table
|
||||||
for idx, joinTableField := range joinTableFields {
|
for idx, joinTableField := range joinTableFields {
|
||||||
|
|
|
@ -1627,6 +1627,46 @@ func TestPrefixedPreloadDuplication(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestPreloadManyToManyCallbacks(t *testing.T) {
|
||||||
|
type (
|
||||||
|
Level2 struct {
|
||||||
|
ID uint
|
||||||
|
}
|
||||||
|
Level1 struct {
|
||||||
|
ID uint
|
||||||
|
Level2s []Level2 `gorm:"many2many:level1_level2s;AssociationForeignKey:ID;ForeignKey:ID"`
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
DB.DropTableIfExists("level1_level2s")
|
||||||
|
DB.DropTableIfExists(new(Level1))
|
||||||
|
DB.DropTableIfExists(new(Level2))
|
||||||
|
|
||||||
|
if err := DB.AutoMigrate(new(Level1), new(Level2)).Error; err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
lvl := Level1{
|
||||||
|
Level2s: []Level2{
|
||||||
|
Level2{},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
DB.Save(&lvl)
|
||||||
|
|
||||||
|
called := 0
|
||||||
|
|
||||||
|
DB.Callback().Query().After("gorm:query").Register("TestPreloadManyToManyCallbacks", func(scope *gorm.Scope) {
|
||||||
|
called = called + 1
|
||||||
|
})
|
||||||
|
|
||||||
|
found := Level1{ID: lvl.ID}
|
||||||
|
DB.Preload("Level2s").First(&found, &found)
|
||||||
|
|
||||||
|
if called != 2 {
|
||||||
|
t.Errorf("Wanted callback to be called 2 times but got %d", called)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func toJSONString(v interface{}) []byte {
|
func toJSONString(v interface{}) []byte {
|
||||||
r, _ := json.MarshalIndent(v, "", " ")
|
r, _ := json.MarshalIndent(v, "", " ")
|
||||||
return r
|
return r
|
||||||
|
|
Loading…
Reference in New Issue