我有一個類似的問題,寫了下面的效用函數,用於將左外連接在使用Django ORM的子查詢集上。
util是從給出的解決方案中派生出來的,該解決方案使用Django ORM將自定義左外部聯接添加到另一個表(而不是子查詢)。這裏是一個解決方案:https://stackoverflow.com/a/37688104/2367394
以下爲UTIL及所有相關代碼:
from django.db.models.fields.related import ForeignObject
from django.db.models.options import Options
from django.db.models.sql.where import ExtraWhere
from django.db.models.sql.datastructures import Join
class CustomJoin(Join):
def __init__(self, subquery, subquery_params, parent_alias, table_alias, join_type, join_field, nullable):
self.subquery_params = subquery_params
super(CustomJoin, self).__init__(subquery, parent_alias, table_alias, join_type, join_field, nullable)
def as_sql(self, compiler, connection):
"""
Generates the full
LEFT OUTER JOIN (somequery) alias ON alias.somecol = othertable.othercol, params
clause for this join.
"""
params = []
sql = []
alias_str = '' if self.table_alias == self.table_name else (' %s' % self.table_alias)
params.extend(self.subquery_params)
qn = compiler.quote_name_unless_alias
qn2 = connection.ops.quote_name
sql.append('%s (%s)%s ON (' % (self.join_type, self.table_name, alias_str))
for index, (lhs_col, rhs_col) in enumerate(self.join_cols):
if index != 0:
sql.append(' AND ')
sql.append('%s.%s = %s.%s' % (
qn(self.parent_alias),
qn2(lhs_col),
qn(self.table_alias),
qn2(rhs_col),
))
extra_cond = self.join_field.get_extra_restriction(
compiler.query.where_class, self.table_alias, self.parent_alias)
if extra_cond:
extra_sql, extra_params = compiler.compile(extra_cond)
extra_sql = 'AND (%s)' % extra_sql
params.extend(extra_params)
sql.append('%s' % extra_sql)
sql.append(')')
return ' '.join(sql), params
def join_to(table, subquery, table_field, subquery_field, queryset, alias):
"""
Add a join on `subquery` to `queryset` (having table `table`).
"""
# here you can set complex clause for join
def extra_join_cond(where_class, alias, related_alias):
if (alias, related_alias) == ('[sys].[columns]',
'[sys].[database_permissions]'):
where = '[sys].[columns].[column_id] = ' \
'[sys].[database_permissions].[minor_id]'
children = [ExtraWhere([where],())]
return where_class(children)
return None
foreign_object = ForeignObject(to=subquery, from_fields=[None], to_fields=[None], rel=None)
foreign_object.opts = Options(table._meta)
foreign_object.opts.model = table
foreign_object.get_joining_columns = lambda: ((table_field, subquery_field),)
foreign_object.get_extra_restriction = extra_join_cond
subquery_sql, subquery_params = subquery.query.sql_with_params()
join = CustomJoin(
subquery_sql, subquery_params, table._meta.db_table,
alias, "LEFT JOIN", foreign_object, True)
queryset.query.join(join)
# hook for set alias
join.table_alias = alias
queryset.query.external_aliases.add(alias)
return queryset
join_to
是您要使用的效用函數。
sq = Stats.objects.filter(date=my_date)
q = Site.objects.filter()
q = join_to(Site, sq, 'id', 'site_id', q, 'stats')
而下面的語句將打印類似於您的示例查詢(與子查詢)查詢:爲了您的查詢,如下所示,你可以使用它。
q.query.__str__()
謝謝,Suor。但在這種情況下,Django ORM使用'INNER JOIN'將統計信息連接到站點,然後應用按日期過濾。如果統計信息表不包含某個網站和日期的記錄,則該網站不會包含在結果中。我想,只有一種方法可以解決問題 - 加入過濾的統計表。 – psln
我不太瞭解你。如果統計表不包含記錄,那麼您沒有日期並且不應返回行。如果你的意思是這個統計信息沒有網站,那麼在加入時就會被淘汰,而不是它在Django中的工作方式。你在這種情況下設置site = models.ForeignKey('Site',null = True),Django爲你添加相同的代碼。 – Suor
網站可以在某個日期沒有統計記錄。我需要獲取所有網站和統計信息,這些信息存在於此日期。 F.E.如果'site'表包含記錄'site_1'和'site_2','stats'表只包含'stats_1(site = site_1,date = my_date)',我想獲得'[{site_1,stats_1},{site_2,NULL }]'。但是你的代碼只會返回'[{site_1,stats_1}]'。 – psln