文章目錄
- 0.思路引導
- 1.在文中插入目錄
- 2.在頁面的任何地方插入目錄
- 3.美化標題的錨點 URL
0.思路引導
Markdown 在解析內容的同時還可以自動提取整個內容的目錄結構,本文內容將從以下幾個方面展開:
1)在文中插入目錄;
2)在頁面的任何地方插入目錄;
3)美化標題的錨點 URL。
1.在文中插入目錄
博客的 Post(文章)模型,其中 body 是我們存儲 Markdown 文本的字段:
文件位置:blog/models.py
from django.db import modelsclass Post(models.Model):# Other fields ...body = models.TextField()
再來回顧一下文章詳情頁的視圖,我們在 detail 視圖函數中將 post 的 body 字段中的 Markdown 文本解析成了 HTML 文本,然后傳遞給模板顯示
文件位置:blog/views.py
def detail(request, pk):post = get_object_or_404(Post, pk=pk)post.body = markdown.markdown(post.body,extensions=['markdown.extensions.extra','markdown.extensions.codehilite','markdown.extensions.toc',])return render(request, 'blog/detail.html', context={'post': post})
markdown.markdown() 方法把 post.body 中的 Markdown 文本解析成了 HTML 文本。同時我們還給該方法提供了一個 extensions 的額外參數。其中 markdown.extensions.toc 就是自動生成目錄的拓展。
在渲染 Markdown 文本時加入了 toc 拓展后,就可以在文中插入目錄了。方法是在書寫 Markdown 文本時,在你想生成目錄的地方插入 [TOC] 標記即可。
后臺輸入如下:
效果展示如下:
2.在頁面的任何地方插入目錄
上述方式的一個局限性就是只能通過 [TOC] 標記在文章內容中插入目錄。如果想在頁面的其它地方,比如側邊欄插入一個目錄該怎么做呢?只需要稍微改動一下解析 Markdown 文本內容的方式即可。
以detail.py中的模板標簽{% block toc %}為例,其渲染的位置位于詳情頁的側邊欄,現在想要在側邊欄中生成目錄。
{% block toc %}
...
{% endblock toc %}
需要修改的代碼如下:
文件位置:blog/views.py
def detail(request, pk):post = get_object_or_404(Post, pk=pk)md = markdown.Markdown(extensions=['markdown.extensions.extra','markdown.extensions.codehilite','markdown.extensions.toc',])post.body = md.convert(post.body)m = re.search(r'<div class="toc">\s*<ul>(.*)</ul>\s*</div>', md.toc, re.S)post.toc = m.group(1) if m is not None else ''return render(request, 'blog/detail.html', context={'post': post})
文件位置:detail.html
{% block toc %}{% if post.toc %}<div class="widget widget-content"><h3 class="widget-title">文章目錄</h3><div class="toc"><ul>{{ post.toc|safe }}</ul></div></div>{% endif %}
{% endblock toc %}
在這里,我們沒有直接用 markdown.markdown() 方法來渲染 post.body 中的內容,而是先實例化了一個 markdown.Markdown 對象 md,和 markdown.markdown() 方法一樣,也傳入了 extensions 參數。
接著我們便使用該實例的 convert 方法將 post.body 中的 Markdown 文本解析成 HTML 文本。而一旦調用該方法后,實例 md 就會多出一個 toc 屬性,這個屬性的值就是內容的目錄,我們把 md.toc 的值通過整個表達式的處理,賦給 post.toc 。
注意:為了防止文章目錄為空的情況,使用正則表達式來測試 ul 標簽中是否包裹有元素,從而來確定是否存在目錄。并使用新的模板標簽 {% if %},來對post.toc做條件判斷。
效果展示如下:
3.美化標題的錨點 URL
文章內容的標題被設置了錨點,點擊目錄中的某個標題,頁面就會跳到該文章內容中標題所在的位置,這時候瀏覽器的 URL 顯示的值可能不太美觀,比如像下面的樣子:
#1_1 就是錨點,Markdown 在設置錨點時利用的是標題的值,由于通常我們的標題都是中文,Markdown 沒法處理,所以它就忽略的標題的值,而是簡單地在后面加了個1 _1 這樣的錨點值。為了解決這一個問題,需要修改一下傳給 extentions 的參數,其具體做法如下:
文件位置:blog/views.py
from django.utils.text import slugify
from markdown.extensions.toc import TocExtensiondef detail(request, pk):post = get_object_or_404(Post, pk=pk)md = markdown.Markdown(extensions=['markdown.extensions.extra','markdown.extensions.codehilite',# 記得在頂部引入 TocExtension 和 slugifyTocExtension(slugify=slugify),])post.body = md.convert(post.body)m = re.search(r'<div class="toc">\s*<ul>(.*)</ul>\s*</div>', md.toc, re.S)post.toc = m.group(1) if m is not None else ''return render(request, 'blog/detail.html', context={'post': post})
和之前不同的是,extensions 中的 toc 拓展不再是字符串 markdown.extensions.toc ,而是 TocExtension 的實例。
TocExtension 在實例化時其 slugify 參數可以接受一個函數,這個函數將被用于處理標題的錨點值。
Markdown 內置的處理方法不能處理中文標題,所以我們使用了 django.utils.text 中的 slugify 方法,該方法可以很好地處理中文。
效果展示如下: