﻿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
7872	Some complex lookups with Q objects (or-style) don't return all matching model objects.	brooks.travis@…	Malcolm Tredinnick	"When using Q objects to make some complex queries, the resulting queryset does not contain all expected objects. Here is an example case:

Models:
{{{
class ExtendedUser(User):
	""""""
	This class inherits (MTI-style) from auth.models.User to add extra fields, and is the parent class 
	for the SupportStaff and Client classes to keep things DRY.
	""""""
	parent = models.OneToOneField(User, parent_link=True)
	middle_initial = models.CharField(max_length=10, blank=True)
	title = models.CharField(max_length=50, blank=True)
	office_building = models.ForeignKey(""Building"", null=True, blank=True)
	office_number = models.CharField(max_length=15, blank=True)
	phone = models.PhoneNumberField(blank=True)
	twitter = models.CharField(max_length=50, blank=True)
	pownce = models.CharField(max_length=50, blank=True)
	aim = models.CharField(max_length=50, blank=True)
	msn = models.CharField(max_length=50, blank=True)
	yahoo = models.CharField(max_length=50, blank=True)
	
	def get_full_name(self):
		if self.middle_initial:
			return u""%s %s %s"" % (self.first_name, self.middle_initial, self.last_name)
		else:
			return super(ExtendedUser, self).get_full_name()
			
	def __unicode__(self):
		return u""%s"" % self.get_full_name()
	
	class Meta:
		ordering = ('last_name', 'first_name')
		verbose_name = u'Extended User'
		verbose_name_plural = u'Extended Users'

class SupportStaff(ExtendedUser):
	""""""
	This class inherits from the model class ExtendedUser (itself a subclass of auth User model). 
	This should only be used for people who are going to provide end-user support services.
	""""""
	parent = models.OneToOneField(ExtendedUser, parent_link=True)
	coverage = models.ManyToManyField(""OrgUnit"", related_name=""support_staff_coverage"", null=True, blank=True)
	expertise = models.ManyToManyField(""SpecialSkill"", related_name=""support_staff_expertise"", null=True, blank=True)
	
	def __unicode__(self):
		return u""%s"" % self.get_full_name()

class SupportTicket(models.Model):
	call_source = models.ForeignKey(""CallSource"", null=True, blank=True)
	client = models.ManyToManyField(Client)
	call_description = models.TextField(""Call Description"", max_length=500)
	call_type = models.ManyToManyField(""CallType"")
	equipment_tag = models.CharField(max_length=50, null=True, blank=True)
	priority = models.CharField(choices=PRIORITY_LEVELS, default=3, max_length=2)
	target_date = models.DateTimeField(""Target Date and Time"", null=True, blank=True)
	call_status = models.ForeignKey(""CallStatus"")
	cause = models.ManyToManyField(""Cause"", null=True, blank=True)
	solution = models.TextField(max_length=500, blank=True)
	date_created = models.DateTimeField(default=datetime.datetime.now, editable=False)
	created_by = models.ForeignKey(SupportStaff, related_name='ticket_created')
	date_modified = models.DateTimeField(null=True, blank=True, editable=False)
	last_modified_by = models.ForeignKey(SupportStaff, related_name='ticket_modified_by', null=True, blank=True)
	date_closed = models.DateTimeField(null=True, blank=True)
	closed_by = models.ForeignKey(User, related_name='ticket_closed_by', null=True, blank=True)

class Assignment(models.Model):
	support_ticket = models.ForeignKey(SupportTicket, edit_inline=models.STACKED, num_in_admin=1, related_name='assignment_made')
	group_assigned = models.ForeignKey(Group, core=True)
	group_notified = models.NullBooleanField(editable=False)
	support_staff_assigned = models.ForeignKey(SupportStaff, related_name='staff_assignment', null=True, blank=True)
	support_staff_notified = models.NullBooleanField(editable=False)
	notes = models.TextField(max_length=500, blank=True)
	acknowledge = models.BooleanField(default=False)
	date_created = models.DateTimeField(default=datetime.datetime.now, editable=False)
	created_by = models.ForeignKey(SupportStaff, related_name='assigned_by')
	date_modified = models.DateTimeField(default=datetime.datetime.now, editable=False)
	last_modified_by = models.ForeignKey(SupportStaff, related_name='assignment_modified_by', null=True, blank=True)
	date_acknowledged = models.DateTimeField(null=True, blank=True, editable=False)
}}}

Queries:

{{{

>>> t4 = SupportTicket.objects.filter(assignment_made__group_assigned__user=u)
>>> t5 = SupportTicket.objects.filter(created_by__parent__parent=u)
>>> t6 = SupportTicket.objects.filter(Q(assignment_made__group_assigned__user=u)|Q(created_by__parent__parent=u)).distinct()
>>> t4
[<SupportTicket: 3>, <SupportTicket: 2>, <SupportTicket: 1>]
>>> t5
[<SupportTicket: 5>, <SupportTicket: 3>, <SupportTicket: 2>, <SupportTicket: 1>]
>>> t6
[<SupportTicket: 3>, <SupportTicket: 2>, <SupportTicket: 1>]
>>> str(SupportTicket.objects.filter(Q(assignment_made__group_assigned__user=u)|Q(created_by__parent__parent=u)).distinct().query)
'SELECT DISTINCT ""tickets_supportticket"".""id"", ""tickets_supportticket"".""call_source_id"", ""tickets_supportticket"".""call_description"", ""tickets_supportticket"".""equipment_tag"", ""tickets_supportticket"".""priority"", ""tickets_supportticket"".""target_date"", ""tickets_supportticket"".""call_status_id"", ""tickets_supportticket"".""solution"", ""tickets_supportticket"".""date_created"", ""tickets_supportticket"".""created_by_id"", ""tickets_supportticket"".""date_modified"", ""tickets_supportticket"".""last_modified_by_id"", ""tickets_supportticket"".""date_closed"", ""tickets_supportticket"".""closed_by_id"" FROM ""tickets_supportticket"" LEFT OUTER JOIN ""tickets_assignment"" ON (""tickets_supportticket"".""id"" = ""tickets_assignment"".""support_ticket_id"") INNER JOIN ""auth_group"" ON (""tickets_assignment"".""group_assigned_id"" = ""auth_group"".""id"") LEFT OUTER JOIN ""auth_user_groups"" ON (""auth_group"".""id"" = ""auth_user_groups"".""group_id"") WHERE (""auth_user_groups"".""user_id"" = 6  OR ""tickets_supportticket"".""created_by_id"" = 6 ) ORDER BY ""tickets_supportticket"".""date_created"" DESC'

}}}"		closed	Database layer (models, ORM)	dev		fixed		brooks.travis@… mir@…	Accepted	0	0	0	0	0	0
