【发布时间】:2020-06-19 03:06:13
【问题描述】:
我正在使用 Django Rest Framework 3.11.0,我想使用带有自定义模板的 BrowsableAPIRenderer 来呈现实例的详细信息。我只想覆盖 dict/json 的渲染,下图中标记为红色,其余的我想保留。
通过覆盖restframework/api.html,我只设法更改了标题、标题和一些字段,但我没有找到一种方法来呈现实例的详细信息,例如在一张桌子上。有没有办法做到这一点?
澄清:我的模型包含大型字典,我希望它们显示得比内联字符串更漂亮。我认为当我发现如何自定义(已经很漂亮的)Django RestFramework BrowsableAPI 时,我也将能够解决我的问题。
(如果您想解决类似问题,请查看我的更新 2。)
更新 1
这是我使用Bedilbeks answer 的地方(直到第一次更新)。
我不想更改所有视图,所以我没有全局注册渲染器。
REST_FRAMEWORK = {
'DEFAULT_RENDERER_CLASSES': [
'rest_framework.renderers.JSONRenderer',
'rest_framework.renderers.BrowsableAPIRenderer',
# 'users.renderers.CustomBrowsableAPIRenderer',
]
}
相反,我将renderer_classes 设置为我的UserViewSet 并在此处使用我的CustomBrowsableAPIRenderer。
class UserViewSet(GenericViewSet, ListModelMixin, RetrieveModelMixin):
queryset = UserModel.objects.all()
serializer_class = UserSerializer
renderer_classes = [renderers.JSONRenderer, CustomBrowsableAPIRenderer]
我需要覆盖api.html 模板,但我不希望此更改适用于任何地方,因此我在渲染器中动态选择模板。默认情况下,BrowsableAPIRenderer 有一个template = "rest_framework/api.html" 属性,但我需要逻辑,所以我使用@property 装饰器来执行以下操作:
- 检查我们是否在
detail视图中 - 检查 GET 参数
如果我们在详细视图中并且存在"table" 参数,则返回我的模板,否则返回默认值。
class CustomBrowsableAPIRenderer(BrowsableAPIRenderer):
@property
def template(self):
view = self.renderer_context.get("view", {})
table = "table" in view.request.query_params
if view and hasattr(view, "detail") and view.detail and table:
return "users/api.html" # custom template
else:
return "rest_framework/api.html" # default
def get_default_renderer(self, view):
table = "table" in view.request.query_params
if hasattr(view, "detail") and view.detail and table:
return TableHtmlRenderer()
return super().get_default_renderer(view)
api.html 的关键部分如下所示(第 123 行附近)。
...
{% block style %}
{{ block.super }}
<link rel="stylesheet" type="text/css" href="{% static "css/api.css" %}"/>
{% endblock %}
<!-- HERE IS THE ACTUAL CONTENT -->
</span></pre><div class="prettyprint" style="overflow: auto;">{{ content|urlize_quoted_links }}</div>
</div>
...
我实际上并没有为User 模型和ViewSet 这样做,但为了示例,我坚持使用它。在我的模型中,我想要渲染更大的 JSON 元素,因此我在 TableHTMLRenderer 中进行了一些预处理以返回缩进形式的 JSON。
class TableHtmlRenderer(TemplateHTMLRenderer):
media_type = "text/html"
format = "api"
template_name = "table_template.html"
def get_template_context(self, data, renderer_context):
for key in data.keys():
try:
data[key] = json.dumps(json.loads(data[key]), indent=4)
except (JSONDecodeError, TypeError):
pass
context = {
"data": data
}
response = renderer_context["response"]
if response.exception:
context["status_code"] = response.status_code
return context
所以由URL控制,我可以在默认渲染器和自定义/表格渲染器之间切换。
- localhost.me:8000/api/users/1/?table
- localhost.me:8000/api/users/1/
到目前为止一切顺利,我现在有了自己的渲染器类,我可以修改我的用户实例的 API 视图的外观。我还在为这张桌子苦苦挣扎,因为长行上的换行符不起作用,而且它不会留在 div 的边界内。
这是app.css,它被加载到api.html 模板中。
pre.inline {
padding: 0;
border: none;
word-break: break-all;
word-wrap: break-word;
display: contents;
}
table, th, td {
vertical-align: top;
padding: 2px;
text-align: left;}
table {
//table-layout: fixed;
width: 100% !important;
word-wrap:break-word;
}
th, td {
border-bottom: 1px solid #ddd;
overflow: auto;
width: 100%;
}
tr:hover {
background-color: #f2f2f2;
}
tr:nth-child(even) {
background-color: #f5f5f5;
}
更新 2
由于我现在可以使用自定义的 BrowsableAPIRenderer 和带有很多技巧的模板显示一些视图,所以我回到了导致我提出这个问题的问题。我想了解 DRF 如何渲染我的模型,以进行更改以显示大型嵌套字典。
我发现BrowsableAPIRenderer 将模型内容作为单个字符串插入到api.html 模板中,例如{{ content|urlize_quoted_links }} 在<pre> 标记内。
插入发生在BrowsableAPIRenderer.get_content 方法中。
# original code
renderer_context['indent'] = 4
content = renderer.render(data, accepted_media_type, renderer_context)
我现在看到我所要做的就是继承 BrowsableAPIRenderer 并覆盖 get_content 方法。我就是这样做的。
class LogBrowsableAPIRenderer(BrowsableAPIRenderer):
def get_content(self, renderer, data, accepted_media_type, renderer_context):
"""
Extends BrowsableAPIRenderer.get_content.
"""
if not renderer:
return '[No renderers were found]'
renderer_context['indent'] = 4
# content = renderer.render(data, accepted_media_type, renderer_context)
# try to convert all string-values into dictionaries
data_dict = dict(data.items())
for k in data_dict.keys():
try:
data_dict[k] = json.loads(data_dict[k], strict=False)
except JSONDecodeError:
# ignore errors and move on for now
pass
# dump into indented string again
content = json.dumps(data_dict, indent=4, sort_keys=True).encode(encoding="utf-8")
render_style = getattr(renderer, 'render_style', 'text')
assert render_style in ['text', 'binary'], 'Expected .render_style "text" or "binary", but got "%s"' % render_style
if render_style == 'binary':
return '[%d bytes of binary content]' % len(content)
return content
我也意识到我可以用不同的措辞来表达我的问题,以便更快地结束。
【问题讨论】:
-
我没有得到你真正想要的东西?拜托,你能为你的场景提供一些例子吗?你想要什么样的桌子?
-
因此,django restframework 的可浏览 API 功能使显示我的 Django REST 端点变得非常容易,并且还可以呈现截图中所示的数据库模型。您可以自定义 DRF 中的大多数内容,例如更改引导主题或覆盖用于构建页面的模板。你可以例如覆盖
api.html以自定义我截屏页面的外观。我没有找到一种方法来自定义模型的表示和渲染方式。表格的类型并不重要,如果我可以在表格中显示模型(例如键值),我可以以任何方式显示它。 -
其实我知道重写模板的目的和 BrowsableAPI 的使用,我只是想知道你的场景,给出一个更好理解的答案。所以,无论如何我现在会尝试给出答案
标签: django django-rest-framework django-templates