網絡上關於Django的內容其實已經不少了,包括本身也是從網絡上的內容一點一從零開始學習Django的,可是這部份內容都過於零散,能夠說大部分都是在講怎麼從django-admin startproject
到建立Model
,再到寫一個TODO List或者一個Blog之類的內容,對於已經瞭解Django
的人來講,並無太好渠道去了解一些別人的實踐,在開發本身的項目時,遇到的需求很難去了解其餘人會怎麼設計,固然這也是社區存在的通病,你們更多在討論語言層面的問題.
我但願能把本身平時在工做中總結出來的tips分享出來,也許在某些地方能幫助到別人,也算是本身梳理經驗,自我提升的一個過程.
既然標題是如何開發,那我就按照一個項目的生命週期來整理.前端
討論需求的事情就不談了.
一個項目需求給到以後,第一步確定不是django-admin startproject
,而是選型,換句話說,咱們得先決定要不要使用django
.它適合什麼樣的項目,不適合什麼樣的項目 java
避免websocket
Django出現的太早了,它甚至和ajax的年紀差沒幾年,因此對不少現代化的需求和功能支持都不太好,好比websocket,好比異步任務.
若是需求中有一些功能會比較依賴websocket
,那麼就建議不要選擇Django
,並非說徹底不行,可是Django Channel
和asgi
目前來講都算不上很成熟的解決方案,不得不認可在這方面django
的確是沒有優點.python
避免對性能敏感的需求
這是老生常談了,我說一下我本身遇到的性能問題都出如今哪裏(不談惡俗的abtest
).git
服務端RSA
加解密 web
這是我偶然發現的,python
進行rsa
運算簡直慢出天際.可是這個問題並不會很是明顯的爆發,畢竟只算一次的話,最多也就是零點幾秒甚至更少的時間,當訪問量不大的時候並無那麼明顯,很難發現.
可是有一天我使用多線程進行rsa
加密的時候,發現我不管使用多少個線程,加密的速度都是同樣的慢,纔算是發現這個問題.它很容易成爲服務器性能的瓶頸.
固然這個問題仍是能夠解決的.用Golang
寫了一個加解密的模塊,編譯成.so
給python調用,這個問題就算解決了.ajax
相似上傳Excel導入數據數據庫
這是一個很複雜的狀況.大文件傳過來以後,究竟選擇什麼策略進行處理不能一律而論.好比前期我選擇直接讀出數據寫入到數據庫中,後來給我來了一個10W行的excel,直接GG了,python
的循環操做是很是慢的,1W行的文件已經須要加載好久了,這就致使服務端處理Excel並實時對數據進行格式檢查和去重並當即返回結果是很不合理的需求,固然不是說別的語言能作的完美,而是想說由於django
不能直接處理異步任務,完成相似需求就必須提升系統複雜度,引入消息隊列和額外的worker
才行,不划算. django
實際上使用Django
真正能遇到性能問題的時候很少,由於絕大部分產品不存在性能瓶頸,一個產品QPS能達到10,就已經美滋滋了,這個吞吐量Django
徹底沒問題,到了Django
不行了那個時候換性能更好的方案徹底不是問題.json
我全部使用Django
開發的項目,架構上都差很少.後端
Nginx
沒什麼說的,配合gunicorn
使用proxy_pass
,方即可靠,省心.gunicorn
性能優秀,使用簡單,可靠,徹底對uwsgi
沒興趣Django
Redis
在python技術棧中自然扮演着緩存和消息隊列的雙重身份,與Django
和Celery
配合完美.省一個系統組件.Celery
彌補Django
異步任務缺陷,妙用無窮.MySQL
其實postgreSQL
與Django
更搭,不過各個雲服務都是對MySQL
支持更好一些.這套東西我使用起來能夠說並無遇到過解決不了的問題,不說無所不能,可是混口飯吃絕對是沒問題了.很是適合中小型項目快速開發試錯,基本上不須要開發基礎功能.
好久沒寫後端渲染的項目了,這裏都是先後端分離的內容
一個Django項目初始化以後,第一個要考慮的就是模塊的劃分,app創建好以後,我通常會在settings.py
那個項目同名文件夾下創建以下幾個文件:
basic.py
這個文件我用來定義一些基礎的組件,最經常使用的是這個.
繼承於HttpResponse
,定義返回結構的類
import json from django.core.serializers.json import DjangoJSONEncoder from django.http import HttpResponse from Myapp.settings import DEBUG class Response(HttpResponse): def __init__(self, data=None, msg="成功", status_code=200, encoder=DjangoJSONEncoder, json_dumps_params=None, **kwargs): if json_dumps_params is None: json_dumps_params = {} if DEBUG: # 開發模式增長縮進,方便人工檢查數據 json_dumps_params["indent"] = 2 json_dumps_params["ensure_ascii"] = False kwargs.setdefault('content_type', 'application/json') ret = dict( status_code=status_code, msg=msg, ) if data: ret["data"] = data s = json.dumps(ret, cls=encoder, **json_dumps_params) super().__init__(content=s, **kwargs)
繼承Django
的HttpResponse
類,參考(抄)了django.http.JsonResponse
的結構.接收status_code
,msg
,data
,避免在視圖函數中重複的構建返回結構,只須要關心業務數據就能夠了.同時能夠約束一塊兒開發的人,避免不當心寫錯返回結構之類的事兒.與之配合的還有一個: code.py
# 請求成功 OK = 200 # 資源不存在 NOT_FOUND = 404
這樣在構建返回結構的時候,直接
return Response(msg="資源不存在",status_code=NOT_FOUND)
前端接收到的返回就是統一格式的JSON
,開發模式帶有縮進,生產環境DEBUG模式關掉就是普通JSON
字符串了
{ "status_code":404, "msg":"資源不存在" }
歸納的說就是,將返回結構抽象成一個對象,而且統一管理返回碼,這樣在開發中能少些一些重複代碼,節省精力,最重要的是方便維護.
自定義中間件:
Django
自己繼承了模板引擎,默認開啓,可是如今流行的先後端分離方案下,模板是能夠放棄的(可是初期的管理後臺我更喜歡用自帶的admin
模塊,能夠經過配置兩套不一樣的settings.py
文件和manage.py
,實現使用不一樣的項目配置).一樣,基於session
的用戶驗證是否要繼續採用也是能夠靈活選擇的.這裏我說一下咱們是怎麼用JWT
來代替session
的. Django
自己是經過SessionMiddleware
和AuthenticationMiddleware
來管理session
和用戶身份的.咱們使用JWT
代替session
,那麼天然就要用一箇中間件來代替SessionMiddleware
,AuthenticationMiddleware
依賴於session
一樣也就沒法使用了.咱們用下邊這個中間件來代替他們
class JwtMiddleware(object): def __init__(self, get_response=None): self.get_response = get_response def __call__(self, request, *args, **kwargs): auth_token = request.META.get("HTTP_AUTHORIZATION") try: _id = jwt.decode(auth_token, SECRET_KEY)["id"] user = User.objects.get(id=_id) except: user = AnonymousUser() request.user = user response = self.get_response(request, *args, **kwargs) return response
和前端約定在header
中攜帶Authorization
字段,做爲身份證實.這個token
是登陸接口簽發的,這麼作的好處:
django
的admin
模塊進行登錄,進行開發,這意味着用戶管理等模塊和業務模塊能夠直接拆分,在兩個分支上開發LoginRequiredMixin
等框架自身依賴request.user
接口的功能依然可用,固然相似的功能咱們通常也本身寫了.不過在儘可能不破壞框架的可用性的前提下進行自定義,是咱們快速開發的基本原則之一.能夠有效下降團隊溝通學習成本,文檔都省了,業務邏輯部分徹底不須要關注自定義以後的用戶鑑權方式.值得注意的地方是,讀取用戶是一個數據庫操做,Django
原生中間件使用了懶加載的模式來操做,這麼作的緣由(我猜的)是,django做爲框架,沒法肯定是否須要用到user對象,若是每次都加載會很蠢,因此採用懶加載,第一次加載的時候才獲取一次並緩存user,避免第二次讀庫.咱們的項目要求每次操做都要讀,而且只會讀一次,就不須要使用懶加載了.更好的設計是根據本身的需求選擇是直接讀取用戶仍是使用懶加載讀取用戶信息.
小結: 還有一個將請求body
讀取成dict
綁定到request
的中間件,避免業務邏輯進行json.loads(request.body)
的操做,由於這個是須要try..except..
的操做,最少也須要四行代碼才能作完.經過這些,算是約定了一個團隊內部的開發基本規則,保證你們寫的邏輯返回數據結構是能夠統一維護的,定義的errcode
都集中在一個文件.業務邏輯中須要的信息(request.user
/request.json
)都已提供.
咱們寫業務邏輯的時候,可能會遇到將一個Model
對象序列化爲一個JSON
的狀況,大名鼎鼎的drfDjango restful framework
就是作這個的,可是今天不提他了.
Model
序列化爲JSON
的的代碼,我一般是寫在Model
中,哪怕他只用了一次我也習慣定義在Model
中,View
層只調用方法而不須要將數據分別取出進行構建.這樣咱們View
層的邏輯會顯得很是簡潔,一樣也更容易維護.
除了最終的序列化,Model
不少時候還會出現計算屬性.
這些若是寫在業務邏輯裏,不免要寫(datetime.datetime.now()-obj.publish_time).total_seconds()
這種又臭又長的代碼,可能還會在多個地方使用到.這些數據雖然不是直接寫在數據庫中,但依然屬於Model
層該作的事兒,那麼咱們就寫在Model
就完了.配合@property
,進一步保證了代碼可讀性.
假如如今咱們須要作一個定時任務,定時從網上抓取信息存入Django
項目的數據庫中.咱們能夠選擇直接寫SQL
語句插入,可是有些問題不太好處理.
SQL
語句使其和Model
行爲一致因此我推薦使用在定時任務中使用Django ORM
,保證和項目內代碼邏輯一致,下降維護成本.
如今咱們須要編寫一個獨立於項目運行的腳本,官方給出的例子是:
import django from django.conf import settings from myapp import myapp_defaults settings.configure(default_settings=myapp_defaults, DEBUG=True) django.setup() # Now this script or any imported module can use any part of Django it needs. from myapp import models
我經常使用的是
import os import django os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'Myapp.settings') django.setup()
其實都差很少,我也建議使用官方的方法,能夠避免出現環境變量路徑不對的問題.
這個操做至關於加載了django項目的配置,在這以後就能夠引用你想用的項目Model
,進行必要的操做了.必須在django.setup()
以後引入Model
經過這個方法,也能夠不用django-celery
了,配合處理異步任務.
Python
進行WEB開發的惟一優點大概就是速度了.並非說用python
的人比用java
的人快,而是說同一我的用python
開發會更快一些.
使用Django
而不是Flask
或者Tornado
也是爲了開發快,迭代快.作這些邊邊角角的工做,作這些細節的東西,都是爲了快.寫一行代碼也許須要5秒鐘,改一行代碼可能須要一成天.也許今天多花幾分鐘定義的Response
能夠避免和前端扯皮的致使衝突升級打架鬥毆住院一個月.仍是很是值得的.本身看着本身寫的代碼整潔乾淨,心情也更好不是?
https://luliangce.gitee.io/bl...