我如何用Django開發一個項目

前言

網絡上關於Django的內容其實已經不少了,包括本身也是從網絡上的內容一點一從零開始學習Django的,可是這部份內容都過於零散,能夠說大部分都是在講怎麼從django-admin startproject到建立Model,再到寫一個TODO List或者一個Blog之類的內容,對於已經瞭解Django的人來講,並無太好渠道去了解一些別人的實踐,在開發本身的項目時,遇到的需求很難去了解其餘人會怎麼設計,固然這也是社區存在的通病,你們更多在討論語言層面的問題.
我但願能把本身平時在工做中總結出來的tips分享出來,也許在某些地方能幫助到別人,也算是本身梳理經驗,自我提升的一個過程.
既然標題是如何開發,那我就按照一個項目的生命週期來整理.前端

需求分析

討論需求的事情就不談了.

一個項目需求給到以後,第一步確定不是django-admin startproject,而是選型,換句話說,咱們得先決定要不要使用django.它適合什麼樣的項目,不適合什麼樣的項目 java

避免websocket
Django出現的太早了,它甚至和ajax的年紀差沒幾年,因此對不少現代化的需求和功能支持都不太好,好比websocket,好比異步任務.
若是需求中有一些功能會比較依賴websocket,那麼就建議不要選擇Django,並非說徹底不行,可是Django Channelasgi目前來講都算不上很成熟的解決方案,不得不認可在這方面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技術棧中自然扮演着緩存和消息隊列的雙重身份,與DjangoCelery配合完美.省一個系統組件.
  • Celery 彌補Django異步任務缺陷,妙用無窮.
  • MySQL 其實postgreSQLDjango更搭,不過各個雲服務都是對MySQL支持更好一些.

這套東西我使用起來能夠說並無遇到過解決不了的問題,不說無所不能,可是混口飯吃絕對是沒問題了.很是適合中小型項目快速開發試錯,基本上不須要開發基礎功能.

前100行代碼

好久沒寫後端渲染的項目了,這裏都是先後端分離的內容

一個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)

繼承DjangoHttpResponse類,參考(抄)了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自己是經過SessionMiddlewareAuthenticationMiddleware來管理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是登陸接口簽發的,這麼作的好處:

  • 能夠先不開發用戶系統,使用djangoadmin模塊進行登錄,進行開發,這意味着用戶管理等模塊和業務模塊能夠直接拆分,在兩個分支上開發
  • 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,進一步保證了代碼可讀性.

在項目外訪問ORM

假如如今咱們須要作一個定時任務,定時從網上抓取信息存入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...

相關文章
相關標籤/搜索