前言
在第四篇中,加入了用戶模型,以及相關的認證和權限的功能。但是我們在使用的時候,會發現在訪問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_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) })
關於裝飾器在之前的文章已經講解過了,這里的新知識是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 renderers 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)
這個代碼高亮是為了在瀏覽器上使用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 None return Hyperlink(url, value)
這個過程下來我們大概能知道HyperlinkedIdentityField也能幫我們生成相應url,並且是一個超鏈接的形式。
另外注意到我們想要讓代碼高亮API只用HTML呈現,所以還設置了format='html'參數限定了后綴。
為各個URL模式命名
上面的程序為了生成url又是reverse又是HyperlinkedIdentityField的,其中的參數都用到了命名,所以我們想要生成正確的url就要給各個URL模式根據上面的參數正確命名。
編輯 snippets/urls.py,添加命名:
from django.conf.urls import url from snippets import views from rest_framework.urlpatterns import format_suffix_patterns 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'), 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,關於添加超鏈接提高模型間的關聯性的介紹就先到這了。下一篇文章會介紹視圖集和路由相關的內容。
本文地址:http://www.cnblogs.com/zivwong/p/7461764.html
作者博客:ziv
歡迎轉載,請在明顯位置給出出處及鏈接