본문 바로가기
Django/Django REST framework

8. Renderers

by hyun-am 2021. 7. 7.

Renderers

 

TemplateResponse 인스턴스를 클라이언트로 반환하려면 먼저 인스턴스를 렌더링해야 합니다. 렌더링 프로세스는 템플릿과 컨텍스트를 중간으로 표현하여 클라이언트에 제공할 수 있는 최종 byte stream으로 변환합니다.

REST 프레임워크에는 다양한 media types으로 응답을 반환할 수 있는 여러 기본 렌더러 클래스가 포함되어 있습니다. 또한 고유한 사용자 정의 렌더러를 정의할 수 있으므로 고유한 media type을 유연하게 설계할 수 있습니다.

렌더러 결정 방법

view에 대한 유효한 렌더러 집합은 항상 클래스 목록으로 정의됩니다. view를 입력하려면 DRF가 들어오는 요청에 대해 콘텐츠 협상을 수행하고 요청을 충족하는 가장 적합한 렌더러를 결정합니다.

컨텐츠 협상의 기본 로세스에는 요청에 필요한 미디어타입을 결정하기 위해 요청의 accept 헤더를 검토하는 작업이 포함됩니다.

선택적으로 URL의 형식 접미사를 사용하여 특정 표현을 명시적으로 요청할 수 있습니다. 예를 들어 URL → http://example.com/api/users_count.json은 항상 JSON 데이터를 반환하는 끝점일 수 있습니다.

자세한 내용은 컨텐츠 협상에 대한 설명서를 참고하겠습니다.

렌더러 설정

기본 렌더러 set은 DEFAULT_RENDERER_CLASSES 설정을 사용하여 전체적으로 설정할 수 있습니다. 예를 들어 다음 설정에서는 JSON을 기본 미디어 유형으로 사용하고 자체 설명 API도 포함합니다.

REST_FRAMEWORK = {
    'DEFAULT_RENDERER_CLASSES': [
        'rest_framework.renderers.JSONRenderer',
        'rest_framework.renderers.BrowsableAPIRenderer',
    ]
}

또한 API View CBV를 사용하여 개별 view 또는 viewset에 사용되는 렌더러를 설정할 수 있습니다.

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)

또는 FBV와 함께 @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)

Ordering of renderer classes

API에 렌더러 클래스를 지정할 때 각 미디어 타입에 할당할 우선 순위를 고려하는 것이 중요합니다. 클라이언트가 Accept: */* 헤더 전송 또는 수락 헤더를 전혀 포함하지 않는 등 수락할 수 있는 표현을 과소 지정하면 REST 프레임워크가 목록에서 응답에 사용할 첫 번째 렌더러를 선택합니다.

 

예를 들어 API가 JSON 응답과 HTML 검색 가능 API를 제공하는 경우 Accept 헤더를 지정하지 않은 클라이언트에 JSON 응답을 보내기 위해 JSON렌더를 기본 렌더러로 설정할 수 있습니다.

 

요청에 따라 API에 일반 웹 페이지와 API 응답을 모두 제공할 수 있는 view가 포함된 경우 손상된 accept 헤더를 보내는 이전 브라우저에서 TemplateHTMLRenderer을 기본 렌더러로 지정하는 것이 좋습니다.

 

API Reference

JSONRenderer

utf-8 인코딩을 사용하여 요청 데이터를 JSON으로 렌더링 합니다.

기본 스타일은 유니코드 문자를 포함하고 불필요한 공백이 없는 콤팩트 스타일을 사용하여 응답을 렌더링하는 것입니다.

{"unicode black star":"★","value":999}

클라이언트는 '인덴트' 미디어 유형 매개 변수를 추가로 포함할 수 있으며, 이 경우 반환된 JSON이 들여쓰여집니다. 예 : Accept: application/json; indent=4

 

{
    "unicode black star": "★",
    "value": 999
}

UNICODE_JSON 및 COMPCT_JSON 설정 키를 사용하여 기본 JSON 인코딩 스타일을 변경할 수 있습니다.

.media_type: application/json

.format: 'json'

.charset: None

 

TemplateHTMLRenderer

Django의 표준 템플릿 렌더링을 사용하여 데이터를 HTML로 렌더링합니다. 다른 렌더러와 달리 응답에 전달된 데이터는 직렬화할 필요가 없습니다. 또한 다른 렌더러와 달리 응답을 생성할 때 template_name 아규먼트를 포함할 수도 있습니다.

TemplateHTMLRenderer는 response.data를 컨텍스트 딕셔너리로 사용하여 RequestContext를 생성하고 컨텍스트를 렌더링하는 데 사용할 템플릿 이름을 결정합니다.

참고 : 시리얼라이저를 사용하는 View에서 사용할 경우, 렌더링을 위해 전송된 응답이 아닌 딕셔너리가 아닐 수 있으며 템플릿을 허용하려면 리턴하기 전에 딕셔너리로 감싸야 합니다.

response.data = {'results': response.data}

템플릿 이름은 (기본 설정 순서)에 의해 결정됩니다.

  1. 명시적 template_name 아규먼트가 응답에 전달되었습니다.
  2. 이 클래스에 설정된 명시적 .template_name 특성입니다.
  3. view.get_template_names() 호출의 반환 결과입니다.

view에서 사용하는 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와 DRF를 사용하여 일반 HTML 페이지를 반환하거나 endpoint에서 HTML 및 API응답을 모두반할 수 있습니다.

다른 렌더러 클래스와 함께 TemplateHTMLRenderer를 사용하는 웹 사이트를 구축하려면 renderer_classes목록의 첫 번째 클래스로 TemplateHTMLRenderer를 나열하는 것이 좋습니다. 이렇게 하면 잘못된 형식의 ACCEPT:헤더를 보내는 브라우저에서도 우선 순위를 지정할 수 있습니다.

See the *HTML & Forms* Topic Page for further examples of TemplateHTMLRenderer usage.

.media_typetext/html

.format'html'

.charsetutf-8

See also: StaticHTMLRenderer

StaticHTMLRenderer

미리 렌더링 된 HTML을 단순히 반환하는 간단한 렌더러 입니다. 다른 렌더러와 달리 응답 객체에 전달되는 데이터는 반환할 콘텐츠를 나타내는 문자열이여야합니다.

StaticHTMLRenderer를 사용하는 view의 예시

@api_view(['GET'])
@renderer_classes([StaticHTMLRenderer])
def simple_html_view(request):
    data = '<html><body><h1>Hello, world</h1></body></html>'
    return Response(data)

StaticHTMLRenderer와 DRF를 사용하여 일반 HTML페이지를 반환하거나 단일 endpoint에서 HTML 및 API응답을 모두 반환할 수 있습니다.

.media_type: text/html

.format: 'html'

.charset: utf-8

See also: TemplateHTMLRenderer

BrowsableAPIRenderer

Browsable API를 위해 데이터를 HTML로 렌더링합니다.

이 렌더러는 어떤 다른 렌더러가 가장 높은 우선순위를 받았는지 결정하고 이를 사용하여 HTML 페이지 내에 API 스타일 응답을 표시합니다.

.media_type: text/html

.format: 'api'

.charset: utf-8

.template: 'rest_framework/api.html'

Customizing BrowsableAPIRenderer

기본적으로 응답 콘텐츠는 BrowsableAPIRenderer를 제외하고 우선 순위가 가장 높은 렌더러로 렌더링됩니다.

예를 들어 HTML을 기본 리턴형식으로 사용하고 탐색 가능한 API에서 JSON을 사용하기 위해 이 동작을 사용자가 지정해야 하는 경우 get_default_renderer()메서드를 재정의하여 수행할 수 있습니다.

class CustomBrowsableAPIRenderer(BrowsableAPIRenderer):
    def get_default_renderer(self, view):
        return JSONRenderer()

AdminRenderer

관리자와 같은 디스플레이를 위해 데이터를 HTML로 렌더링합니다.

이 렌더러는 데이터 관리를 위한 사용자 친화적 인 인터페이스를 제공해야하는 CRUD 스타일 웹 API에 적합합니다.

입력에 대해 중첩되거나 나열되는 serializer가 있는 뷰는 HTML 양식에서 제대로 지원할 수 없기 때문에 AdminRenderer에서 제대로 작동하지 않습니다.

참고 : AdminRenderer는 올바르게 구성된 URL_FIELD_NAME(기본적으로 URL) 속성이 데이터에 있는 경우에만 세부 사항 페이지에 대한 링크를 포함할 수 있습니다. 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_type: text/html

.format: 'admin'

.charset: utf-8

.template: 'rest_framework/admin.html'

HTMLFormRenderer

serializer가 반환한 데이터를 HTML 형식으로 렌더링합니다. 이 렌더러의 출력에는 둘러싸는 <form> 태그, 숨겨진 CSRF 입력 또는 제출 버튼이 포함되지 않습니다.

이 렌더러는 직접 사용하기 위한 것이 아니라 SErializer 인스턴스를 render_form 탬플릿 태그에 전달하여 템플릿에서 대신 사용할 수 있습니다.

{% load rest_framework %}

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

For more information see the HTML & Forms documentation.

.media_type: text/html

.format: 'form'

.charset: utf-8

.template: 'rest_framework/horizontal/form.html'

MultiPartRenderer

이 렌더러는 HTML 멀티 파트 폼 데이터를 렌더링 하는데 사용됩니다. 응답 렌더러로는 적합하지 않지만 대신 DRF의 테스트 클라이언트 및 테스트 요청 팩토리를 사용하여 테스트 요청을 만드는 데 사용됩니다.

.media_type: multipart/form-data; boundary=BoUnDaRyStRiNg

.format: 'multipart'

.charset: utf-8

Custom renderers

사용자 지정 렌더러를 구현하려면 BaseRenderer를 재 정의하고 .media_type 및 .format 속성을 설정하고 .render(self, data, media_type = None, renderer_context = None)메서드를 구현해야 합니다.

이 메서드는 HTTP 응답의 content로 사용될 bytestring을 반환해야 합니다.

.render() 메서드에 전달되는 아규먼트는 다음과 같습니다.

data

Response() 인스턴스화에 의해 설정된 요청 데이터입니다.

media_type=None

optional합니다. 제공되는 경우 콘텐츠 협상 단계에서 결정된 허용 된 미디어 유형입니다.

클라이언트의 Accept : 헤더에 따라 이것은 렌더러의 media_type 속성보다 더 구체적 일 수 있으며 미디어 유형 매개 변수를 포함 할 수 있습니다. 예를 들면 "application/json; nested=true".

renderer_context=None

마찬가지로 optional 합니다. 제공되는 경우 view에서 제공하는 context한 딕셔너리 입니다.

기본적으로 여기에는 view,request,response,args,kwargs 키가 포함됩니다.

Example

다음은 응답 내용으로 data 파라미터가 있는 응답을 반환하는 plaintext 렌더러의 예입니다.

from django.utils.encoding import smart_text
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 smart_text(data, encoding=self.charset)

Setting the character

기본적으로 렌더러 클래스는 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)

렌더러 클래스가 유니 코드 문자열을 반환하는 경우 응답 콘텐츠는 인코딩을 결정하는 데 사용되는 렌더러에 설정된 charset 속성을 사용하여 Response 클래스에 의해 bytestring으로 강제 변환됩니다.

렌더러가 원시 바이너리 콘텐츠를 나타내는 바이트 문자열을 반환하는 경우 Charset 값을 None으로 설정해야 합니다. 이렇게하면 응답의 Content-type 헤더에 charset 값이 설정되지 않습니다.

어떤 경우에는 render_style 속성을 'binary'로 설정할 수도 있습니다. 이렇게하면 browsable한 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

Advanced renderer usage

drf 렌더러를 사용하여 매우 유연한 작업을 수행 할 수 있습니다.

  • 요청 된 미디어 타입에 따라 동일한 endpoint에서 플랫 또는 중첩 표현을 제공합니다.
  • 동일한 endpoint에서 일반 HTML 웹 페이지와 JSON 기반 API 응답을 모두 제공합니다.
  • API 클라이언트가 사용할 여러 유형의 HTML 표현을 지정합니다.
  • 렌더러의 미디어 유형(예시 : media_type = 'image/*')을 지정하고 Accept 헤더를 사용하여 응답의 인코딩을 변경합니다.

미디어 타입별 다양한 동작

어떤 경우에는 허용되는 미디어 유형에 따라 뷰에서 다른 직렬화 스타일을 사용하도록 할 수 있습니다. 이 작업을 수행해야하는 경우 request.accepted_renderer에 액세스하여 응답에 사용할 협상 된 렌더러를 결정할 수 있습니다.

예시는 다음과 같습니다.

@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)

미디어 타입 지정 부족

어떤 경우에는 렌더러가 다양한 미디어 타입을 제공하기를 원할 수 있습니다. 이 경우 image/* 또는 */* 와 같은 media_type 값을 사용하여 응답해야하는 미디어 유형을 과소 지정할 수 있습니다.

렌더러의 미디어 유형을 지정하지 않은 경우 응답을 반환할 때 content_type 속성을 사용하여 미디어 타입을 명시적으로 지정해야합니다. 예를들면 다음과 같습니다.

return Response(data, content_type='image/png')

미디어 타입 디자인 하기

많은 웹 API의 목적을 위해 하이퍼링크 관계가 있는 간단한 JSON 응답으로 충분할 수 있습니다. RESTful 디자인과 HATEOAS를 완전히 수용하려면 미디어 유형의 디자인과 사용을 더 자세히 고려해야 합니다.

HATEOAS : 애플리케이션 상태의 엔진으로서의 하이퍼 미디어는 다른 네트워크 애플리케이션 아키텍처와 구별되는 REST 애플리케이션 아키텍처의 제약입니다.

HTML error views

일반적으로 렌더러는 일반 응답을 처리하는지 또는 Http404 또는 PermissionDenied 예외 또는 APIException의 하위 클래스와 같이 발생하는 예외로 인해 발생하는 응답에 관계없이 동일하게 작동합니다.

TemplateHTMLRenderer 또는 StaticHTMLRenderer를 사용하고 예외가 발생하면 동작이 약간 다르며 Django의 기본 에러 뷰 처리를 반영합니다.

HTML 렌더러에서 발생하고 처리하는 예외는 우선 순위에 따라 다음 방법 중 하나를 사용하여 렌더링을 시도합니다.

  • Load and render a template named {status_code}.html.
  • Load and render a template named api_exception.html.
  • Render the HTTP status code and text, for example "404 Not Found".

템플릿은 status_code 및 세부 정보 키를 포함하는 RequestContext로 렌더링됩니다.

참고 : DEBUG = True이면 HTTP 상태 코드와 텍스트를 렌더링하는 대신 Django의 traceback 오류 페이지가 표시됩니다.

 

참고링크 : 

https://www.django-rest-framework.org/api-guide/renderers/#customizing-browsableapirenderer

 

Renderers - Django REST framework

 

www.django-rest-framework.org

 

'Django > Django REST framework' 카테고리의 다른 글

9-2 Serializers-2  (0) 2021.07.15
9-1. Serializers  (0) 2021.07.15
7. Parsers  (0) 2021.06.29
6. Routers  (0) 2021.06.28
5. ViewSets  (0) 2021.06.26

댓글