整合營銷服務商

          電腦端+手機端+微信端=數據同步管理

          免費咨詢熱線:

          在Django中使用Markdown

          為開發人員,我們依賴于靜態分析工具來檢查、lint(分析)和轉換我們的代碼。我們使用這些工具來幫助我們提高生產效率并生成更好的代碼。然而,當我們使用markdown編寫內容時,可用的工具就很少。

          在本文中,我們將介紹如何開發一個Markdown擴展來解決在使用Markdown管理Django站點中的內容時遇到的挑戰。

          你認為他們有linter嗎?

          照片來自Pexels,由mali maeder拍攝

          問題

          像每個網站一樣,我們在主頁、FAQ部分和“關于”頁面等地方都有不同類型的(大部分)靜態內容。很長一段時間以來,我們都是在Django模板中直接管理這些內容的。

          當我們最終決定是時候將這些內容從模板轉移到數據庫中時,我們認為最好使用Markdown。從Markdown生成HTML更安全,它提供了一定程度的控制和一致性,并且對于非技術用戶來說更容易處理。隨著我們轉移過程的進展,我們注意到我們遺漏了一些東西:

          內部鏈接

          當URL更改時,鏈接到內部頁面的鏈接可能會中斷。在Django模板和視圖中,我們使用了reverseand {% url %},但是這在普通的Markdown中是不可用的。

          在不同環境之間進行復制

          絕對內部連接不能在不同環境之間進行復制。這可以使用相對鏈接來解決,不過目前沒有開箱即用的增強這一點的方法。

          無效鏈接

          無效鏈接會損害用戶體驗,并導致用戶質疑整個內容的可靠性。這并不是Markdown獨有的東西,只不過HTML模板是由對URL有一定了解的開發人員維護的。另一方面,Markdown文檔是為非技術寫作人員設計的。

          前期工作

          當我研究這個問題時,我搜索了Python linters、Markdown預處理器和擴展來幫助生成更好的Markdown。結果都不是很好。一個引人注目的方法是使用Django模板來生成Markdown文檔。

          使用Django模板預處理Markdown

          使用Django模板,你可以使用諸如url之類的模板標記來反向查詢URL名稱,并配合使用條件、變量、日期格式和所有其他Django模板特性。這種方法本質上是使用Django模板作為Markdown文檔的預處理程序。

          我個人認為這可能不是非技術作家的最佳解決方案。另外,我擔心提供對Django模板標記的訪問可能是危險的。

          使用 Markdown

          對這個問題有了更好的理解之后,我們準備在Python中更深入地研究Markdown。

          將Markdown轉換為HTML

          要在Python中開始使用Markdown,我們先安裝markdown包:

          接著,創建一個Markdown對象并使用其函數將一些Markdown轉換成HTML:

          你現在可以在你的模板中使用這個HTML代碼片段。

          使用Markdown擴展

          基本的Markdown處理器提供了生成HTML內容的基本要素。對于更“新奇”的選項,Python markdown包包含了一些內置擴展。一個流行的擴展是“extra”擴展,除了其他東西之外,它增加了對隔離代碼塊的支持:

          為了使用我們獨特的Django功能擴展Markdown,我們將開發自己的擴展。

          創建一個Markdown擴展來處理內聯鏈接

          如果你查看源代碼,你將看到要將markdown轉換為HTML, Markdown會使用多種不同的處理器。一種類型的處理器是內聯處理器。內聯處理器會匹配特定的內聯模式,如鏈接、反引號、粗體文本和帶下劃線的文本,并將它們轉換為HTML。

          我們的Markdown擴展的主要目的是驗證和轉換鏈接。因此,我們最感興趣的內聯處理器是LinkInlineProcessor。這個處理器以[Haki的網站](https://hakibenito.com)的形式獲取markdown ,解析它并返回一個包含鏈接和文本的元組。

          為了擴展該功能,我們擴展了LinkInlineProcessor并創建了一個Markdown.Extension, 我們用它來處理鏈接:

          我們來將這段代碼分解一下::

          • DjangoUrlExtension擴展注冊了一個名為DjangoLinkInlineProcessor的內聯鏈接處理器。這個處理器將取代任何其他現有的鏈接處理器。

          • 內聯處理器DjangoLinkInlineProcessor擴展了內置的LinkInlineProcessor,并在它處理的每個鏈接上調用clean_link函數。

          • clean_link函數接收一個鏈接和一個域名,并返回一個轉換后的鏈接。這就是我們要插入我們的實現的地方。

          如何獲得網站域名


          要識別到你自己網站的鏈接,你必須知道你的網站的域名。如果你正在使用Django的sites框架,那么你可以使用它來獲取當前域名。


          我沒有把它包含在我的實現中,因為我們沒有使用sites框架。相反,我們在Django設置中設置了一個變量。


          獲取當前域名的另一種方法是使用HttpRequest對象。如果內容只在你自己的站點中被編輯,你可以嘗試從請求對象中插入站點域名。這可能需要對你的實現進行一些更改。

          要使用該擴展,請在初始化一個新的Markdown實例時添加它:

          太好了,這個擴展已經被使用了,我們準備進入有趣的部分了!

          驗證和轉換Django鏈接

          既然我們得到了在所有鏈接上調用clean_link的擴展,那我們可以來實現我們的驗證和轉換邏輯。

          驗證mailto鏈接

          要開始工作,我們將從一個簡單的驗證開始。mailto鏈接對于使用預定義的收件人地址、主題甚至消息正文打開用戶的電子郵件客戶端非常有用。

          一個常見的mailto鏈接是這樣的:

          這個鏈接將打開你的電子郵件客戶端,并設置成撰寫一封主題行為“我需要幫助!”的新電子郵件給“support@service.com”。

          mailto鏈接不一定非要包含電子郵件地址。如果你看一看這篇文章底部的“分享”按鈕,你會發現像這樣的一個mailto鏈接:

          這個mailto鏈接沒有包含收件人,僅包含了主題行和消息正文。

          既然我們已經很好地理解了mailto鏈接是什么樣子的,我們就可以向clean_link函數添加第一個驗證:

          為了驗證mailto鏈接,我們向clean_link中添加了以下代碼:

          • 檢查鏈接是否以mailto:開頭,以識別相關鏈接。

          • 使用正則表達式將鏈接分割到它的組件。

          • 從mailto鏈接中刪除實際的電子郵件地址,并使用Django的EmailValidator驗證它。

          注意,我們還添加了一種名為InvalidMarkdown的新異常類型。我們定義了自己的自定義異常類型,以將它與markdown本身所引發的其他錯誤區分開來。

          自定義錯誤類

          我曾經寫過關于自定義錯誤類的文章,為什么它們是有用的,以及你什么時候應該使用它們。

          在我們繼續之前,讓我們添加一些測試,看看它的實際效果:

          太棒了!按預期的運行了。

          處理內部和外部鏈接

          既然我們已經了解了mailto鏈接,我們也可以處理其他類型的鏈接:

          外部鏈接

          • 我們的Django應用程序外部的鏈接。

          • 必須包含一個頁面跳轉協議(scheme):http或https。

          • 理想情況下,我們還希望確保這些鏈接沒有被破壞,但我們現在不會這樣做。

          內部鏈接

          • 到我們的Django應用程序中的頁面的鏈接。

          • 鏈接必須是相對的:這將允許我們在不同環境之間移動內容。

          • 使用Django的URL名稱而不是一個URL路徑:這將允許我們安全地來回移動視圖,而不必擔心markdown內容中的失效鏈接。

          • 鏈接可能包含查詢參數(?)和片段(#)。

          SEO

          從SEO的角度來看,公共URL不應該改變。當他們這樣做的時候,你應該使用重定向正確地處理它,否則你可能會受到搜索引擎的懲罰。

          有了這個需求列表,我們就可以開始工作了。

          解析URL名稱

          要鏈接到內部頁面,我們希望編寫者提供一個URL名稱,而不是URL路徑。例如,假設我們有這個視圖:

          這個頁面的URL路徑是https://example.com/, URL名稱是home。我們想要在我們的markdown鏈接中使用這個URL名稱home,就像這樣:

          這將渲染到:

          我們還想支持查詢參數和散列:

          這將渲染到以下HTML:

          在使用URL名稱時,如果我們更改了URL路徑,內容中的鏈接將不會被破壞。要檢查作者提供的href是否是一個有效的url_name,我們可以嘗試reverse它:

          URL名稱“home”指向URL路徑“/”。當沒有匹配項時,將會引發一個異常:

          在我們繼續之前,當URL名稱包含查詢參數或散列時,會發生什么:

          這是有意義的,因為查詢參數和散列不是URL名稱的一部分。

          要使用reverse并支持查詢參數和散列,我們首先需要清除值。然后,檢查它是一個有效的URL名稱,并返回包含查詢參數和散列的URL路徑,如果提供了的話:

          這個代碼段使用一個正則表達式來以?或#的出現對href進行分割,并返回各部分。

          請確保它可以工作:

          太了不起了!作者們現在可以在Markdown中使用URL名稱了。它們還可以包括要添加到該URL的查詢參數和片段。

          處理外部鏈接

          要正確處理外部鏈接,我們需要檢查兩件事:

          1.外部鏈接總是提供一個跳轉協議,http:或者https:。

          2.阻止到我們自己網站的絕對鏈接。內部鏈接應該使用URL名稱。

          到目前為止,我們已經處理了URL名稱和mailto鏈接。如果我們通過了這兩個檢查,這意味著href是一個URL。讓我們從檢查鏈接是否是鏈接到我們自己的網站開始:

          函數urlparse會返回一個命名元組,該元組包含URL的不同部分。如果netloc屬性等于site_domain,那么該鏈接就確實是一個內部鏈接。

          如果URL實際上是內部的,我們就需要終止。但是,請記住,作者們不一定是技術人員,因此我們希望幫助他們,并提供一個有用的錯誤消息。我們要求該內部鏈接使用URL名稱而不是URL路徑,所以最好讓作者們知道他們提供的路徑的URL名稱。

          要獲得一個URL路徑的URL名稱,Django為我們提供了一個名為resolve的函數:

          當找到匹配項時,resolve會返回一個ResolverMatch對象,其中包含URL名稱和其他信息。當沒有找到匹配項時,它就會引發一個錯誤:

          這實際上就是Django在底層所做的工作,用來確定在一個新請求到來時執行哪個視圖函數。

          為了給作者們提供更好的錯誤信息,我們可以使用來自ResolverMatch對象的URL名稱:

          當我們識別出內部鏈接時,我們要處理兩種情況:

          • 我們沒有識別出這個URL:這個URL很可能是不正確的。請作者檢查該URL是否有錯誤。

          • 我們識別出了這個URL: 這個URL是正確的,所以就告訴作者應該使用什么URL名稱。

          我們來實際地看一下它:

          漂亮!外部鏈接被接受,內部鏈接被拒絕,并帶有一個有用的消息。

          要求跳轉協議

          我們要做的最后一件事是確保外部鏈接包含一個跳轉協議,要么是http:,要么是https:。讓我們將這最后一部分添加到函數clean_link:

          使用解析后的URL,我們可以很容易地檢查跳轉協議。讓我們確保它正在工作:

          我們向這個函數提供了一個沒有跳轉協議的鏈接,但是它運行失敗了,并顯示了一條有用的消息。太酷了!

          整合代碼

          這是clean_link函數的全部代碼:

          要了解所有這些特性的一個實際用例是什么樣子的,請看下面的內容:

          這將產生以下HTML:

          不錯!

          結論

          我們現在有一個很不錯的擴展,它可以驗證和轉換Markdown文檔中的鏈接!現在,在不同環境之間移動文檔和保持內容整潔要容易多了,最重要的是,可以保持正確和最新!

          源碼

          你可以在這個gist中找到全部源代碼。(地址:https://gist.github.com/hakib/73fccc340e855bb65f42197e298c0c7d )

          題外話

          本文中所描述的功能對我們很有用,但是你可能需要根據自己的需求對它進行調整。

          如果你需要一些想法,那么除了這個擴展之外,我們還創建了一個markdown Preprocessor,它允許作者們在markdown中使用常量。例如,我們定義了一個名為SUPPORT_EMAIL的常量,我們像這樣使用它:

          該預處理程序將用我們定義的文本替換字符串$SUPPORT_EMAIL,然后才渲染Markdown。

          英文原文:https://hakibenita.com/django-markdown
          譯者:Nothing

          TML是一種用來描述網頁的標記性語言。學習HTML可能并不難,主要是要記一些HTML標簽和標簽代表的含義。下面PHP程序員雷雪松根據使用的情況,整理出平時常用的HTML標簽。

          HTML基礎之HTML常用標簽-PHP程序員雷雪松的博客

          1、最基本的HTML結構

          <!--<!DOCTYPE> 是HTML5聲明,<!DOCTYPE> 必須是 HTML 文檔的第一行,位于 <html> 標簽之前。<!DOCTYPE>是指示 web 瀏覽器關于頁面使用哪個 HTML 版本進行編寫的指令。-->

          <!DOCTYPE html>

          <html>

          <!-- head標簽是所有頭部元素的容器。head標簽內的元素可包含腳本、樣式表和提供頁面的元信息等等。以下標簽都可以添加到 head 部分:title、base、link、meta、script 以及style。頭部的內容不會顯示在瀏覽器的。 -->

          <head>

          <!-- 設置字符集,如果字符集不對,可能導致亂碼。一般建議utf-8國際編碼 -->

          <meta http-equiv="Content-Type" content="text/html; charset=gb2312或utf-8或gbk" />

          <!-- SEO相關標簽,title定義文檔的標題,百度建議一般不要超過32位,meta定義頁面關鍵詞和頁面的描述-->

          <title>網頁標題</title>

          <meta name="keywords" content="PHP程序員,技術博客,個人博客,雷雪松" />

          <meta name="description" content="PHP程序員,雷雪松(Raykaeso)的博客是一個優秀的個人技術博客。PHP程序員雷雪松的博客記錄了Linux學習,PHP開發與編程,Web前端開發,MySQL學習和教程,NoSQL數據庫教程以及個人的人生經歷和觀點。" />

          <link rel="stylesheet" type="text/css" href="main.css" />

          <script type="text/javascript" src="main.js"></script>

          </head>

          <!-- 正文部分,所有在瀏覽器上可見的內容必須寫在body標簽內部 -->

          <body>

          </body>

          </html>

          2、最常用的HTML標簽

          a、布局標簽

          div標簽定義文檔中的分區或節(division/section),可以把文檔分割為獨立的、不同的部分,主要用于布局。

          aside標簽的內容可用作文章的側欄,<span style="color: #ff0000;">html5新增標簽</span>。

          header標簽定義頁面的頭部(介紹信息),<span style="color: #ff0000;">html5新增標簽</span>。

          section標簽定義文檔中的節(section、區段)。比如章節、頁眉、頁腳或文檔中的其他部分,<span style="color: #ff0000;">html5新增標簽</span>。

          footer 標簽定義文檔或節的頁腳,通常包含文檔的作者、版權信息、使用條款鏈接、聯系信息等等,<span style="color: #ff0000;">html5新增標簽</span>。

          article標簽規定文章獨立的其他內容,比如:標題、內容、評論,<span style="color: #ff0000;">html5新增標簽</span>。

          b、文本標簽

          h1-h6標簽可定義標題

          p標簽定義段落

          b/strong標簽加粗

          em標簽來表示強調的文本,斜體

          strong標簽表示重要文本

          u標簽下劃線

          s標簽刪除線

          br標簽表示回車換行

          hr標簽表示水平線

          span標簽被用來組合文檔中的行內元素。

          blockquote標簽表示塊引用

          pre標簽可定義預格式化的文本,保持原有格式的一種標簽。

          sub標簽下標,

          sup>標簽上標

          &nbsp;表示一個空格

          &copy;表示版權符

          &lt;表示<

          &gt;表示>

          c、a標簽定義超鏈接,指定頁面間的跳轉。鏈接可以指向外部鏈接或者頁面內部id錨點,可以在當前頁面打開,新開窗口。

          <a href="指向的鏈接地址或者網址#ID名" target="_blank|_self|_top|_parent">百度</a>

          d、多媒體標簽

          img標簽主要在網頁中插入圖像,可以定義圖片替換文本、顯示寬度和高度、是否帶邊框,建議等比例設置,否則圖像會變形。

          <img src="圖片地址" alt="替換文本,圖片打不開的時候顯示" width="圖片寬度" height="高度" border="0" />

          audio標簽定義聲音,比如音樂或其他音頻流。<span style="color: #ff0000;">html5新增標簽</span>。

          <audio src="someaudio.wav">您的瀏覽器不支持 audio 標簽。</audio>

          video標簽定義視頻,比如電影片段或其他視頻流。<span style="color: #ff0000;">html5新增標簽</span>。

          <video src="movie.ogg" controls="controls">您的瀏覽器不支持 video 標簽。</video>

          e、序列化標簽

          ul和li無序列表標簽

          <ul>

          <li>HTML</li>

          <li>JS</li>

          <li>PHP</li>

          </ul>

          ol和li有序列表標簽,可以使用type屬性規定有序列表符號的類型。1 按數字有序排列,為默認值,(1、2、3、4);a 按小寫字母有序排列,(a、b、c、d);A 按字母大寫有序排列,(A、B、C、D)。i 按小寫羅馬字母有序,(i, ii, iii, iv);I 按小寫羅馬字母有序,(I, II, III, IV)。

          <ol>

          <li>HTML</li>

          <li>JS</li>

          <li>PHP</li>

          </ol>

          dl標簽定義了定義列表(definition list),dl標簽用于結合 dt(定義列表中的項目)和 dd(描述列表中的項目)。

          <dl>

          <dt>計算機</dt>

          <dd>用來計算的儀器 ... ...</dd>

          </dl>

          f、表格標簽

          table標簽和tr標簽,th標簽和td標簽,合并單元格。

          <table width="100%" height="193" border="1" cellpadding="0" cellspacing="0" bordercolor="#FF0000" bgcolor="#000000" background="">

          <tr>

          <th>標題</th>

          <th>標題</th>

          </tr>

          <tr>

          <!-- 合并橫向單元格 -->

          <td colspan="2" nowrap="nowrap">&amp;nbsp;</td>

          </tr>

          <tr>

          <td></td>

          <!-- 合并縱向單元格 -->

          <td rowspan="2"> </td>

          </tr>

          <tr>

          <td height="16"> </td>

          </tr>

          </table>

          g、表單標簽

          form標簽定義提交方式、提交地址、表單字符集以及如何對其進行編碼,需要提交的表單一定要放在form標簽內。

          <form id="form1" name="form1" method="post|get" enctype="multipart/form-data" action="提交到的地址"></form>

          input標簽用于搜集用戶信息

          <input name="userName" type="text" maxlength="5" size="100" value="asdfasdfasfd" />

          密碼,輸入的字符會被掩碼(顯示為星號或原點)

          <input name="pwd" type="password" maxlength="5" size="100" value="" />

          文件類型的表單,上傳文件時,form表單一定要設置為enctype="multipart/form-data"

          <input type="file" name="file" />

          隱藏表單

          <input type="hidden" name="country" value="China" />

          提交

          <input type="submit" name="Submit" value="提交" disabled="disabled" />

          重置

          <input type="reset" name="Submit2" value="重置" />

          radio單選

          <input name="sex" type="radio" value="1" />男

          <input name="sex" type="radio" value="2" checked="checked" />女

          checkbox多選

          <input name="skill" type="checkbox" value="1" checked="checked" />PHP

          <input name="skill" type="checkbox" value="2" />前端

          <input name="skill" type="checkbox" value="2" />數據庫

          <span style="color: #ff0000;">注:checked="checked"可以簡寫成checked</span>

          label標簽為input元素定義標注,如果您點擊label元素文本,就會觸發此input控件。

          textarea標簽,設置文本區內的可見行數和寬度

          <textarea name="content" cols="30" rows="10">大段文本輸入框</textarea>

          button標簽定義一個按鈕

          提交按鈕

          <button type="submit" value="提交">提交</button>

          重置按鈕

          <button type="reset" value="重置">重置</button>

          select標簽和option標簽下拉列表

          單選菜單列表框

          <select name="user">

          <option value="1">ray</option>

          <option value="2" selected="selected">raykaeso</option>

          </select>

          多選列表下拉框,shift加鼠標單擊,可以連續選擇多個選擇,CTRL+鼠標點擊,可以點擊多個。

          <select name="user" size="10" multiple="multiple">

          <option value="1">雷雪松</option>

          <option value="2" selected="selected">ray</option>

          <option value="3">raykaeso</option>

          </select>

          注:selected="selected"可簡寫成selected,表示選中

          3、其他HTML事項

          a、HTML標簽和屬性是不區分大小寫的,建議HTML標簽和屬性都小寫,屬性值必須用雙引號包圍。

          b、HTML標簽都是以開始標簽起始,以結束標簽終止。大部分HTML標簽都是成對出現的,稱為雙標簽,比如:p標簽、div標簽,也有的HTML標簽在開始標簽中結束的標簽,稱為單標簽,比如:hr標簽、br標簽。大多數 HTML 元素可擁有屬性,文本內容都是寫在開始標簽與結束標簽之間。

          c、HTML標簽之間盡量縮進與換行,每行代碼不要過長,方便閱讀和維護。

          d、HTML標簽使用必須符合標簽嵌套規則。禁止a標簽嵌套a標簽,p標簽嵌套div標簽。

          e、建議不使用HTML已經廢棄的或者不贊成使用的標簽,少使用table布局、iframe框架嵌套以及flash播放器。

          來源:PHP程序員雷雪松的博客 -HTML基礎之HTML常用標簽(http://www.leixuesong.cn/2045)

          引號和雙引號的使用:

          $("div td:nth-child("+iNum+"), th:nth-child("+iNum+")").addClass("on").show();
              function disp(divs){
              for(var i=0;i<divs.length;i++)
              $(document.body).append($("<div style='background:"+divs[i].style.background+";'>"+divs[i].innerHTML+"</div>"));
          }

          屬性選擇器

          使用屬性選擇器加單引號和不加單引號,程序都能正確運行,為了程序的可讀性,最好是加上單引號(個人習慣);

          $("div a[title]");

          $("div a[title='isaac']")

          $("div a[href^='pdf']") 以pdf開頭

          $("div a[href$='pdf']") 以pdf結尾

          $("div a[href*='pdf']") 包含字符串pdf

          json格式: 屬性值必須加引號, 而屬性可加可不加

          attr({json})

          $("div img").attr({src: "02.jpg", title: "紫金公寓", alt: "紫金公寓"});

          css({json})

          $("div p").css({color: "#ff0011", background: "blue", "font-size":"16px" });

          注意:帶橫線的屬性必須加引號; font-size也可寫成fontSize;

          屬性值帶單位(如px)的必須加引號;

          animate({json})

          $("#block").animate({left: "-=90px"},300); //相對左移
          $("#block").animate({
            opacity: "0.5",
            width: "80%",
            height: "100px",
            borderWidth: "5px",
            fontSize: "16px",
            marginTop: "45px",
            marginLeft: "24px"
          },2000);


          注意:border-width不被支持(即使加引號也不支持, 屬于極其特殊情況, 而font-size加引號是被支持的), parem參數必須采用馱峰式寫法, 且不帶橫線, 與CSS樣式使用DOM名稱一致。

          CSS樣式使用DOM名稱(比如"fontSize")來設置, 而非CSS名稱(比如"font-size")。


          主站蜘蛛池模板: 日韩电影一区二区三区| 国产精品视频分类一区| 日本不卡免费新一区二区三区| 精品少妇一区二区三区在线| 美女视频一区二区三区| 国产精品视频一区二区三区四| 国产精品区一区二区三| 久久精品无码一区二区三区日韩| 精品人妻系列无码一区二区三区| 国产伦精品一区二区免费| 天堂一区人妻无码| 亚洲AV综合色区无码一区爱AV| 美女免费视频一区二区| 国产Av一区二区精品久久| 精品无码人妻一区二区三区品| 久久国产精品免费一区| 国产成人AV区一区二区三| 中文字幕精品一区二区2021年 | 精品国产一区二区三区2021| 亚洲一区二区在线视频| 无人码一区二区三区视频| 极品少妇伦理一区二区| 成人H动漫精品一区二区| 91在线视频一区| 国产高清在线精品一区二区三区| 亚洲中文字幕乱码一区| 一区二区在线免费视频| 日本免费一区二区在线观看| 精品一区二区三区四区在线| 中文字幕日韩欧美一区二区三区| 久久综合一区二区无码| 天堂Av无码Av一区二区三区| 风间由美性色一区二区三区| 日本美女一区二区三区| 精品国产日韩亚洲一区在线| 成人精品一区二区三区校园激情 | 人妻AV中文字幕一区二区三区| 亚洲色无码一区二区三区 | 国模一区二区三区| 亚洲综合一区二区国产精品| 国偷自产av一区二区三区|