Call Query callback chain when preloading many2many
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
							
								
									3a9e91ab37
								
							
						
					
					
						commit
						c80b78b22d
					
				@ -15,6 +15,10 @@ func init() {
 | 
			
		||||
 | 
			
		||||
// queryCallback used to query data from database
 | 
			
		||||
func queryCallback(scope *Scope) {
 | 
			
		||||
	if _, skip := scope.Get("gorm:skip_query_callback"); skip {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	defer scope.trace(NowFunc())
 | 
			
		||||
 | 
			
		||||
	var (
 | 
			
		||||
 | 
			
		||||
@ -324,6 +324,10 @@ func (scope *Scope) handleManyToManyPreload(field *Field, conditions []interface
 | 
			
		||||
 | 
			
		||||
		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))
 | 
			
		||||
		// generate hashed forkey keys in join table
 | 
			
		||||
		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 {
 | 
			
		||||
	r, _ := json.MarshalIndent(v, "", "  ")
 | 
			
		||||
	return r
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user