mirror of
https://github.com/encode/django-rest-framework.git
synced 2024-11-22 01:26:53 +03:00
Admin renderer urls (#5988)
* Make admin detail link have small width * Disable admin detail link when no URL * Add 'AdminRenderer.get_result_url' Attempts to reverse the result's detail view URL.
This commit is contained in:
parent
3578bd6883
commit
f89cc066bc
|
@ -18,6 +18,7 @@ from django.core.paginator import Page
|
|||
from django.http.multipartparser import parse_header
|
||||
from django.template import engines, loader
|
||||
from django.test.client import encode_multipart
|
||||
from django.urls import NoReverseMatch
|
||||
from django.utils import six
|
||||
from django.utils.html import mark_safe
|
||||
|
||||
|
@ -815,6 +816,12 @@ class AdminRenderer(BrowsableAPIRenderer):
|
|||
columns = [key for key in header if key != 'url']
|
||||
details = [key for key in header if key != 'url']
|
||||
|
||||
if isinstance(results, list) and 'view' in renderer_context:
|
||||
for result in results:
|
||||
url = self.get_result_url(result, context['view'])
|
||||
if url is not None:
|
||||
result.setdefault('url', url)
|
||||
|
||||
context['style'] = style
|
||||
context['columns'] = columns
|
||||
context['details'] = details
|
||||
|
@ -823,6 +830,26 @@ class AdminRenderer(BrowsableAPIRenderer):
|
|||
context['error_title'] = getattr(self, 'error_title', None)
|
||||
return context
|
||||
|
||||
def get_result_url(self, result, view):
|
||||
"""
|
||||
Attempt to reverse the result's detail view URL.
|
||||
|
||||
This only works with views that are generic-like (has `.lookup_field`)
|
||||
and viewset-like (has `.basename` / `.reverse_action()`).
|
||||
"""
|
||||
if not hasattr(view, 'reverse_action') or \
|
||||
not hasattr(view, 'lookup_field'):
|
||||
return
|
||||
|
||||
lookup_field = view.lookup_field
|
||||
lookup_url_kwarg = getattr(view, 'lookup_url_kwarg', None) or lookup_field
|
||||
|
||||
try:
|
||||
kwargs = {lookup_url_kwarg: result[lookup_field]}
|
||||
return view.reverse_action('detail', kwargs=kwargs)
|
||||
except (KeyError, NoReverseMatch):
|
||||
return
|
||||
|
||||
|
||||
class DocumentationRenderer(BaseRenderer):
|
||||
media_type = 'text/html'
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{% load rest_framework %}
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>{% for column in columns%}<th>{{ column|capfirst }}</th>{% endfor %}<th></th></tr>
|
||||
<tr>{% for column in columns%}<th>{{ column|capfirst }}</th>{% endfor %}<th class="col-xs-1"></th></tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for row in results %}
|
||||
|
@ -14,7 +14,11 @@
|
|||
{% endif %}
|
||||
{% endfor %}
|
||||
<td>
|
||||
{% if row.url %}
|
||||
<a href="{{ row.url }}"><span class="glyphicon glyphicon-chevron-right" aria-hidden="true"></span></a>
|
||||
{% else %}
|
||||
<span class="glyphicon glyphicon-chevron-right text-muted" aria-hidden="true"></span>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
|
|
|
@ -728,6 +728,75 @@ class AdminRendererTests(TestCase):
|
|||
response.render()
|
||||
self.assertContains(response, '<tr><th>Iteritems</th><td>a string</td></tr>', html=True)
|
||||
|
||||
def test_get_result_url(self):
|
||||
factory = APIRequestFactory()
|
||||
|
||||
class DummyGenericViewsetLike(APIView):
|
||||
lookup_field = 'test'
|
||||
|
||||
def reverse_action(view, *args, **kwargs):
|
||||
self.assertEqual(kwargs['kwargs']['test'], 1)
|
||||
return '/example/'
|
||||
|
||||
# get the view instance instead of the view function
|
||||
view = DummyGenericViewsetLike.as_view()
|
||||
request = factory.get('/')
|
||||
response = view(request)
|
||||
view = response.renderer_context['view']
|
||||
|
||||
self.assertEqual(self.renderer.get_result_url({'test': 1}, view), '/example/')
|
||||
self.assertIsNone(self.renderer.get_result_url({}, view))
|
||||
|
||||
def test_get_result_url_no_result(self):
|
||||
factory = APIRequestFactory()
|
||||
|
||||
class DummyView(APIView):
|
||||
lookup_field = 'test'
|
||||
|
||||
# get the view instance instead of the view function
|
||||
view = DummyView.as_view()
|
||||
request = factory.get('/')
|
||||
response = view(request)
|
||||
view = response.renderer_context['view']
|
||||
|
||||
self.assertIsNone(self.renderer.get_result_url({'test': 1}, view))
|
||||
self.assertIsNone(self.renderer.get_result_url({}, view))
|
||||
|
||||
def test_get_context_result_urls(self):
|
||||
factory = APIRequestFactory()
|
||||
|
||||
class DummyView(APIView):
|
||||
lookup_field = 'test'
|
||||
|
||||
def reverse_action(view, url_name, args=None, kwargs=None):
|
||||
return '/%s/%d' % (url_name, kwargs['test'])
|
||||
|
||||
# get the view instance instead of the view function
|
||||
view = DummyView.as_view()
|
||||
request = factory.get('/')
|
||||
response = view(request)
|
||||
|
||||
data = [
|
||||
{'test': 1},
|
||||
{'url': '/example', 'test': 2},
|
||||
{'url': None, 'test': 3},
|
||||
{},
|
||||
]
|
||||
context = {
|
||||
'view': DummyView(),
|
||||
'request': Request(request),
|
||||
'response': response
|
||||
}
|
||||
|
||||
context = self.renderer.get_context(data, None, context)
|
||||
results = context['results']
|
||||
|
||||
self.assertEqual(len(results), 4)
|
||||
self.assertEqual(results[0]['url'], '/detail/1')
|
||||
self.assertEqual(results[1]['url'], '/example')
|
||||
self.assertEqual(results[2]['url'], None)
|
||||
self.assertNotIn('url', results[3])
|
||||
|
||||
|
||||
@pytest.mark.skipif(not coreapi, reason='coreapi is not installed')
|
||||
class TestDocumentationRenderer(TestCase):
|
||||
|
|
Loading…
Reference in New Issue
Block a user