7
7
REST framework also provides an HTML renderer that renders the browsable API.
8
8
"""
9
9
10
- import base64
11
10
import contextlib
12
11
import datetime
13
- from urllib import parse
14
12
15
13
from django import forms
16
14
from django .conf import settings
17
15
from django .core .exceptions import ImproperlyConfigured
18
16
from django .core .paginator import Page
19
17
from django .template import engines , loader
20
18
from django .urls import NoReverseMatch
21
- from django .utils .html import mark_safe
22
19
from django .utils .http import parse_header_parameters
23
20
from django .utils .safestring import SafeString
24
21
25
22
from rest_framework import VERSION , exceptions , serializers , status
26
23
from rest_framework .compat import (
27
- INDENT_SEPARATORS , LONG_SEPARATORS , SHORT_SEPARATORS , coreapi , coreschema ,
28
- pygments_css , yaml
24
+ INDENT_SEPARATORS , LONG_SEPARATORS , SHORT_SEPARATORS , pygments_css , yaml
29
25
)
30
26
from rest_framework .exceptions import ParseError
31
27
from rest_framework .request import is_form_media_type , override_method
@@ -418,7 +414,7 @@ def get_content(self, renderer, data,
418
414
419
415
render_style = getattr (renderer , 'render_style' , 'text' )
420
416
assert render_style in ['text' , 'binary' ], 'Expected .render_style ' \
421
- '"text" or "binary", but got "%s"' % render_style
417
+ '"text" or "binary", but got "%s"' % render_style
422
418
if render_style == 'binary' :
423
419
return '[%d bytes of binary content]' % len (content )
424
420
@@ -487,8 +483,8 @@ def get_rendered_html_form(self, data, view, method, request):
487
483
has_serializer_class = getattr (view , 'serializer_class' , None )
488
484
489
485
if (
490
- (not has_serializer and not has_serializer_class ) or
491
- not any (is_form_media_type (parser .media_type ) for parser in view .parser_classes )
486
+ (not has_serializer and not has_serializer_class ) or
487
+ not any (is_form_media_type (parser .media_type ) for parser in view .parser_classes )
492
488
):
493
489
return
494
490
@@ -837,7 +833,7 @@ def get_result_url(self, result, view):
837
833
and viewset-like (has `.basename` / `.reverse_action()`).
838
834
"""
839
835
if not hasattr (view , 'reverse_action' ) or \
840
- not hasattr (view , 'lookup_field' ):
836
+ not hasattr (view , 'lookup_field' ):
841
837
return
842
838
843
839
lookup_field = view .lookup_field
@@ -850,57 +846,6 @@ def get_result_url(self, result, view):
850
846
return
851
847
852
848
853
- class DocumentationRenderer (BaseRenderer ):
854
- media_type = 'text/html'
855
- format = 'html'
856
- charset = 'utf-8'
857
- template = 'rest_framework/docs/index.html'
858
- error_template = 'rest_framework/docs/error.html'
859
- code_style = 'emacs'
860
- languages = ['shell' , 'javascript' , 'python' ]
861
-
862
- def get_context (self , data , request ):
863
- return {
864
- 'document' : data ,
865
- 'langs' : self .languages ,
866
- 'lang_htmls' : ["rest_framework/docs/langs/%s.html" % language for language in self .languages ],
867
- 'lang_intro_htmls' : ["rest_framework/docs/langs/%s-intro.html" % language for language in self .languages ],
868
- 'code_style' : pygments_css (self .code_style ),
869
- 'request' : request
870
- }
871
-
872
- def render (self , data , accepted_media_type = None , renderer_context = None ):
873
- if isinstance (data , coreapi .Document ):
874
- template = loader .get_template (self .template )
875
- context = self .get_context (data , renderer_context ['request' ])
876
- return template .render (context , request = renderer_context ['request' ])
877
- else :
878
- template = loader .get_template (self .error_template )
879
- context = {
880
- "data" : data ,
881
- "request" : renderer_context ['request' ],
882
- "response" : renderer_context ['response' ],
883
- "debug" : settings .DEBUG ,
884
- }
885
- return template .render (context , request = renderer_context ['request' ])
886
-
887
-
888
- class SchemaJSRenderer (BaseRenderer ):
889
- media_type = 'application/javascript'
890
- format = 'javascript'
891
- charset = 'utf-8'
892
- template = 'rest_framework/schema.js'
893
-
894
- def render (self , data , accepted_media_type = None , renderer_context = None ):
895
- codec = coreapi .codecs .CoreJSONCodec ()
896
- schema = base64 .b64encode (codec .encode (data )).decode ('ascii' )
897
-
898
- template = loader .get_template (self .template )
899
- context = {'schema' : mark_safe (schema )}
900
- request = renderer_context ['request' ]
901
- return template .render (context , request = request )
902
-
903
-
904
849
class MultiPartRenderer (BaseRenderer ):
905
850
media_type = 'multipart/form-data; boundary=BoUnDaRyStRiNg'
906
851
format = 'multipart'
@@ -921,139 +866,6 @@ def render(self, data, accepted_media_type=None, renderer_context=None):
921
866
return encode_multipart (self .BOUNDARY , data )
922
867
923
868
924
- class CoreJSONRenderer (BaseRenderer ):
925
- media_type = 'application/coreapi+json'
926
- charset = None
927
- format = 'corejson'
928
-
929
- def __init__ (self ):
930
- assert coreapi , 'Using CoreJSONRenderer, but `coreapi` is not installed.'
931
-
932
- def render (self , data , media_type = None , renderer_context = None ):
933
- indent = bool (renderer_context .get ('indent' , 0 ))
934
- codec = coreapi .codecs .CoreJSONCodec ()
935
- return codec .dump (data , indent = indent )
936
-
937
-
938
- class _BaseOpenAPIRenderer :
939
- def get_schema (self , instance ):
940
- CLASS_TO_TYPENAME = {
941
- coreschema .Object : 'object' ,
942
- coreschema .Array : 'array' ,
943
- coreschema .Number : 'number' ,
944
- coreschema .Integer : 'integer' ,
945
- coreschema .String : 'string' ,
946
- coreschema .Boolean : 'boolean' ,
947
- }
948
-
949
- schema = {}
950
- if instance .__class__ in CLASS_TO_TYPENAME :
951
- schema ['type' ] = CLASS_TO_TYPENAME [instance .__class__ ]
952
- schema ['title' ] = instance .title
953
- schema ['description' ] = instance .description
954
- if hasattr (instance , 'enum' ):
955
- schema ['enum' ] = instance .enum
956
- return schema
957
-
958
- def get_parameters (self , link ):
959
- parameters = []
960
- for field in link .fields :
961
- if field .location not in ['path' , 'query' ]:
962
- continue
963
- parameter = {
964
- 'name' : field .name ,
965
- 'in' : field .location ,
966
- }
967
- if field .required :
968
- parameter ['required' ] = True
969
- if field .description :
970
- parameter ['description' ] = field .description
971
- if field .schema :
972
- parameter ['schema' ] = self .get_schema (field .schema )
973
- parameters .append (parameter )
974
- return parameters
975
-
976
- def get_operation (self , link , name , tag ):
977
- operation_id = "%s_%s" % (tag , name ) if tag else name
978
- parameters = self .get_parameters (link )
979
-
980
- operation = {
981
- 'operationId' : operation_id ,
982
- }
983
- if link .title :
984
- operation ['summary' ] = link .title
985
- if link .description :
986
- operation ['description' ] = link .description
987
- if parameters :
988
- operation ['parameters' ] = parameters
989
- if tag :
990
- operation ['tags' ] = [tag ]
991
- return operation
992
-
993
- def get_paths (self , document ):
994
- paths = {}
995
-
996
- tag = None
997
- for name , link in document .links .items ():
998
- path = parse .urlparse (link .url ).path
999
- method = link .action .lower ()
1000
- paths .setdefault (path , {})
1001
- paths [path ][method ] = self .get_operation (link , name , tag = tag )
1002
-
1003
- for tag , section in document .data .items ():
1004
- for name , link in section .links .items ():
1005
- path = parse .urlparse (link .url ).path
1006
- method = link .action .lower ()
1007
- paths .setdefault (path , {})
1008
- paths [path ][method ] = self .get_operation (link , name , tag = tag )
1009
-
1010
- return paths
1011
-
1012
- def get_structure (self , data ):
1013
- return {
1014
- 'openapi' : '3.0.0' ,
1015
- 'info' : {
1016
- 'version' : '' ,
1017
- 'title' : data .title ,
1018
- 'description' : data .description
1019
- },
1020
- 'servers' : [{
1021
- 'url' : data .url
1022
- }],
1023
- 'paths' : self .get_paths (data )
1024
- }
1025
-
1026
-
1027
- class CoreAPIOpenAPIRenderer (_BaseOpenAPIRenderer ):
1028
- media_type = 'application/vnd.oai.openapi'
1029
- charset = None
1030
- format = 'openapi'
1031
-
1032
- def __init__ (self ):
1033
- assert coreapi , 'Using CoreAPIOpenAPIRenderer, but `coreapi` is not installed.'
1034
- assert yaml , 'Using CoreAPIOpenAPIRenderer, but `pyyaml` is not installed.'
1035
-
1036
- def render (self , data , media_type = None , renderer_context = None ):
1037
- structure = self .get_structure (data )
1038
- return yaml .dump (structure , default_flow_style = False ).encode ()
1039
-
1040
-
1041
- class CoreAPIJSONOpenAPIRenderer (_BaseOpenAPIRenderer ):
1042
- media_type = 'application/vnd.oai.openapi+json'
1043
- charset = None
1044
- format = 'openapi-json'
1045
- ensure_ascii = not api_settings .UNICODE_JSON
1046
-
1047
- def __init__ (self ):
1048
- assert coreapi , 'Using CoreAPIJSONOpenAPIRenderer, but `coreapi` is not installed.'
1049
-
1050
- def render (self , data , media_type = None , renderer_context = None ):
1051
- structure = self .get_structure (data )
1052
- return json .dumps (
1053
- structure , indent = 4 ,
1054
- ensure_ascii = self .ensure_ascii ).encode ('utf-8' )
1055
-
1056
-
1057
869
class OpenAPIRenderer (BaseRenderer ):
1058
870
media_type = 'application/vnd.oai.openapi'
1059
871
charset = None
@@ -1067,6 +879,7 @@ def render(self, data, media_type=None, renderer_context=None):
1067
879
class Dumper (yaml .Dumper ):
1068
880
def ignore_aliases (self , data ):
1069
881
return True
882
+
1070
883
Dumper .add_representer (SafeString , Dumper .represent_str )
1071
884
Dumper .add_representer (datetime .timedelta , encoders .CustomScalar .represent_timedelta )
1072
885
return yaml .dump (data , default_flow_style = False , sort_keys = False , Dumper = Dumper ).encode ('utf-8' )
0 commit comments