Django REST framework API 指南(8):渲染

wcode發表於2018-03-07

Django REST framework API 指南(1):請求
Django REST framework API 指南(2):響應
Django REST framework API 指南(3):檢視
Django REST framework API 指南(4):通用檢視
Django REST framework API 指南(5):檢視集
Django REST framework API 指南(6):路由
Django REST framework API 指南(7):解析
Django REST framework API 指南(8):渲染

官方原文連結

渲染

REST framework 包含許多內建的渲染器類,允許您使用各種 media type 返回響應。同時也支援自定義渲染器。

如何確定使用哪個渲染器

檢視的渲染器集合始終被定義為類列表。當呼叫檢視時,REST framework 將對請求內容進行分析,並確定最合適的渲染器以滿足請求。內容分析的基本過程包括檢查請求的 Accept header,以確定它在響應中期望的 media type。或者,用 URL 上的格式字尾明確表示。例如,URL http://example.com/api/users_count.json 可能始終返回 JSON 資料。

設定渲染器

可以使用 DEFAULT_RENDERER_CLASSES 設定全域性的預設渲染器集。例如,以下設定將使用JSON作為主要 media type,並且還包含自描述 API。

REST_FRAMEWORK = {
    'DEFAULT_RENDERER_CLASSES': (
        'rest_framework.renderers.JSONRenderer',
        'rest_framework.renderers.BrowsableAPIRenderer',
    )
}
複製程式碼

還可以使用基於 API​​View 的檢視類來設定單個檢視或檢視集的渲染器。

from django.contrib.auth.models import User
from rest_framework.renderers import JSONRenderer
from rest_framework.response import Response
from rest_framework.views import APIView

class UserCountView(APIView):
    """
    A view that returns the count of active users in JSON.
    """
    renderer_classes = (JSONRenderer, )

    def get(self, request, format=None):
        user_count = User.objects.filter(active=True).count()
        content = {'user_count': user_count}
        return Response(content)
複製程式碼

或者是在基於 @api_view 裝飾器的函式檢視上設定。

@api_view(['GET'])
@renderer_classes((JSONRenderer,))
def user_count_view(request, format=None):
    """
    A view that returns the count of active users in JSON.
    """
    user_count = User.objects.filter(active=True).count()
    content = {'user_count': user_count}
    return Response(content)
複製程式碼

渲染器類的優先順序

在為 API 指定渲染器類時,需要考慮它們處理每種媒體型別時的優先順序,這點很重要。如果客戶端沒有指定接受資料的表現形式,例如傳送 Accept:*/* header,或者根本不包含 Accept header,則 REST framework 將選擇列表中的第一個渲染器用於響應。

例如,如果你的 API 提供 JSON 響應和可瀏覽的 HTML API,則可能需要將 JSONRenderer 作為預設渲染器,以便將 JSON 響應傳送給未指定 Accept header 的客戶端。

如果你的 API 包含可根據請求同時處理常規網頁和 API 響應的檢視,那麼你可以考慮將 TemplateHTMLRenderer 設定為預設渲染器,以便與傳送 broken accept headers 的老式瀏覽器很好地配合使用。

API 參考

JSONRenderer

使用 utf-8 編碼將請求資料呈現為 JSON

請注意,預設風格包含 unicode 字元,並使用緊湊風格呈現(沒有多餘的空白)響應:

{"unicode black star":"★","value":999}
複製程式碼

客戶端可能還會包含 “縮排” media type 引數,在這種情況下,返回的 JSON 將會縮排。

比如: Accept: application/json; indent=4

{
    "unicode black star": "★",
    "value": 999
}
複製程式碼

使用 UNICODE_JSONCOMPACT_JSON 設定鍵可以更改預設的 JSON 編碼風格。

.media_typeapplication/json

.format'.json'

.charsetNone

TemplateHTMLRenderer

使用 Django 的標準模板將資料呈現為 HTML。與其他渲染器不同,傳遞給 Response 的資料不需要序列化。另外,建立 Response 時可能需要包含 template_name 引數。

TemplateHTMLRenderer 將建立一個 RequestContext,使用 response.data 作為上下文字典,並確定用於呈現上下文的模板名稱。

模板名稱由(按優先順序)確定:

  1. 傳遞給 response 的顯式 template_name 引數。
  2. 在此類上設定明確的 .template_name 屬性。
  3. 呼叫 view.get_template_names() 的返回結果。

使用 TemplateHTMLRenderer 的檢視示例:

class UserDetail(generics.RetrieveAPIView):
    """
    A view that returns a templated HTML representation of a given user.
    """
    queryset = User.objects.all()
    renderer_classes = (TemplateHTMLRenderer,)

    def get(self, request, *args, **kwargs):
        self.object = self.get_object()
        return Response({'user': self.object}, template_name='user_detail.html')
複製程式碼

你可以使用 TemplateHTMLRenderer 來使 REST framework 返回常規 HTML 頁面,或者從單個端點(a single endpoint)返回 HTML 和 API 響應。

如果你正在構建使用 TemplateHTMLRenderer 以及其他渲染器類的網站,則應考慮將 TemplateHTMLRenderer 列為 renderer_classes 列表中的第一個類,以便即使對於傳送格式錯誤的 ACCEPT: header 的瀏覽器,也會優先考慮它。

.media_typetext/html

.format'.html'

.charsetutf-8

StaticHTMLRenderer

一個簡單的渲染器,它只是返回預渲染的 HTML。與其他渲染器不同,傳遞給響應物件的資料應該是表示要返回的內容的字串。

使用 StaticHTMLRenderer 的檢視示例:

@api_view(('GET',))
@renderer_classes((StaticHTMLRenderer,))
def simple_html_view(request):
    data = '<html><body><h1>Hello, world</h1></body></html>'
    return Response(data)
複製程式碼

你可以使用 StaticHTMLRenderer 來使 REST framework 返回常規 HTML 頁面,或者從單個端點(a single endpoint)返回 HTML 和 API 響應。

.media_typetext/html

.format'.html'

.charsetutf-8

BrowsableAPIRenderer

將資料呈現為可瀏覽的 HTML API:

Django REST framework API 指南(8):渲染

該渲染器將確定哪個其他渲染器被賦予最高優先順序,並使用該渲染器在 HTML 頁面中顯示 API 風格響應。

.media_typetext/html

.format'.api'

.charsetutf-8

.template'rest_framework/api.html'

自定義 BrowsableAPIRenderer

預設情況下,除 BrowsableAPIRenderer 之外,響應內容將使用最高優先順序的渲染器渲染。如果你需要自定義此行為,例如,將 HTML 用作預設返回格式,但在可瀏覽的 API 中使用 JSON,則可以通過覆蓋 get_default_renderer() 方法來實現。

例如:

class CustomBrowsableAPIRenderer(BrowsableAPIRenderer):
    def get_default_renderer(self, view):
        return JSONRenderer()
複製程式碼

AdminRenderer

將資料呈現為 HTML,以顯示類似管理員的內容:

Django REST framework API 指南(8):渲染

該渲染器適用於 CRUD 風格的 Web API,該 API 還應提供用於管理資料的使用者友好介面。

請注意, AdminRenderer 對於巢狀或列出序列化輸入的檢視不起作用,因為 HTML 表單無法正確支援它們。

注意:當資料中存在正確配置的 URL_FIELD_NAME (預設為 url)屬性時, AdminRenderer 僅能夠包含指向詳細頁面的連結。對於 HyperlinkedModelSerializer ,情況就是這樣,但對於 ModelSerializer 類或普通 Serializer 類,你需要確保明確包含該欄位。例如,在這裡我們使用模型 get_absolute_url 方法:

class AccountSerializer(serializers.ModelSerializer):
    url = serializers.CharField(source='get_absolute_url', read_only=True)

    class Meta:
        model = Account
複製程式碼

.media_typetext/html

.format'.admin'

.charsetutf-8

.template'rest_framework/admin.html'

HTMLFormRenderer

將序列化返回的資料呈現為 HTML 表單。該渲染器的輸出不包含封閉的 <form> 標籤,隱藏的 CSRF 輸入或任何提交按鈕。

這個渲染器不是直接使用,而是可以通過將一個序列化器例項傳遞給 render_form 模板標籤來在模板中使用。

{% load rest_framework %}

<form action="/submit-report/" method="post">
    {% csrf_token %}
    {% render_form serializer %}
    <input type="submit" value="Save" />
</form>
複製程式碼

.media_typetext/html

.format'.form'

.charsetutf-8

.template'rest_framework/horizontal/form.html'

MultiPartRenderer

該渲染器用於呈現 HTML multipart form 資料。它不適合作為響應渲染器,而是用於建立測試請求,使用REST framework 的測試客戶端和測試請求工廠。

.media_typemultipart/form-data; boundary=BoUnDaRyStRiNg

.format'.multipart'

.charsetutf-8

自定義渲染器

要實現自定義渲染器,您應該繼承 BaseRenderer ,設定 .media_type.format 屬性,並實現 .render(self, data, media_type=None, renderer_context=None) 方法。

該方法應返回一個字串,它將用作 HTTP 響應的主體。

傳遞給 .render() 方法的引數是:

data

請求資料,由 Response() 例項化時設定。

media_type=None

可選的。如果提供,這是接受的媒體型別,由內容協商(content negotiation)階段確定。

依賴於客戶端的 Accept: header,它可以比渲染器的 media_type 屬性更具體,並且可能包含媒體型別引數。比如 "application/json; nested=true"

renderer_context=None

可選的。如果提供,它是檢視提供的上下文資訊字典。

預設情況下,這將包括以下鍵:view , request , response , args , kwargs

舉個栗子

以下是一個示例純文字渲染器,它將返回帶有資料引數的響應作為響應的內容。

from django.utils.encoding import smart_unicode
from rest_framework import renderers


class PlainTextRenderer(renderers.BaseRenderer):
    media_type = 'text/plain'
    format = 'txt'

    def render(self, data, media_type=None, renderer_context=None):
        return data.encode(self.charset)
複製程式碼

設定 charset

預設情況下,渲染器類被假定為使用 UTF-8 編碼。要使用不同的編碼,請在渲染器上設定 charset 屬性。

class PlainTextRenderer(renderers.BaseRenderer):
    media_type = 'text/plain'
    format = 'txt'
    charset = 'iso-8859-1'

    def render(self, data, media_type=None, renderer_context=None):
        return data.encode(self.charset)
複製程式碼

請注意,如果渲染器類返回一個 unicode 字串,則響應內容將被 Response 類強制為一個 bytestring,請在渲染器上設定 charset 屬性用於確定編碼。

如果渲染器返回代表原始二進位制內容的字串,則應將 charset 值設定為 None,這將確保響應的 Content-Type header 不會設定 charset 值。

在某些情況下,你可能還想將 render_style 屬性設定為 'binary'。這樣做也將確保可瀏覽的 API 不會嘗試將二進位制內容顯示為字串。

class JPEGRenderer(renderers.BaseRenderer):
    media_type = 'image/jpeg'
    format = 'jpg'
    charset = None
    render_style = 'binary'

    def render(self, data, media_type=None, renderer_context=None):
        return data
複製程式碼

渲染器的高階用法

你可以使用 REST framework 的渲染器來做一些非常靈活的事情。例如...

  • 根據請求的 media type,提供來自同一端點的平面或巢狀(flat or nested)表示。
  • 同時處理常規 HTML 網頁和來自相同端點的基於 JSON 的 API 響應。
  • 指定 API 客戶端使用的多種 HTML 表示形式。
  • 不用明確指定渲染器的 media type,例如使用 media_type ='image/*',並使用 Accept header 改變響應的編碼。

media type 的變化

在某些情況下,可能希望檢視根據接受的 media type 使用不同的序列化風格。如果需要這樣做,可以訪問 request.accepted_renderer 以確定將用於響應的協商(negotiate)渲染器。

例如:

@api_view(('GET',))
@renderer_classes((TemplateHTMLRenderer, JSONRenderer))
def list_users(request):
    """
    A view that can return JSON or HTML representations
    of the users in the system.
    """
    queryset = Users.objects.filter(active=True)

    if request.accepted_renderer.format == 'html':
        # TemplateHTMLRenderer takes a context dict,
        # and additionally requires a 'template_name'.
        # It does not require serialization.
        data = {'users': queryset}
        return Response(data, template_name='list_users.html')

    # JSONRenderer requires serialized data as normal.
    serializer = UserSerializer(instance=queryset)
    data = serializer.data
    return Response(data)
複製程式碼

不明確的 media type

在某些情況下,可能需要渲染器來提供一系列 media type 。在這種情況下,可以通過使用 media_type 值(如 image/**/*)來指定它應該響應的 media type 。

如果沒有明確指定渲染器的 media type ,則應確保在返回響應時使用 content_type 屬性明確指定 media type 。比如:

return Response(data, content_type='image/png')
複製程式碼

設計你的 media type

出於許多 Web API 的目的,具有超連結關係的簡單 JSON 響應可能就足夠了。如果你想完全融入 RESTful 設計和 HATEOAS,則需要更詳細地考慮 media type 的設計和使用。

HTML error 檢視

通常情況下,無論處理常規響應還是引發異常的響應(例如 Http404PermissionDenied 異常)或 APIException 的子類引起的響應,渲染器都會有相同的表現。

如果您使用的是 TemplateHTMLRendererStaticHTMLRenderer,並且引發異常,則行為會稍有不同,反映了 Django 對錯誤檢視的預設處理。

由 HTML 渲染器引發和處理的異常將嘗試使用以下方法之一(按優先順序)進行渲染。

  • 載入並渲染模板 {status_code}.html
  • 載入並渲染模板 api_exception.html
  • 渲染 HTTP 狀態碼和文字,例如 "404 Not Found"。

模板將使用 RequestContext 進行渲染,其中包含 status_codedetails 鍵。

注意:如果 DEBUG = True,則會顯示 Django 的標準錯誤回溯頁面,而不是顯示 HTTP 狀態碼和文字。

相關文章