from django.db.models.sql import query
from django.db.models.sql.where import WhereNode
class ExtraJoinsQuery(query.Query):
"""Supports explicit left outer joins
"""
def __init__(self, model, connection, where=WhereNode, joins = None):
super(ExtraJoinsQuery, self).__init__(model, connection, where)
self.extra_joins = joins or []
def clone(self, klass=None, **kwargs):
obj = super(ExtraJoinsQuery, self).clone(klass, **kwargs)
obj.extra_joins = self.extra_joins[:]
return obj
def get_from_clause(self):
"""
Returns a list of strings that are joined together to go after the
"FROM" part of the query, as well as a list any extra parameters
that
need to be included. Sub-classes, can override this to create a
from-clause via a "select", for example (e.g. CountQuery).
This should only be called after any SQL construction methods that
might change the tables we need. This means the select columns and
ordering must be done first.
"""
result = []
qn = self.quote_name_unless_alias
qn2 = self.connection.ops.quote_name
for join in self.extra_joins:
name, alias, join_type, lhs, lhs_col, col = join
alias_str = (alias != name and ' %s' % alias or '')
result.append('%s %s%s ON (%s.%s = %s.%s)'
% (join_type, qn(name), alias_str, qn(lhs),
qn2(lhs_col), qn(alias), qn2(col)))
super_from = super(ExtraJoinsQuery, self).get_from_clause()
head = [super_from[0][0]]#the main table
add = False #skip the first
for fc in super_from[0]:
if add:
if fc[0] == ",":
head.append(fc) #tables go first
else:
result.append(fc)#joins go after
else:
add = True
head.extend(result)
return head, super_from[1]
class ExtraManager(djangodb.Manager):
def get_query_set(self):
"""Returns a new QuerySet object. Subclasses can override this
method
to easily customize the behavior of the Manager.
"""
return djangodb.query.QuerySet(self.model,
dbutil.ExtraJoinsQuery(self.model, connection))
def extra(self, joins = None, *args, **kwargs):
qs = self.get_query_set().extra(*args, **kwargs)
if(joins):
qs.query.extra_joins.extend(joins)
return qs
Best,
Plamen Dragozov