﻿id	summary	reporter	owner	description	type	status	component	version	severity	resolution	keywords	cc	stage	has_patch	needs_docs	needs_tests	needs_better_patch	easy	ui_ux
7128	Infinite Recursion in new QuerySet Refactor when using a post_init signal	bo <bo.blanton@…>	Malcolm Tredinnick	"
The new Queryset refactor uses deepcopy for the where element of Query.clone()


{{{
in django.db.models.sql.Query.py line 169(ish)
        obj.where = deepcopy(self.where)
}}}

One gets an object that has a post_init signal (or post_save).  (best shown be example)

{{{
class MyObj(models.Model):
	cola = models.Char...
	colb = models.Char...
	..
	attr_a = None # none DB attribute
	attr_b = None # none DB attribute

class AnotherObj(models.Model):
	my_obj = models.ForeignKey(MyObj)
	cola = models.Char...
	colb = models.Char...

class YetAnotherObj(models.Model):
	my_obj = models.ForeignKey(MyObj)
	cola = models.Char...
	colb = models.Char...
	

def after_my_obj_init(sender, instance, signal, *args, **kwargs):
	# try to fill in the instances 'other attributes' with some preset
	# querySets for use as properties later
	
	#the deepcopy in the of the Querset clone
	# will cause an intinite HERE (clone on the order_by)
	instance.attr_a = AnotherObj.objects.filter(
		my_obj = instance, 
		cola__contains = ""Some Text""
	).order_by('cola')
	
	#No Infinite Recusrion as there is no Clone() called
	instance.attr_b = AnotherObj.objects.filter(
		my_obj = instance, 
		cola__contains = ""Some Other Text""
	)
	
dispatcher.connect(after_my_obj_init, signal=signals.post_init, sender=MyObj)

}}}

The above signal will now result in an infinite recursion .. as apparently it keeps trying to call the post_init function on 'instance' as it marches done the Where Tree.

There is a solution, but becuase it can also happen Outside the initsignal on other QuerySets, and seems rather sporatic sometimes for instance anytime clone is used it can crash.

{{{

an_obj = MyObj.objects.get(pk = 1)

# No infinite recursion
my_stuf = YetAnotherObj.objects.filter(cola = ""something"", my_obj = an_obj)
my_stuf = YetAnotherObj.objects.filter(cola = ""something"")

#BOOM (due to the clone())
my_stuf = my_stuf.filter(my_obj = an_obj)

}}}


The work around is to use the Primary Key

{{{

#in the signal
def after_my_obj_init(sender, instance, signal, *args, **kwargs):
	# try to fill in the instances 'other attributes' with some preset
	# querySets for use as properties later
	
	#the deepcopy in the of the Querset clone
	# will cause an intinite HERE (clone on the order_by)
	instance.attr_a = AnotherObj.objects.filter(
		my_obj__pk = instance.pk, 
		cola__contains = ""Some Text""
	).order_by('cola')
	
	#No Infinite Recusrion as there is no Clone() called
	instance.attr_b = AnotherObj.objects.filter(
		my_obj__pk = instance.pk, 
		cola__contains = ""Some Other Text""
	)

#in the other qset
my_stuf = my_stuf.filter(my_obj__pk = an_obj.pk)

}}}

As you can probably guess, this behavior is rather scary.  Changing the ""deepcopy"" to a normal ""copy"" did seem to help the issue, but i imagine that will break other tests.




"		closed	Core (Other)	dev		fixed	qsrf-cleanup QuerySet Refactor Infinite Recursion		Design decision needed	0	0	0	0	0	0
