這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。
本文為《爬著學Python》系列第十二篇文章。
本來打算周更的,鴿了好幾個星期。原因一個是物件導向那篇實在有點難寫,另一個是我去弄了個Web項目-Qotes。之前跟著書和實驗樓的線上課試過幾次Flask,這次是真正的實戰。
體驗還算不錯。
於是本專題說好了講爬蟲的,結果第一個大實踐內容是web項目實戰。本文就算在介紹Flask架構之前先給個執行個體,以後根據此執行個體展開講細節吧。
Qotes開發初衷
再貼一下連結Qotes。
其實項目的開發初衷在網站中寫了。我本來在簡書寫這個專題是為了整理知識的,因為簡書的Markdown編輯器很好用。但是我後來發現一個問題,簡書採取了筆記類應用的文件管理方式,就是一個筆記簿裡面可以加一篇篇的筆記,但是筆記裡面不能再加筆記,也不能在筆記簿裡面再建立筆記簿。這給我帶來了很大的困擾
我解釋一下我困擾的地方在哪裡。
- 可以看到我的文集裡面小說分了本文和設定兩個部分,要是可以把這兩個文集放在一個檔案夾“小說”中不就和上面的其他文集邏輯一致了嗎,但是這做不到,除非我把小說設定的一系列文章混在小說本文文集的頭或者尾部。
- 文集內部也會存在問題。比如說“日記本”裡面是我的一些雜亂的零碎的隨時筆記。但是單一的結構讓我很難將這些筆記進行整理。比如《mongodb tips》這裡面其實可以分好幾篇文章,但是如果分開來,那麼又和該文集中的其他文章邏輯不一致了。
總結起來就是:1文集不能包含2文章不能包含。
我後來想過別的方案。最靠譜的是這個:用Typora在Github上寫學習筆記。
但是總覺得有些大材小用了,而且Github開啟的速度不太理想(大多數時候正常,有時要2s左右,很難受),更大問題在於Github的private repository需要收費,收費就算了還不方便。功能更符合這個應用情境的GitHub pages又被牆,體驗實在算不上好。於是還是決定用簡書。於是還是有些不舒服。
後來覺得還是自己寫一個吧。於是我就試著寫了。
Qotes的特點
卡片
首先要解決的是文章和文集的包含問題。最後我的解決方案是這樣的:
- 所有的筆記都是卡片形式存在
- 所有卡片在資料庫中地位是同樣的,儲存在同一個collection中
- 卡片可以互相包含,每個卡片不一定有子卡片,但一定會有父結點
- 如果父節點是使用者,那麼該卡片就可視為卡片集
- 當然,每個卡片都可以看成是某卡片集的子卡片集
總結起來就是,每個卡片既是卡片又是卡片集。
在資料庫中等價就好像是樹節點在記憶體中散布但是通過連結來形成樹結構,唯一的不同是root根節點有的時候是使用者,而且樹結構一般連結記錄子節點資訊,但是本執行個體中卡片通過父節點資訊完成結構的連結。這樣做的原因是:
- 為了方便查詢,給相關欄位設定索引以後,查詢速度倒比根據某個節點的子節點表中的id一個個查詢來得更快(理論上)
- 為了簡化IO操作。每個節點只能有一個父節點但是可以有許多個子節點。如果記錄子節點資訊要對列表進行操作,而且卡片所屬關係的時候會涉及新舊父節點兩個卡片,當前邏輯下只需要把該卡片的父節點資訊改掉就可以。
這樣做也帶來一些問題。比如說一個卡片不能屬於多個卡片,但是我所參考的傳統樹結構也是不支援一個子節點有多個父節點的。我後來開發過程中想了下,整體思路上其實和Linux的檔案系統一切皆檔案的設計有些類似,但是實現方式上有一些區別。
更多的實現細節在以後的文章中分析吧。
Markdown
另一個特點就是Markdown了。首先語類上基本上要比簡書全,但是不如GFM。當然原則是保持Markdown的精神,用純文字快速構建文檔結構。因此卡片的標題直接就是卡片內容中的<h1></h1>亦即# title,二級標題h2和## subtitle作為卡片內容概要。這些基本內容作為卡片預覽時候的資訊。
比如說這個卡片編輯的時候不用寫標題, 標題和概要會自動產生。
說起來簡書的文章需要標題,文章裡面又可以用<h1></h1>其實困擾了我很久。如果不寫,文章內容沒有一級標題直接用二級標題是不符合Markdown規範的,但是如果寫了,從網頁來看文章有兩個標題其實是不符合HTML規範的。在markdown檔案中不存在這個問題在於檔案名稱並不算在檔案內容中,所以markdown檔案可以完全符合markdown規範填寫,給且僅給一個一級標題。
您也別笑我迂腐,HTML規範改到HTML5以後強調了HTML只負責內容分級,把布局和格式交給CSS和JS來做。其實就是這種精神,而Markdown和HTML一樣是標記語言,老前輩都在學習進步,見賢思齊是應該的。
另外值得說的就是首頁的快速編輯模式。
右邊的這個是一個編輯器。是的,除了輸入內容和提交按鈕什麼都沒有,即時輸入,即時轉化成HTML預覽,和Typora類似。這個其實也是Markdown的精神,簡化輸入的同時完成格式。
技術選型
後端,為了快速開發,選擇了Flask。為了方便迭代,資料庫選擇了MongoDB。當然,還有別的原因,比如我比較熟悉Flask,我想嘗試一下NoSQL。
前端沒什麼好說的,Bootstrap簡直是我的救星。中間學了半天Javascript和jQuery寫了幾個簡單的互動。兩種Markdown編輯器都是從Github找的開源編輯器,說實話找這兩個編輯器的時間比我自己寫前面提到的幾個指令碼的時間要長。
其實我有考慮在RESTful API寫完,把前端改成AJAX以後,UI最佳化一下,可能會用Golang重新寫一遍這個項目。就目前來說Flask完全夠支援業務,雖然沒做壓力測試,但是我設定的最大同時串連數才1000,不出編碼上的太大的意外肯定是足以應付的。這不是我對自己的技術自信,是MongoDB實在太強大了。想重構代碼不過就是我想試一試Golang的體驗。
其實選MongoDB是我心血來潮一時之間的決定。Flask的MongoDB外部庫ODM沒一個好用的,真是心累,可能是我被SQLAlchemy和flask-sqlalchemy慣壞了吧。總之我後來直接用pymongo自己寫簡易對應了,代碼可能不太好看但是效果還可以,中間不斷改的時候發現可迭代性非常理想。在這裡誇一下pymongo的文檔,簡直是模範,太羨慕了,是我見過的除了PythonHOWTOs以外體驗最好的文檔。我現在回來看,如果用MySQL並不會更適合我的資料結構設計不說,開發可能還是會更麻煩的。
目前的一些坑
private card還沒做。這是我目前最緊急的TODO。
由於備案的關係,我索性關閉了使用者之間交流資訊的介面來規避風險,設計上是一般使用者只能建立自己的卡片,只能瀏覽自己的以及我的卡片。這樣基本算最大程度上利用個人部落格能做的業務了。使用者可以把它當小工具,但是就備案性質來說它還是個人部落格,沒有越界。
關注功能也索性直接擱置了,評論功能是從一開始就沒打算做。別人的筆記,哪怕公開的,但就別BB了。當然,如果想體驗比較完整的功能也可以發郵件給我,郵箱去網站找吧,貼在這裡怕被爬到時候一堆垃圾郵件。簡書的反爬實在太雞肋了,限制數量的做法未免有點自欺欺人。
大寫加粗的移動端體驗不佳。這個其實對我來說不算太大的問題。我從來沒用過簡書app編輯文章,原因可能是SE的螢幕太小了。而且作為幾年的WP使用者,我已經習慣不用手機完成電話、簡訊和看新聞、天氣以外的功能了(我真的不是在黑windowphone)。電腦完成工作更方便,手機碼不了代碼,My Phone上除了簡書裝了不怎麼用,OneNote也是不常用的。以前用WP還會用,iOS端OneNote簡直蠢,還不如開啟電腦操作。
但是我仔細一思索,對於我不是問題但是對於其他人不一定適用。雖然Bootstrap對移動端的顯示已經儘力了,但是我寫的主要的互動方式拖拽在手機瀏覽器上真的很難有用武之地,只能希望什麼時候我能像個勞模還會樂意做個app吧。
於是這篇文章其實和Flask關係不大
不是這樣的。這篇文章只是先介紹一下實踐項目。中間還是有非常多的經驗值得交流的。Flask和MongoDB的協同工作就很值得講,應用部署方面各種坑也是值得分享的。比如說阿里從今年8月開始封鎖ECS出25連接埠就浪費了我很多時間來排查問題。比如centos用yum裝supervisor是有多麼難受,比如flask-wtform WTForms和flask-bootstrap quick_form到底該如何取捨。至於光頭哥Flask Web Development那本書裡面的各種時代坑就不談了。
連結
- Qotes
- 其他等更新技術相關文章的時候根據不同內容再寫吧。