18
18
from django .http .multipartparser import parse_header
19
19
from django .template import engines , loader
20
20
from django .test .client import encode_multipart
21
+ from django .urls import NoReverseMatch
21
22
from django .utils import six
22
23
from django .utils .html import mark_safe
23
24
@@ -808,6 +809,12 @@ def get_context(self, data, accepted_media_type, renderer_context):
808
809
columns = [key for key in header if key != 'url' ]
809
810
details = [key for key in header if key != 'url' ]
810
811
812
+ if isinstance (results , list ) and 'view' in renderer_context :
813
+ for result in results :
814
+ url = self .get_result_url (result , context ['view' ])
815
+ if url is not None :
816
+ result .setdefault ('url' , url )
817
+
811
818
context ['style' ] = style
812
819
context ['columns' ] = columns
813
820
context ['details' ] = details
@@ -816,6 +823,26 @@ def get_context(self, data, accepted_media_type, renderer_context):
816
823
context ['error_title' ] = getattr (self , 'error_title' , None )
817
824
return context
818
825
826
+ def get_result_url (self , result , view ):
827
+ """
828
+ Attempt to reverse the result's detail view URL.
829
+
830
+ This only works with views that are generic-like (has `.lookup_field`)
831
+ and viewset-like (has `.basename` / `.reverse_action()`).
832
+ """
833
+ if not hasattr (view , 'reverse_action' ) or \
834
+ not hasattr (view , 'lookup_field' ):
835
+ return
836
+
837
+ lookup_field = view .lookup_field
838
+ lookup_url_kwarg = getattr (view , 'lookup_url_kwarg' , None ) or lookup_field
839
+
840
+ try :
841
+ kwargs = {lookup_url_kwarg : result [lookup_field ]}
842
+ return view .reverse_action ('detail' , kwargs = kwargs )
843
+ except (KeyError , NoReverseMatch ):
844
+ return
845
+
819
846
820
847
class DocumentationRenderer (BaseRenderer ):
821
848
media_type = 'text/html'
0 commit comments