django rest framwork教程之外鍵關系和超鏈接


此時,我們的API中的關系通過使用主鍵來表示。在本教程的這一部分中,我們將通過使用超鏈接來改善關系的內聚性和可發現性

為我們的API的根創建一個端點

現在我們有“snippets”和“users”的端點,但我們沒有到我們的API的單個入口點。要創建一個,我們將使用一個常規的基於函數的視圖和我們前面介紹的@api_view裝飾器,在views.py中添加如下代碼:

from rest_framework.decorators import api_view
from rest_framework.response import Response
from rest_framework.reverse import reverse
@api_view(['GET'])
def api_root(request, format=None):
    return Response({
        'users': reverse('user-list', request=request, format=format),
        'snippets': reverse('snippet-list', request=request, format=format)
    })

這里需注意兩件事,首先我們使用rest框架的reverse函數來返回完全限定的URL,其次,URL模式由方便名稱標示,我們將在后面的代碼片段urls.py中聲明

為高亮顯示的代碼段創建端點

我們還需要提供高亮snippet的路徑. 當然這一路徑與其他不同, 我們希望使用HTML而不是JSON來呈現. Django-rest_framework為我們提供了兩種方式呈現HTML, 一種是使用模板, 另一種則是已構建好的HTML文本. 由於在創建snippet時, 我們已經使用pygments將高亮的snippet轉化為HTML文本儲存在數據庫中, 我們使用第二種方式.

由於我們返回的並不是一個object實例, 而是一個實例的某個屬性, django-rest-framework沒有提供該generic class based view. 因此我們需要使用基本的view, 並創建get()方法:

from rest_framework import renderers
from rest_framework.response import Response

class SnippetHighlight(generics.GenericAPIView):
    queryset = Snippet.objects.all()
    renderer_classes = (renderers.StaticHTMLRenderer,)

    def get(self, request, *args, **kwargs):
        snippet = self.get_object()
        return Response(snippet.highlighted)

在urls.py中添加對應的路徑:

url(r'^snippets/(?P<pk>[0-9]+)/highlight/$', views.SnippetHighlight.as_view()),

測試:curl 結果為html

curl  -X GET  -s -u test:1234.com  http://127.0.0.1:8000/snippets/1/highlight/
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
   "http://www.w3.org/TR/html4/strict.dtd">

<html>
<head>
  <title>first test</title>
  <meta http-equiv="content-type" content="text/html; charset=None">
  <style type="text/css">
td.linenos { background-color: #f0f0f0; padding-right: 10px; }
span.lineno { background-color: #f0f0f0; padding: 0 5px 0 5px; }
pre { line-height: 125%; }
body .hll { background-color: #222222 }
body  { background: #000000; color: #cccccc }
body .c { color: #000080 } /* Comment */
body .err { color: #cccccc; border: 1px solid #FF0000 } /* Error */
body .esc { color: #cccccc } /* Escape */
body .g { color: #cccccc } /* Generic */
body .k { color: #cdcd00 } /* Keyword */
body .l { color: #cccccc } /* Literal */
body .n { color: #cccccc } /* Name */
body .o { color: #3399cc } /* Operator */
body .x { color: #cccccc } /* Other */
body .p { color: #cccccc } /* Punctuation */
body .ch { color: #000080 } /* Comment.Hashbang */
body .cm { color: #000080 } /* Comment.Multiline */
body .cp { color: #000080 } /* Comment.Preproc */
body .cpf { color: #000080 } /* Comment.PreprocFile */
body .c1 { color: #000080 } /* Comment.Single */
body .cs { color: #cd0000; font-weight: bold } /* Comment.Special */
body .gd { color: #cd0000 } /* Generic.Deleted */
body .ge { color: #cccccc; font-style: italic } /* Generic.Emph */
body .gr { color: #FF0000 } /* Generic.Error */
body .gh { color: #000080; font-weight: bold } /* Generic.Heading */
body .gi { color: #00cd00 } /* Generic.Inserted */
body .go { color: #888888 } /* Generic.Output */
body .gp { color: #000080; font-weight: bold } /* Generic.Prompt */
body .gs { color: #cccccc; font-weight: bold } /* Generic.Strong */
body .gu { color: #800080; font-weight: bold } /* Generic.Subheading */
body .gt { color: #0044DD } /* Generic.Traceback */
body .kc { color: #cdcd00 } /* Keyword.Constant */
body .kd { color: #00cd00 } /* Keyword.Declaration */
body .kn { color: #cd00cd } /* Keyword.Namespace */
body .kp { color: #cdcd00 } /* Keyword.Pseudo */
body .kr { color: #cdcd00 } /* Keyword.Reserved */
body .kt { color: #00cd00 } /* Keyword.Type */
body .ld { color: #cccccc } /* Literal.Date */
body .m { color: #cd00cd } /* Literal.Number */
body .s { color: #cd0000 } /* Literal.String */
body .na { color: #cccccc } /* Name.Attribute */
body .nb { color: #cd00cd } /* Name.Builtin */
body .nc { color: #00cdcd } /* Name.Class */
body .no { color: #cccccc } /* Name.Constant */
body .nd { color: #cccccc } /* Name.Decorator */
body .ni { color: #cccccc } /* Name.Entity */
body .ne { color: #666699; font-weight: bold } /* Name.Exception */
body .nf { color: #cccccc } /* Name.Function */
body .nl { color: #cccccc } /* Name.Label */
body .nn { color: #cccccc } /* Name.Namespace */
body .nx { color: #cccccc } /* Name.Other */
body .py { color: #cccccc } /* Name.Property */
body .nt { color: #cccccc } /* Name.Tag */
body .nv { color: #00cdcd } /* Name.Variable */
body .ow { color: #cdcd00 } /* Operator.Word */
body .w { color: #cccccc } /* Text.Whitespace */
body .mb { color: #cd00cd } /* Literal.Number.Bin */
body .mf { color: #cd00cd } /* Literal.Number.Float */
body .mh { color: #cd00cd } /* Literal.Number.Hex */
body .mi { color: #cd00cd } /* Literal.Number.Integer */
body .mo { color: #cd00cd } /* Literal.Number.Oct */
body .sa { color: #cd0000 } /* Literal.String.Affix */
body .sb { color: #cd0000 } /* Literal.String.Backtick */
body .sc { color: #cd0000 } /* Literal.String.Char */
body .dl { color: #cd0000 } /* Literal.String.Delimiter */
body .sd { color: #cd0000 } /* Literal.String.Doc */
body .s2 { color: #cd0000 } /* Literal.String.Double */
body .se { color: #cd0000 } /* Literal.String.Escape */
body .sh { color: #cd0000 } /* Literal.String.Heredoc */
body .si { color: #cd0000 } /* Literal.String.Interpol */
body .sx { color: #cd0000 } /* Literal.String.Other */
body .sr { color: #cd0000 } /* Literal.String.Regex */
body .s1 { color: #cd0000 } /* Literal.String.Single */
body .ss { color: #cd0000 } /* Literal.String.Symbol */
body .bp { color: #cd00cd } /* Name.Builtin.Pseudo */
body .fm { color: #cccccc } /* Name.Function.Magic */
body .vc { color: #00cdcd } /* Name.Variable.Class */
body .vg { color: #00cdcd } /* Name.Variable.Global */
body .vi { color: #00cdcd } /* Name.Variable.Instance */
body .vm { color: #00cdcd } /* Name.Variable.Magic */
body .il { color: #cd00cd } /* Literal.Number.Integer.Long */

  </style>
</head>
<body>
<h2>first test</h2>

<div class="highlight"><pre><span></span><span class="n">python</span> <span class="n">asdasdas</span> <span class="n">manasdnasd</span>
</pre></div>
</body>
</html>

使用超鏈接

處理實例之間的關系是WEB API中比較麻煩的事情,我們可以選擇以下來表示關系:

  • 使用主鍵
  • 使用超鏈接
  • 使用相關項的slug field
  • 使用相關項的默認文本信息
  • 將子項顯示在母項中
  • 其他自定義表現方式

REST框架支持所有這些樣式,並且可以跨前向或反向關系應用它們,或將它們應用於諸如通用外鍵的自定義管理器。
現在我們在實例之間用超鏈接形式,我們需要修改我們的serializers,用HyperlinkedModelSerializer代替ModelSerializer, 修改后將有如下不同:

  • HyperlinkedModelSerializer不會自動包含pk field
  • HyperlinkedModelSerializer會自動包括url field
  • 關系使用的是HyperlinkedRelatedField而不是PrimaryKeyRelatedField
 class SnippetSerializer(serializers.HyperlinkedModelSerializer):
        owner = serializers.ReadOnlyField(source='owner.username')
        highlight = serializers.HyperlinkedIdentityField(view_name='snippet-highlight', format='html')

        class Meta:
            model = Snippet
            fields = ('url', 'highlight', 'owner',
                      'title', 'code', 'linenos', 'language', 'style')


    class UserSerializer(serializers.HyperlinkedModelSerializer):
        snippets = serializers.HyperlinkedRelatedField(many=True, view_name='snippet-detail')

        class Meta:
            model = User
            fields = ('url', 'username', 'snippets')

注意, 我們同時添加了 "highlight" field, 它與url field使用的一樣, 是HyperlinkedRelatedField, 但指向的是snippet-highlight url而不是snippet-detail url
由於我們在url中包含了格式信息, 我們使用format='html'參數為highlight指定.html后綴.

確保我們的url有name

如果我們要有一個超鏈接的API,我們需要確保我們命名我們的URL模式。讓我們來看看我們需要命名的網址格式。

  • 我們的API根指向user-list and snippet-list
  • 我們的snippet序列化包含一個snippet-highlight字段
  • 我們的user序列化包含一個引用snippet-detail的字段
  • Our snippet and user serializers include 'url' fields that by default will refer to '{model_name}-detail', which in this case will be 'snippet-detail' and 'user-detail'
    中文(簡體)
    我們的代碼段和用戶序列化程序包括url字段,默認情況下將引用{model_name} -detail,在這種情況下,它將是snippet-detailuser-detail
urlpatterns = [
    url(r'^snippets/$', views.SnippetList.as_view(),name="snippet-list"),
    url(r'^snippets/(?P<pk>[0-9]+)/$', views.SnippetDetail.as_view(),name="snippet-detail"),
    url(r'^users/$', views.UserList.as_view(),name='user-list',name='user-detail'),
    url(r'^users/(?P<pk>[0-9]+)/$', views.UserDetail.as_view()),
    url(r'^snippets/(?P<pk>[0-9]+)/highlight/$', views.SnippetHighlight.as_view(),name='snippet-highlight'),
]

添加分頁

用戶和代碼片段的列表視圖最終可能返回相當多的實例,因此,我們希望確保分頁結果,並允許API客戶端逐步瀏覽每個單獨的頁面。
我們通過修改settings.py ,添加如下代碼

REST_FRAMEWORK = {
    'PAGE_SIZE': 10
}

請注意,REST框架中的設置都命名為單個字典設置,名為“REST_FRAMEWORK”,這有助於保持它們與其他項目設置分離


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM