1- from typing import List , Tuple , Optional
1+ from typing import List , Tuple , Optional , Dict , Any
22from enum import Enum
33
44from django .db import models
55from django .db .models import sql
6-
76from django .db .models .constants import LOOKUP_SEP
7+ from django .core .exceptions import SuspiciousOperation
88
99from .fields import HStoreField
1010from .expressions import HStoreColumn
11+ from .datastructures import ConditionalJoin
1112
1213
1314class ConflictAction (Enum ):
@@ -18,6 +19,43 @@ class ConflictAction(Enum):
1819
1920
2021class PostgresQuery (sql .Query ):
22+ def add_join_conditions (self , conditions : Dict [str , Any ]) -> None :
23+ """Adds an extra condition to an existing JOIN.
24+
25+ This allows you to for example do:
26+
27+ INNER JOIN othertable ON (mytable.id = othertable.other_id AND [extra conditions])
28+
29+ This does not work if nothing else in your query doesn't already generate the
30+ initial join in the first place.
31+ """
32+
33+ alias = self .get_initial_alias ()
34+ opts = self .get_meta ()
35+
36+ for name , value in conditions .items ():
37+ parts = name .split (LOOKUP_SEP )
38+ _ , targets , _ , joins , path = self .setup_joins (parts , opts , alias , allow_many = True )
39+ self .trim_joins (targets , joins , path )
40+
41+ target_table = joins [- 1 ]
42+ field = targets [- 1 ]
43+ join = self .alias_map .get (target_table )
44+
45+ if not join :
46+ raise SuspiciousOperation ((
47+ 'Cannot add an extra join condition for "%s", there\' s no'
48+ 'existing join to add it to.'
49+ ) % target_table )
50+
51+ # convert the Join object into a ConditionalJoin object, which
52+ # allows us to add the extra condition
53+ if not isinstance (join , ConditionalJoin ):
54+ self .alias_map [target_table ] = ConditionalJoin .from_join (join )
55+ join = self .alias_map [target_table ]
56+
57+ join .add_condition (field , value )
58+
2159 def add_fields (self , field_names : List [str ], allow_m2m : bool = True ) -> bool :
2260 """
2361 Adds the given (model) fields to the select set. The field names are
0 commit comments