This commit is contained in:
pmalmgren 2016-01-31 19:53:52 +00:00
commit 3e074fc7f9
2 changed files with 66 additions and 4 deletions

View File

@ -26,10 +26,13 @@ from rest_framework.compat import (
) )
from rest_framework.exceptions import ParseError from rest_framework.exceptions import ParseError
from rest_framework.request import is_form_media_type, override_method from rest_framework.request import is_form_media_type, override_method
from rest_framework.reverse import reverse
from rest_framework.settings import api_settings from rest_framework.settings import api_settings
from rest_framework.utils import encoders from rest_framework.utils import encoders, model_meta
from rest_framework.utils.breadcrumbs import get_breadcrumbs from rest_framework.utils.breadcrumbs import get_breadcrumbs
from rest_framework.utils.field_mapping import ClassLookupDict from rest_framework.utils.field_mapping import (
ClassLookupDict, get_detail_view_name
)
def zero_as_none(value): def zero_as_none(value):
@ -729,10 +732,13 @@ class AdminRenderer(BrowsableAPIRenderer):
return ret return ret
def get_context(self, data, accepted_media_type, renderer_context): def get_context(self, data, accepted_media_type, renderer_context, reverse_func=None):
""" """
Render the HTML for the browsable API representation. Render the HTML for the browsable API representation.
""" """
if not reverse_func:
reverse_func = reverse
context = super(AdminRenderer, self).get_context( context = super(AdminRenderer, self).get_context(
data, accepted_media_type, renderer_context data, accepted_media_type, renderer_context
) )
@ -756,6 +762,31 @@ class AdminRenderer(BrowsableAPIRenderer):
header = results header = results
style = 'detail' style = 'detail'
if style == 'list' and results:
serializer_class = getattr(context['view'], 'serializer_class', None)
fields = getattr(serializer_class.Meta, 'fields', None)
if issubclass(serializer_class, serializers.ModelSerializer) and fields \
and 'url' not in fields:
request = context['request']
def add_url(item):
info = model_meta.get_field_info(serializer_class.Meta.model)
pk = None
for field_name, field in info.fields_and_pk.items():
if field.primary_key and (field_name in item):
pk = item[field_name]
if pk:
view_name = get_detail_view_name(serializer_class.Meta.model)
url = reverse_func(view_name, kwargs={'pk': pk}, request=request)
item['url'] = url
return item
results = [add_url(result) for result in results]
columns = [key for key in header.keys() if key != 'url'] columns = [key for key in header.keys() if key != 'url']
details = [key for key in header.keys() if key != 'url'] details = [key for key in header.keys() if key != 'url']

View File

@ -14,12 +14,14 @@ from django.utils.translation import ugettext_lazy as _
from rest_framework import permissions, serializers, status from rest_framework import permissions, serializers, status
from rest_framework.renderers import ( from rest_framework.renderers import (
BaseRenderer, BrowsableAPIRenderer, HTMLFormRenderer, JSONRenderer AdminRenderer, BaseRenderer, BrowsableAPIRenderer, HTMLFormRenderer,
JSONRenderer
) )
from rest_framework.response import Response from rest_framework.response import Response
from rest_framework.settings import api_settings from rest_framework.settings import api_settings
from rest_framework.test import APIRequestFactory from rest_framework.test import APIRequestFactory
from rest_framework.views import APIView from rest_framework.views import APIView
from tests.utils import mock_reverse
DUMMYSTATUS = status.HTTP_200_OK DUMMYSTATUS = status.HTTP_200_OK
DUMMYCONTENT = 'dummycontent' DUMMYCONTENT = 'dummycontent'
@ -42,6 +44,12 @@ class DummyTestModel(models.Model):
name = models.CharField(max_length=42, default='') name = models.CharField(max_length=42, default='')
class DummySerializer(serializers.ModelSerializer):
class Meta:
model = DummyTestModel
fields = ('pk', 'name',)
class BasicRendererTests(TestCase): class BasicRendererTests(TestCase):
def test_expected_results(self): def test_expected_results(self):
for value, renderer_cls, expected in expected_results: for value, renderer_cls, expected in expected_results:
@ -103,6 +111,12 @@ class HTMLView1(APIView):
def get(self, request, **kwargs): def get(self, request, **kwargs):
return Response('text') return Response('text')
class AdminRendererView(APIView):
renderer_classes = (AdminRenderer, JSONRenderer)
serializer_class = DummySerializer
urlpatterns = [ urlpatterns = [
url(r'^.*\.(?P<format>.+)$', MockView.as_view(renderer_classes=[RendererA, RendererB])), url(r'^.*\.(?P<format>.+)$', MockView.as_view(renderer_classes=[RendererA, RendererB])),
url(r'^$', MockView.as_view(renderer_classes=[RendererA, RendererB])), url(r'^$', MockView.as_view(renderer_classes=[RendererA, RendererB])),
@ -111,6 +125,7 @@ urlpatterns = [
url(r'^html$', HTMLView.as_view()), url(r'^html$', HTMLView.as_view()),
url(r'^html1$', HTMLView1.as_view()), url(r'^html1$', HTMLView1.as_view()),
url(r'^empty$', EmptyGETView.as_view()), url(r'^empty$', EmptyGETView.as_view()),
url(r'^admin_renderer$', AdminRendererView.as_view()),
url(r'^api', include('rest_framework.urls', namespace='rest_framework')) url(r'^api', include('rest_framework.urls', namespace='rest_framework'))
] ]
@ -459,3 +474,19 @@ class TestHiddenFieldHTMLFormRenderer(TestCase):
field = serializer['published'] field = serializer['published']
rendered = renderer.render_field(field, {}) rendered = renderer.render_field(field, {})
assert rendered == '' assert rendered == ''
class TestAdminRenderer(TestCase):
def test_admin_renderer_adds_url(self):
"""AdminRenderer should add a URL to the results if they come from a ModelSerializer"""
renderer = AdminRenderer()
data = [OrderedDict([('pk', 1), ('name', 'dummyname')])]
view = AdminRendererView()
request = view.initialize_request(APIRequestFactory().request())
context = {'view': view, 'request': request,
'response': view.as_view()(request)}
rendered_context = renderer.get_context(data, 'text/html', context, reverse_func=mock_reverse)
results = rendered_context['results'][0]
assert results['url'] == 'http://example.org/dummytestmodel-detail/1/'