Skip to content

Commit e14b529

Browse files
committed
Add query string and fragment identifier capability to linkify.
1 parent 82411ec commit e14b529

File tree

1 file changed

+47
-16
lines changed

1 file changed

+47
-16
lines changed

django_tables2/columns/base.py

Lines changed: 47 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from collections import OrderedDict
22
from itertools import islice
3+
from urllib.parse import urlencode
34

45
from django.core.exceptions import ImproperlyConfigured
56
from 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
155182
class 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

Comments
 (0)