11from collections import OrderedDict
22from itertools import islice
3+ from urllib .parse import urlencode
34
45from django .core .exceptions import ImproperlyConfigured
56from django .urls import reverse
@@ -65,7 +66,7 @@ class LinkTransform:
6566 accessor = None
6667 attrs = None
6768
68- def __init__ (self , url = None , accessor = None , attrs = None , reverse_args = None ):
69+ def __init__ (self , url = None , accessor = None , attrs = None , reverse_args = None , query = None , fragment = None ):
6970 """
7071 arguments:
7172 url (callable): If supplied, the result of this callable will be used as ``href`` attribute.
@@ -79,6 +80,8 @@ def __init__(self, url=None, accessor=None, attrs=None, reverse_args=None):
7980 self .url = url
8081 self .attrs = attrs
8182 self .accessor = accessor
83+ self .query = query
84+ self .fragment = fragment
8285
8386 if isinstance (reverse_args , (list , tuple )):
8487 viewname , args = reverse_args
@@ -95,23 +98,33 @@ def compose_url(self, **kwargs):
9598 record = kwargs ["record" ]
9699
97100 if self .reverse_args .get ("viewname" , None ) is not None :
98- return self .call_reverse (record = record )
99-
100- if bound_column is None and self .accessor is None :
101- accessor = Accessor ("" )
101+ url = self .call_reverse (record = record )
102102 else :
103- accessor = Accessor (self .accessor if self .accessor is not None else bound_column .name )
104- context = accessor .resolve (record )
105- if not hasattr (context , "get_absolute_url" ):
106- if hasattr (record , "get_absolute_url" ):
107- context = record
103+ if bound_column is None and self .accessor is None :
104+ accessor = Accessor ("" )
108105 else :
109- raise TypeError (
110- "for linkify=True, '{}' must have a method get_absolute_url" .format (
111- str (context )
106+ accessor = Accessor (self .accessor if self .accessor is not None else bound_column .name )
107+ context = accessor .resolve (record )
108+ if not hasattr (context , "get_absolute_url" ):
109+ if hasattr (record , "get_absolute_url" ):
110+ context = record
111+ else :
112+ raise TypeError (
113+ "for linkify=True, '{}' must have a method get_absolute_url" .format (
114+ str (context )
115+ )
112116 )
113- )
114- return context .get_absolute_url ()
117+ url = context .get_absolute_url ()
118+
119+ if self .query :
120+ url += '?' + urlencode ({
121+ a : v .resolve (record ) if isinstance (v , Accessor ) else v
122+ for a , v in self .query .items ()
123+ })
124+ if self .fragment :
125+ url += '#' + self .fragment
126+
127+ return url
115128
116129 def call_reverse (self , record ):
117130 """
@@ -150,6 +163,20 @@ def __call__(self, content, **kwargs):
150163
151164 return format_html ("<a {}>{}</a>" , attrs .as_html (), content )
152165
166+ @classmethod
167+ def get_callback (cls , * args , ** kwargs ):
168+ """
169+ This method constructs a LinkTransform and returns a callback function suitable as the linkify
170+ parameter of Column.__init__() This may be used to access features of LinkTransform which aren't
171+ exposed via linkify dict or tuple linkify values, for example, constructing a url which has both
172+ positional args AND a query string.
173+ """
174+ link_transform = cls (* args , ** kwargs )
175+
176+ def callback (record , value , ** kwargs ):
177+ return link_transform .compose_url (record = record , value = value , ** kwargs )
178+
179+ return callback
153180
154181@library .register
155182class Column :
@@ -295,8 +322,12 @@ def __init__(
295322 link_kwargs = None
296323 if callable (linkify ) or hasattr (self , "get_url" ):
297324 link_kwargs = dict (url = linkify if callable (linkify ) else self .get_url )
298- elif isinstance (linkify , (dict , tuple )):
325+ elif isinstance (linkify , (list , tuple )):
299326 link_kwargs = dict (reverse_args = linkify )
327+ elif isinstance (linkify , dict ):
328+ # specific uppercase keys in linkify are understood to be link_kwargs, and the rest must be reverse_args
329+ link_kwargs = { name .lower (): linkify .pop (name ) for name in ('QUERY' , 'FRAGMENT' ) if name in linkify }
330+ link_kwargs ['reverse_args' ] = linkify
300331 elif linkify is True :
301332 link_kwargs = dict (accessor = self .accessor )
302333
0 commit comments