class QLeftOuterJoin(models.Q): """ Example: [alias, table to join, field, table, field] QLeftOuterJoin('mm_p', Post, 'pk', Category, 'last_post') class Thread(models.Model): category = models.ForeignKey(Category) subject = models.CharField(_('subject'), maxlength=255) last_post = models.ForeignKey("Post", editable=False, null=True, blank=True, default=None) class Post(models.Model): thread = models.ForeignKey(Thread, verbose_name=_('thread')) writer = models.ForeignKey(User, verbose_name=_('writer'), editable=False, null=True, blank=True) writer_verbose_name = models.CharField(_("writer's verbose name"), maxlength=255, editable=False) content = models.TextField(_('content')) created = models.DateTimeField(_('created'), editable=False) modified = models.DateTimeField(_('modified'), editable=False) threads = Thread.items.filter( Q(category=1) & QLeftOuterJoin('thread_last_post', Post, 'pk', Thread, 'last_post') ).select_related().extra(select={ 'last_post_modified': '`thread_last_post`.`modified`', }) """ def __init__(self, alias, table1, field1, table2, field2): if isinstance(table1, models.base.ModelBase): if field1 is None or field1 == 'pk': field1 = table1._meta.pk.name try: field1 = table1._meta.get_field(field1).column except AttributeError, e: raise AttributeError, "%s: %s" % (e, "table: %s, field: %s" % table1, field1) table1 = table1._meta.db_table if isinstance(table2, models.base.ModelBase): if field2 is None or field2 == 'pk': field2 = table2._meta.pk.name try: field2 = table2._meta.get_field(field2).column except AttributeError, e: raise AttributeError, "%s: %s" % (e, "table: %s, field: %s" % table1, field1) table2 = table2._meta.db_table self.where = "%s.%s = %s.%s" % (alias, field1, table2, field2) self.alias, self.table = alias, table1 def get_sql(self, opts): joins = SortedDict() joins[self.alias] = (self.table, 'LEFT OUTER JOIN', self.where) return (joins, [], [])