Django編寫RESTful API(五):添加超連結提高模型間的關聯性,djangorestful
前言
在第四篇中,加入了使用者模型,以及相關的認證和許可權的功能。但是我們在使用的時候,會發現在訪問http://127.0.0.1:8000/users/時看到的使用者列表,不能夠直接點擊某個連結然後查看其詳情,也就是不能跳轉到http://127.0.0.1:8000/users/2這樣的連結,查看Snippet列表的時候也是如此。而且User和Snippet也沒相關的連結進行相互之間的跳轉。這些就很影響使用者體驗了,每次都需要重新輸入URL才可以訪問別的內容。這就是這篇文章主要解決的問題。
另外,上一篇文章說的能使程式碼片段高亮的HTML代碼,也會在本文中看到其使用。
為API建立根URL
根URL也就是訪問根路徑,就是http://127.0.0.1:8000/,要讓這個頁面能顯示並訪問所有的模型,也就是本項目的snippets和users。所以在views.py中肯定要多增加一個內容作為根URL(也就是首頁)的視圖,在這裡我們採用基於函數的視圖,編輯snippets/views.py並添加下面的內容:
from rest_framework.decorators import api_viewfrom rest_framework.response import Responsefrom 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) })
關於裝飾器在之前的文章已經講解過了,這裡的新知識是reverse,這是rest_framework的reverse而不是Django內建的那個,但是使用習慣類似,它會根據參數返回一個超連結,看到'user-list'和'snippet-list'基本就和Django內建的reverse一樣的道理,就是根據路由匹配模式的命名來產生超連結,所以等下需要編輯snippets/urls.py設定一下name參數。
然後Response的參數是一個字典,這個其實也和Django開發一樣,這個字典的鍵和值會傳到前端模板然後經過模板引擎渲染,只不過這裡的前端模板django-rest-framework已經幫我們做好了,只需把值傳遞過去就OK啦。
建立跳轉至查看高亮程式碼片段的URL
現在我們的API還不能查看高亮程式碼片段,所以需要添加一個連結進行跳轉。
回到上一篇文章裡面的snippets/models.py,我們為Snippet模型添加了highlighted欄位,並且使用save方法,使得儲存資料時產生能使程式碼片段高亮的HTML代碼,也就是下面這段代碼:
def save(self, *args, **kwargs): """ 使用pygments庫來產生能使代碼高亮的HTML代碼 """ lexer = get_lexer_by_name(self.language) linenos = self.linenos and 'table' or False options = self.title and {'title': self.title} or {} formatter = HtmlFormatter(style=self.style, linenos=linenos, full=True, **options) self.highlighted = highlight(self.code, lexer, formatter) super(Snippet, self).save(*args, **kwargs)
所以每次儲存資料時都會自動更新產生新的HTML代碼。
現在我們要做的就是使用API的時候,每個snippet下面除了id、title、owner等這些本來就有的,還要加一個超連結,點選連結就能查看高亮程式碼片段的頁面,所以需要為這個新頁面再建立一個視圖,編輯snippet/views.py,添加代碼:
from rest_framework import renderersclass 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)
這個代碼高亮是為了在瀏覽器上使用API時查看的,所以返回json格式的資料就沒有什麼意思了,所以這裡限定為只用HTML方式呈現。
REST framework為我們提供了兩種方式來呈現HTML,一種是使用已有的模板(我們平時開發Django更常用的那種方式),另一種就是使用已經構建好的HTML代碼。在這裡我們會使用第二種方法,因為剛才已經說了每次儲存資料時都會自動更新產生新的HTML代碼,而這個由pygments產生的程式碼就儲存在Snippet下的highlighted,所以有瀏覽器渲染並呈現highlighted下的HTML代碼就行了。因此有:
renderer_classes = (renderers.StaticHTMLRenderer,)
另外我們還注意到這裡使用了get方法,其他的視圖類不用這個方法因為他們返回的是整個執行個體對象,而我們的高亮程式碼片段頁面只需要這個執行個體對象的一個屬性,也就是snippet.highlighted。REST framework提供的通用視圖類並沒有提供直接返回一個執行個體的某個屬性的方法,所以這裡需要我們自己寫一個get方法來指定返回的屬性。
完成了根視圖以及高亮程式碼片段視圖的設計,要調用到它們的話,接下來自然要為其設計URL了。編輯snippets/urls.py,添加下面兩個url模式:
url(r'^$', views.api_root),url(r'^snippets/(?P<pk>[0-9]+)/highlight/$', views.SnippetHighlight.as_view()),
用超連結關聯API
到目前,User和Snippet在瀏覽時還不能相互之間進行跳轉,比如我們訪問一個使用者的詳情頁時,單個User下的snippets會顯示此使用者建立的所有snippet,但是只顯示了id值,可讀性不好並且不能跳轉,光看到個數字其實意義不大。我們希望實現的是把這些id值換成相應的snippet的超連結,同時希望在查看使用者列表的時候每個使用者下面有個超連結能直接進入該使用者詳情頁;同樣的,在每個snippet下有個URL指向其建立者的詳情頁面。
說了那麼多,我們想要的就是用超連結來關聯API,用來代替之前簡單粗暴的使用外鍵以及id值來表示。
由此,在序列化器中引出一個新的HyperlinkedModelSerializer類來代替之前的ModelSerializer類。這個新的類有以下的不同點: 1. 預設不包含id值 2. 通過HyperlinkedIdentityField這個欄位會為序列化器產生一個url屬性 3. 關聯API使用的是HyperlinkedRelatedField而不是PrimaryKeyRelatedField(超連結代替外鍵)
這麼一看,這個新的HyperlinkedModelSerializer類好像可以實現上面我們所說的那些功能,確實是這樣的。編輯snippet/serializers.py,改進序列化器:
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', 'id', 'highlight', 'owner', 'title', 'code', 'linenos', 'language', 'style')class UserSerializer(serializers.HyperlinkedModelSerializer): snippets = serializers.HyperlinkedRelatedField(many=True, view_name='snippet-detail', read_only=True) class Meta: model = User fields = ('url', 'id', 'username', 'snippets')
可以看到兩個Meta類都多了一個'url',這就是HyperlinkedRelatedField產生的,並且看到參數中又有一個命名空間,乍一看好像有點像reverse產生URL的套路啊?
額...內部的實現真的是有用到reverse,通過查看源碼就能追蹤到那裡,首先進入HyperlinkedRelatedField源碼,發現裡面只有一個__init__構造方法,那就繼續進入它的父類HyperlinkedRelatedField的源碼,發現裡面有這麼一個函數:
def get_url(self, obj, view_name, request, format): """ Given an object, return the URL that hyperlinks to the object. May raise a `NoReverseMatch` if the `view_name` and `lookup_field` attributes are not configured to correctly match the URL conf. """ # Unsaved objects will not yet have a valid URL. if hasattr(obj, 'pk') and obj.pk in (None, ''): return None lookup_value = getattr(obj, self.lookup_field) kwargs = {self.lookup_url_kwarg: lookup_value} return self.reverse(view_name, kwargs=kwargs, request=request, format=format)
發現其實這個方法最後用的還是reverse方法,並且將產生的url作為返回的資料。所以繼續往下看這個類的代碼,會發現還有個to_representation方法裡面有這麼幾行代碼:
try: url = self.get_url(value, self.view_name, request, format)except NoReverseMatch: ...if url is None: return Nonereturn Hyperlink(url, value)
這個過程下來我們大概能知道HyperlinkedIdentityField也能幫我們產生相應url,並且是一個超連結的形式。
另外注意到我們想要讓代碼高亮API只用HTML呈現,所以還設定了format='html'參數限定了尾碼。
為各個URL模式命名
上面的程式為了產生url又是reverse又是HyperlinkedIdentityField的,其中的參數都用到了命名,所以我們想要產生正確的url就要給各個URL模式根據上面的參數正確命名。
編輯 snippets/urls.py,添加命名:
from django.conf.urls import urlfrom snippets import viewsfrom rest_framework.urlpatterns import format_suffix_patternsurlpatterns = [ 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'), url(r'^users/(?P<pk>[0-9]+)/$',views.UserDetail.as_view(),name='user-detail'), url(r'^$',views.api_root), url(r'^snippets/(?P<pk>[0-9]+)/highlighted/$',views.SnippetHighlight.as_view(),name='snippet-highlighted'),]urlpatterns = format_suffix_patterns(urlpatterns)
添加分頁
如果我們建立的使用者和程式碼片段都很多的話,再查看列表是全部顯示在一頁有時候可能有點難看,所以這裡需要添加一個分頁設定,很簡單,只需要在項目的settings.py中添加一個配置字典:
REST_FRAMEWORK = { 'PAGE_SIZE': 10}
這樣就可以實現分頁了
OK,現在我們的項目通過使用各種超連結來關聯,API之間已經可以方便的進行花式跳轉了。下面看一下實際的效果:
首先是API根頁面:
裡面的連結都是可以點擊的,下面是單個Snippet詳情頁:
最後是代碼高亮頁面,其實就是highlighted中的HTML代碼被瀏覽器渲染後的樣子:
想要這個頁面的源碼的話除了在瀏覽器右鍵開啟,還可以直接SnippetSerializer下面的Meta類中,直接為field再加一個'highlighted',然後瀏覽的時候就會發現Snippet詳情頁多了個highlighted鍵,它的值就是很長很長的一坨HTML代碼,這代碼產生的頁面其實就是上面那個圖的樣子。
OK,關於添加超連結提高模型間的關聯性的介紹就先到這了。下一篇文章會介紹視圖集和路由相關的內容。