Django REST framework API 指南(21):Schemas

wcode發表於2019-03-04

官方原文連結
本系列文章 github 地址
轉載請註明出處

Schemas

API schema 是一個非常有用的工具,它允許一系列用例,包括生成參考文件,或者驅動可以與 API 互動的動態客戶端庫。

安裝 Core API

你需要安裝 coreapi 軟體包才能為 REST framework 新增 schema 支援。

pip install coreapi
複製程式碼

內部 schema 表示

REST framework 使用 Core API 以便以獨立於格式的表示對 schema 資訊建模。這些資訊可以被渲染成各種不同的 schema 格式,或者用於生成 API 文件。

在使用 Core API 時,schema 表示為 Document,它是有關 API 資訊的頂級容器物件。可用的 API 互動使用 Link 物件表示。每個連結都包含一個 URL,HTTP 方法,並且可能包含一個 Field 例項列表,它描述了 API 端點可以接受的任何引數。LinkField 例項還可能包含描述,允許將 API schema 渲染到使用者文件中。

以下是包含單個搜尋端點的 API 說明示例:

coreapi.Document(
    title=`Flight Search API`,
    url=`https://api.example.org/`,
    content={
        `search`: coreapi.Link(
            url=`/search/`,
            action=`get`,
            fields=[
                coreapi.Field(
                    name=`from`,
                    required=True,
                    location=`query`,
                    description=`City name or airport code.`
                ),
                coreapi.Field(
                    name=`to`,
                    required=True,
                    location=`query`,
                    description=`City name or airport code.`
                ),
                coreapi.Field(
                    name=`date`,
                    required=True,
                    location=`query`,
                    description=`Flight date in "YYYY-MM-DD" format.`
                )
            ],
            description=`Return flight availability and prices.`
        )
    }
)
複製程式碼

Schema 輸出格式

為了呈現在 HTTP 響應中,必須將內部表示渲染為響應中使用的實際位元組。

Core JSON 被設計為與 Core API 一起使用的規範格式。REST framework 包含用於處理此媒體型別的渲染器類,該渲染器類可用作 renderers.CoreJSONRenderer

備用 schema 格式

其他 schema 格式(如 Open API(“Swagger”),JSON HyperSchema 或 API Blueprint)也可以通過實現處理將 Document 例項轉換為字串表示形式的自定義渲染器類來支援。

如果有一個 Core API 編解碼器包支援將編碼格式化為你要使用的格式,則可以使用編解碼器來實現渲染器類。

舉個例子

例如,openapi_codec 包提供對 Open API(“Swagger”)格式的編碼或解碼支援:

from rest_framework import renderers
from openapi_codec import OpenAPICodec

class SwaggerRenderer(renderers.BaseRenderer):
    media_type = `application/openapi+json`
    format = `swagger`

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

Schemas vs 超媒體

值得指出的是,Core API 也可以用來模擬超媒體響應,它為 API schema 提供了另一種互動風格。

通過 API schema,整個可用介面作為單個端點呈現在前端。然後,對各個 API 端點的響應通常會以普通資料的形式呈現,而不會在每個響應中包含任何進一步的互動。

使用超媒體,客戶端會看到包含資料和可用互動的文件。每次互動都會生成一個新文件,詳細說明當前狀態和可用互動。


建立一個 schema

REST framework 包含用於自動生成 schema 的功能,或者允許你明確指定。

手動 Schema 規範

要手動指定 schema,請建立一個 Core API Document,與上例類似。

schema = coreapi.Document(
    title=`Flight Search API`,
    content={
        ...
    }
)
複製程式碼

自動 Schema 生成

自動 schema 生成由 SchemaGenerator 類提供。

SchemaGenerator 處理路由 URL pattterns 列表並編譯適當結構化的 Core API 文件。

基本用法只是為 schema 提供標題並呼叫 get_schema()

generator = schemas.SchemaGenerator(title=`Flight Search API`)
schema = generator.get_schema()
複製程式碼

按檢視 schema 自定義

預設情況下,檢視自省是由可通過 APIView 上的 schema 屬性訪問的 AutoSchema 例項執行的。這為檢視,請求方法和路徑提供了適當的 Core API Link 物件:

auto_schema = view.schema
coreapi_link = auto_schema.get_link(...)
複製程式碼

(在編譯模式時,SchemaGenerator 為每個檢視,允許的方法和路徑呼叫 view.schema.get_link()。)


注意: 對於基本的 APIView 子類,預設內省本質上僅限於 URL kwarg 路徑引數。對於包含所有基於類的檢視的 GenericAPIView 子類,AutoSchema 將嘗試自省列化器,分頁和過濾器欄位,並提供更豐富的路徑欄位描述。(這裡的關鍵鉤子是相關的 GenericAPIView 屬性和方法:get_serializerpagination_classfilter_backends 等。)


要自定義 Link 生成,你可以:

1)使用 manual_fields kwarg 在你的檢視上例項化 AutoSchema

from rest_framework.views import APIView
from rest_framework.schemas import AutoSchema

class CustomView(APIView):
    ...
    schema = AutoSchema(
        manual_fields=[
            coreapi.Field("extra_field", ...),
        ]
    )
複製程式碼

這允許擴充套件最常見的情況而不需要子類化。

2)提供具有更復雜定製的 AutoSchema 子類:

from rest_framework.views import APIView
from rest_framework.schemas import AutoSchema

class CustomSchema(AutoSchema):
    def get_link(...):
        # Implement custom introspection here (or in other sub-methods)

class CustomView(APIView):
    ...
    schema = CustomSchema()
複製程式碼

這提供了對檢視內省的完全控制。

3)在你的檢視上例項化 ManualSchema,顯式為檢視提供 Core API Fields

from rest_framework.views import APIView
from rest_framework.schemas import ManualSchema

class CustomView(APIView):
    ...
    schema = ManualSchema(fields=[
        coreapi.Field(
            "first_field",
            required=True,
            location="path",
            schema=coreschema.String()
        ),
        coreapi.Field(
            "second_field",
            required=True,
            location="path",
            schema=coreschema.String()
        ),
    ])
複製程式碼

這允許手動指定某些檢視的 schema,同時在別處保持自動生成。

通過將 schema 設定為 None,你可以禁用檢視的 schema 生成:

    class CustomView(APIView):
        ...
        schema = None  # Will not appear in schema
複製程式碼

新增 schema 檢視

有幾種不同的方式可以將 schema 檢視新增到你的 API 中,具體取決於你需要的內容。

get_schema_view 快捷方式

在你的專案中包含 schema 的最簡單方法是使用 get_schema_view() 函式。

from rest_framework.schemas import get_schema_view

schema_view = get_schema_view(title="Server Monitoring API")

urlpatterns = [
    url(`^$`, schema_view),
    ...
]
複製程式碼

新增檢視後,你將能夠通過 API 請求來檢索自動生成的 schema 定義。

$ http http://127.0.0.1:8000/ Accept:application/coreapi+json
HTTP/1.0 200 OK
Allow: GET, HEAD, OPTIONS
Content-Type: application/vnd.coreapi+json

{
    "_meta": {
        "title": "Server Monitoring API"
    },
    "_type": "document",
    ...
}
複製程式碼

get_schema_view() 的引數是:

title

可用於為 schema 定義提供描述性標題。

url

可用於為 schema 傳遞規範 URL。

schema_view = get_schema_view(
    title=`Server Monitoring API`,
    url=`https://www.example.org/api/`
)
複製程式碼

urlconf

表示要為其生成 API schema 的 URL conf 的匯入路徑的字串。這預設為 Django 的 ROOT_URLCONF setting 的值。

schema_view = get_schema_view(
    title=`Server Monitoring API`,
    url=`https://www.example.org/api/`,
    urlconf=`myproject.urls`
)
複製程式碼

renderer_classes

可用於傳遞渲染 API 根端點的渲染器類列表。

from rest_framework.schemas import get_schema_view
from rest_framework.renderers import CoreJSONRenderer
from my_custom_package import APIBlueprintRenderer

schema_view = get_schema_view(
    title=`Server Monitoring API`,
    url=`https://www.example.org/api/`,
    renderer_classes=[CoreJSONRenderer, APIBlueprintRenderer]
)
複製程式碼

patterns

將 schema 內省限定為 url patterns 列表。如果你只想將 myproject.api url 公開在 schema 中:

schema_url_patterns = [
    url(r`^api/`, include(`myproject.api.urls`)),
]

schema_view = get_schema_view(
    title=`Server Monitoring API`,
    url=`https://www.example.org/api/`,
    patterns=schema_url_patterns,
)
複製程式碼

generator_class

可用於指定要傳遞給 SchemaViewSchemaGenerator 子類。

authentication_classes

可用於指定將應用於 schema 端點的認證類列表。預設為 settings.DEFAULT_AUTHENTICATION_CLASSES

permission_classes

可用於指定將應用於 schema 端點的許可權類列表。預設為 settings.DEFAULT_PERMISSION_CLASSES

使用顯式 schema 檢視

如果你需要比 get_schema_view() 快捷方式更多的控制權,那麼你可以直接使用 SchemaGenerator 類來自動生成 Document 例項,並從檢視中返回該例項。

此選項使你可以靈活地設定 schema 端點,並使用你想要的任何行為。例如,你可以將不同的許可權,限流或身份驗證策略應用於 schema 端點。

以下是使用 SchemaGenerator 和檢視一起返回 schema 的示例。

views.py:

from rest_framework.decorators import api_view, renderer_classes
from rest_framework import renderers, response, schemas

generator = schemas.SchemaGenerator(title=`Bookings API`)

@api_view()
@renderer_classes([renderers.CoreJSONRenderer])
def schema_view(request):
    schema = generator.get_schema(request)
    return response.Response(schema)
複製程式碼

urls.py:

urlpatterns = [
    url(`/`, schema_view),
    ...
]
複製程式碼

你也可以為不同的使用者提供不同的 schema,具體取決於他們擁有的許可權。這種方法可以用來確保未經身份驗證的請求以不同的模式呈現給已驗證的請求,或者確保 API 的不同部分根據角色對不同使用者可見。

為了呈現一個 schema,其中包含由使用者許可權過濾的端點,你需要將 request 引數傳遞給 get_schema() 方法,如下所示:

@api_view()
@renderer_classes([renderers.CoreJSONRenderer])
def schema_view(request):
    generator = schemas.SchemaGenerator(title=`Bookings API`)
    return response.Response(generator.get_schema(request=request))
複製程式碼

顯式 schema 定義

自動生成方法的替代方法是通過在程式碼庫中宣告 Document 物件來明確指定 API schema 。這樣做會多一點工作,但確保你完全控制 schema 表示。

import coreapi
from rest_framework.decorators import api_view, renderer_classes
from rest_framework import renderers, response

schema = coreapi.Document(
    title=`Bookings API`,
    content={
        ...
    }
)

@api_view()
@renderer_classes([renderers.CoreJSONRenderer])
def schema_view(request):
    return response.Response(schema)
複製程式碼

靜態 schema 檔案

最後的選擇是使用 Core JSON 或 Open API 等可用格式之一將你的 API schema 編寫為靜態檔案。

然後你可以:

  • 將模式定義寫為靜態檔案,並直接提供靜態檔案。
  • 編寫一個使用 Core API 載入的 schema 定義,然後根據客戶端請求將其渲染為多種可用格式之一。

作為文件的 Schemas

API schemas 的一個常見用法是使用它們來構建文件頁面。

REST framework 中的 schema 生成使用文件字串來自動填充 schema 文件中的描述。

這些描述將基於:

  • 對應方法的文件字串(如果存在)。
  • 類文件字串中的命名部分,可以是單行或多行。
  • 類文件字串。

舉個例子

一個 APIView,帶有明確的方法文件字串。

class ListUsernames(APIView):
    def get(self, request):
        """
        Return a list of all user names in the system.
        """
        usernames = [user.username for user in User.objects.all()]
        return Response(usernames)
複製程式碼

一個 ViewSet,帶有一個明確的 action 文件字串。

class ListUsernames(ViewSet):
    def list(self, request):
        """
        Return a list of all user names in the system.
        """
        usernames = [user.username for user in User.objects.all()]
        return Response(usernames)
複製程式碼

類文件字串中帶有 action 的通用檢視,使用單行樣式。

class UserList(generics.ListCreateAPIView):
    """
    get: List all the users.
    post: Create a new user.
    """
    queryset = User.objects.all()
    serializer_class = UserSerializer
    permission_classes = (IsAdminUser,)
複製程式碼

使用多行樣式的類文件字串中帶有 action 的通用檢視集。

class UserViewSet(viewsets.ModelViewSet):
    """
    API endpoint that allows users to be viewed or edited.

    retrieve:
    Return a user instance.

    list:
    Return all users, ordered by most recently joined.
    """
    queryset = User.objects.all().order_by(`-date_joined`)
    serializer_class = UserSerializer
複製程式碼

API 參考

SchemaGenerator

一個遍歷路由 URL patterns 列表的類,為每個檢視請求 schema 並整理生成的 CoreAPI 文件。

通常你會用一個引數例項化 SchemaGenerator,如下所示:

generator = SchemaGenerator(title=`Stock Prices API`)
複製程式碼

引數:

  • title 必需 – API 的名稱。
  • url – API schema 的 root URL。除非 schema 包含在路徑字首下,否則此選項不是必需的。
  • patterns – 生成 schema 時要檢查的 URL 列表。預設為專案的 URL conf。
  • urlconf – 生成 schema 時使用的 URL conf 模組名稱。 預設為 settings.ROOT_URLCONF.

get_schema(self, request)

返回表示 API schema 的 coreapi.Document 例項。

@api_view
@renderer_classes([renderers.CoreJSONRenderer])
def schema_view(request):
    generator = schemas.SchemaGenerator(title=`Bookings API`)
    return Response(generator.get_schema())
複製程式碼

request 引數是可選的,如果你希望將每個使用者的許可權應用於生成的 schema ,則可以使用該引數。

get_links(self, request)

返回一個巢狀的字典,其中包含在 API schema 中的所有連結。

如果要修改生成的 schema 的結構,重寫該方法很合適,因為你可以使用不同的佈局構建新的字典。

AutoSchema

一個處理 schema 生成的個別檢視內省的類。

AutoSchema 通過 schema 屬性附加到 APIView

AutoSchema 建構函式接受一個關鍵字引數 manual_fields

manual_fields: 將新增到生成的欄位的 coreapi.Field 例項 list。具有匹配名稱的生成欄位將被覆蓋。

class CustomView(APIView):
    schema = AutoSchema(manual_fields=[
        coreapi.Field(
            "my_extra_field",
            required=True,
            location="path",
            schema=coreschema.String()
        ),
    ])
複製程式碼

對於通過繼承 AutoSchema 來自定義 schema 生成。

class CustomViewSchema(AutoSchema):
    """
    Overrides `get_link()` to provide Custom Behavior X
    """

    def get_link(self, path, method, base_url):
        link = super().get_link(path, method, base_url)
        # Do something to customize link here...
        return link

class MyView(APIView):
  schema = CustomViewSchema()
複製程式碼

以下方法可覆蓋。

get_link(self, path, method, base_url)

返回與給定檢視相對應的 coreapi.Link 例項。

這是主要的入口點。如果你需要為特定檢視提供自定義行為,則可以覆蓋此內容。

get_description(self, path, method)

返回用作連結描述的字串。預設情況下,這基於上面的 “作為文件的 Schemas” action 中描述的檢視文件字串。

get_encoding(self, path, method)

與給定檢視互動時返回一個字串,以指定任何請求主體的編碼。 例如 `application/json`。可能會返回一個空白字串,以便檢視不需要請求主體的檢視。

get_path_fields(self, path, method):

返回 coreapi.Link() 例項列表。用於 URL 中的每個路徑引數。

get_serializer_fields(self, path, method)

返回 coreapi.Link() 例項列表。用於檢視使用的序列化類中的每個欄位。

get_pagination_fields(self, path, method)

返回 coreapi.Link() 例項列表,該列表由 get_schema_fields() 方法返回給檢視使用的分頁類。

get_filter_fields(self, path, method)

返回 coreapi.Link() 例項列表,該列表是由檢視所使用的過濾器類的 get_schema_fields() 方法返回的。

get_manual_fields(self, path, method)

返回 coreapi.Field() 例項列表以新增或替換生成的欄位。預設為(可選)傳遞給 AutoSchema 建構函式的 manual_fields

可以通過 pathmethod 覆蓋自定義 manual field。例如,每個方法的調整可能如下所示:

def get_manual_fields(self, path, method):
    """Example adding per-method fields."""

    extra_fields = []
    if method==`GET`:
        extra_fields = # ... list of extra fields for GET ...
    if method==`POST`:
        extra_fields = # ... list of extra fields for POST ...

    manual_fields = super().get_manual_fields(path, method)
    return manual_fields + extra_fields
複製程式碼

update_fields(fields, update_with)

實用的 staticmethod。封裝邏輯以通過 Field.name 新增或替換列表中的欄位。可能會被覆蓋以調整替換標準。

ManualSchema

允許手動為 schema 提供 coreapi.Field 例項的列表,以及一個可選的描述。

class MyView(APIView):
  schema = ManualSchema(fields=[
        coreapi.Field(
            "first_field",
            required=True,
            location="path",
            schema=coreschema.String()
        ),
        coreapi.Field(
            "second_field",
            required=True,
            location="path",
            schema=coreschema.String()
        ),
    ]
  )
複製程式碼

ManualSchema 建構函式有兩個引數:

fields: coreapi.Field 例項列表。必需。

description: 字串描述。可選的。


Core API

本文件簡要介紹了用於表示 API schema 的 coreapi 包內的元件。

請注意,這些類是從 coreapi 包匯入的,而不是從 rest_framework 包匯入的。

Document

表示 API schema 的容器。

title

API 的名稱。

url

API 的規範 URL。

content

一個字典,包含 schema 的 Link 物件。

為了向 schema 提供更多結構,content 字典可以巢狀,通常是二層。例如:

content={
    "bookings": {
        "list": Link(...),
        "create": Link(...),
        ...
    },
    "venues": {
        "list": Link(...),
        ...
    },
    ...
}
複製程式碼

Link

代表一個單獨的 API 端點。

url

端點的 URL。可能是一個 URI 模板,例如 /users/{username}/

action

與端點關聯的 HTTP 方法。請注意,支援多個 HTTP 方法的 url 應該對應於每個 HTTP 方法的單個連結。

fields

Field 例項列表,描述輸入上的可用引數。

description

對端點的含義和用途的簡短描述。

Field

表示給定 API 端點上的單個輸入引數。

name

輸入的描述性名稱。

required

boolean 值,表示客戶端是否需要包含值,或者引數是否可以省略。

location

確定如何將資訊編碼到請求中。應該是以下字串之一:

“path”

包含在模板化的 URI 中。例如,/products/{product_code}/url 值可以與 "path" 欄位一起使用,以處理 URL 路徑中的 API 輸入,例如 /products/slim-fit-jeans/

這些欄位通常與專案 URL conf 中的命名引數對應。

“query”

包含為 URL 查詢引數。例如 ?search=sale。通常用於 GET 請求。

這些欄位通常與檢視上的分頁和過濾控制元件相對應。

“form”

包含在請求正文中,作為 JSON 物件或 HTML 表單的單個 item。例如 {"colour": "blue", ...}。通常用於POSTPUTPATCH 請求。多個 "form" 欄位可能包含在單個連結上。

這些欄位通常與檢視上的序列化類欄位相對應。

“body”

包含完整的請求主體。通常用於 POST, PUTPATCH 請求。連結上不得存在超過一個 "body" 欄位。不能與 "form" 欄位一起使用。

這些欄位通常對應於使用 ListSerializer來驗證請求輸入或使用檔案上載的檢視。

encoding

“application/json”

JSON編碼的請求內容。對應於使用 JSONParser 的檢視。僅在 Link 中包含一個或多個 location="form" 欄位或單個 location="body" 欄位時有效。

“multipart/form-data”

Multipart 編碼的請求內容。對應於使用 MultiPartParser 的檢視。僅在 Link 中包含一個或多個 location="form" 欄位時有效。

“application/x-www-form-urlencoded”

URL encode 的請求內容。對應於使用 FormParser 的檢視。僅在 Link 中包含一個或多個 location="form" 欄位時有效。

“application/octet-stream”

二進位制上傳請求內容。對應於使用 FileUploadParser 的檢視。僅在 Link 中包含 location="body" 欄位時有效。

description

對輸入欄位的含義和用途的簡短描述。

相關文章