Python + Django 開發實戰(下)

2021-12-29 雲時代的運維開發

    上篇文章談到了項目開發關於如何進行功能劃分,如何設計表結構,如何設計API的話題(傳送門:Python + Django 開發實戰(中) )。接下來談談日誌記錄,單元測試和程序調優和重構等內容。

    這一篇要談的內容,也是開發中不可忽視的環節。 開發中日誌記錄能幫我們記錄信息定位問題;單元測試幫助我們在迭代開發過程及時發現問題,減少bug的引入; 而程序調優與重構,是一個永恆的話題。 

日誌的重要性想必不用多說。 在我看來,日誌的作用主要有兩點:

運營數據支撐。 比如頁面訪問情況,接口調用情況等等,方便運營人員後續的統計分析。  

錯誤回溯定位。 捕捉異常,並記錄錯誤信息,方便在系統出現問題時快速進行定位。 

<關於日誌的種類>

日誌的種類很多,比如系統日誌, nginx日誌, 網絡日誌, 還有業務日誌等等。

這裡主要討論的是業務日誌,即我們在開發過程中為記錄錯誤信息和業務信息的日誌。

<關於異常捕捉>

異常捕捉是必要的,但是這裡面有兩個小建議:

1. 異常捕捉儘量不影響代碼的可讀性

2. 異常捕捉不要太籠統,儘量分得細緻一點

舉個例子,我比較喜歡下面的書寫方式:

try:   <業務邏輯>   ...except Table1.DoesNotExist:   <錯誤處理>except KeyError:   <錯誤處理>except FooException:   <錯誤處理>except BarException:   <錯誤處理>except Exception:      <錯誤處理>      

<關於錯誤碼定義>

一個系統越複雜,越容易出現問題。錯誤碼的用途在於協助定位和修復問題。

最常見的錯誤碼是http狀態碼,比如500代碼軟體內部錯誤,404代碼找不到頁面等

另外各大開發平臺,對應的接口都會有自己的錯誤碼,比如淘寶開放平臺,新浪開放平臺,

微信開放平臺等,也都有自己一套錯誤碼的設計規則。 錯誤碼的設計規則遵循「足夠短」,

「字面容易望文生義」, 「儘量遵循已經達成共識」等。舉個慄子(僅供參考):

CODE_OK= 0     # 成功CODE_ERROR_AUTH_FAIL=40100  # 權限錯誤CODE_ERROR_DB_NOTEXIST=50100  # 資料庫錯誤CODE_ERROR_ACTIVITY_NOTSUPPORT=50200  # 活動業務邏輯錯誤CODE_ERROR_USER_NOTFOUND=50300  # 用戶信息錯誤

擴展閱讀:

錯誤碼設計以及 Django 的異常統一處理  https://www.chenshaowen.com/blog/error-code-design-and-unified-processing-in-django.html

<關於日誌配置>

Django使用python自帶的logging 作為日誌列印工具。簡單介紹下logging。

logging 是線程安全的,其主要由4部分組成:

Logger 用戶使用的直接接口,將日誌傳遞給Handler

Handler 控制日誌輸出到哪裡,console,file… 一個logger可以有多個Handler

Filter 控制哪些日誌可以從logger流向Handler

Formatter 控制日誌的格式

在django settings配置文件中,可以進行logging的配置。 一個典型的logging配置示例:

BASE_LOG_DIR = os.path.join(BASE_DIR, "log")LOGGING = {    'version': 1,  # 保留字    'disable_existing_loggers': False,  # 禁用已經存在的logger實例    # 日誌文件的格式    'formatters': {        # 詳細的日誌格式        'standard': {            'format': '[%(asctime)s][%(threadName)s:%(thread)d][task_id:%(name)s][%(filename)s:%(lineno)d]'                      '[%(levelname)s][%(message)s]'        },        # 簡單的日誌格式        'simple': {            'format': '[%(levelname)s][%(asctime)s][%(filename)s:%(lineno)d]%(message)s'        },        # 定義一個特殊的日誌格式        'collect': {            'format': '%(message)s'        }    },    # 過濾器    'filters': {        'require_debug_true': {            '()': 'django.utils.log.RequireDebugTrue',        },    },    # 處理器    'handlers': {        # 在終端列印        'console': {            'level': 'DEBUG',            'filters': ['require_debug_true'],  # 只有在Django debug為True時才在屏幕列印日誌            'class': 'logging.StreamHandler',  #            'formatter': 'simple'        },        # 默認的        'default': {            'level': 'INFO',            'class': 'logging.handlers.RotatingFileHandler',  # 保存到文件,自動切            'filename': os.path.join(BASE_LOG_DIR, "xxx_info.log"),  # 日誌文件            'maxBytes': 1024 * 1024 * 50,  # 日誌大小 50M            'backupCount': 3,  # 最多備份幾個            'formatter': 'standard',            'encoding': 'utf-8',        },        # 專門用來記錯誤日誌        'error': {            'level': 'ERROR',            'class': 'logging.handlers.RotatingFileHandler',  # 保存到文件,自動切            'filename': os.path.join(BASE_LOG_DIR, "xxx_err.log"),  # 日誌文件            'maxBytes': 1024 * 1024 * 50,  # 日誌大小 50M            'backupCount': 5,            'formatter': 'standard',            'encoding': 'utf-8',        },        # 專門定義一個收集特定信息的日誌        'collect': {            'level': 'INFO',            'class': 'logging.handlers.RotatingFileHandler',  # 保存到文件,自動切            'filename': os.path.join(BASE_LOG_DIR, "xxx_collect.log"),            'maxBytes': 1024 * 1024 * 50,  # 日誌大小 50M            'backupCount': 5,            'formatter': 'collect',            'encoding': "utf-8"        }    },    'loggers': {       # 默認的logger應用如下配置        '': {            'handlers': ['default', 'console', 'error'],  # 上線之後可以把'console'移除            'level': 'DEBUG',            'propagate': True,  # 向不向更高級別的logger傳遞        },        # 名為 'collect'的logger還單獨處理        'collect': {            'handlers': ['console', 'collect'],            'level': 'INFO',        }    },}

在文件中使用logging也很簡單

import logginglogger = logging.getLogger(__name__)logger.debug("這是一個debug級別的日誌。。。。")logger.info("這是一個info級別的日誌。。。。")

擴展閱讀: 

Django之logging日誌    https://cloud.tencent.com/developer/article/1093273

當我們在談及單元測試時,大家可能會說這個單元測試很好,好在哪裡不知道,為什麼要搞單元測試也不清楚。很多時候我們寧願寫花時間寫業務代碼,手動測試,也不願意寫單元測試。 

那單元測試的價值在哪裡呢, 總結以下五點:

  確保了代碼在一定設定條件下的正確性,幫助我們很容易的檢查出基本的語法錯誤和一般邏輯錯誤

 確保了代碼的改動不會影響現有的功能,這一點在代碼重構的時候特別有用,如果沒有單元測試,改動了一個地方,往往不知道會對原有代碼產生什麼影響

使開發人員更好的理解代碼邏輯,良好的測試建立在對代碼邏輯的理解上

良好的測試要求模塊化, 解耦代碼,這是良好設計的標誌,換句話說測試使你的系統設計更好,如果你的系統不容易測試,那麼就要思考一下系統的設計是否有問題

 大大減少花在調試上的時間

前人總結的單元測試的一些最佳實踐:

  同樣的輸入要有同樣的輸出

原子性: 要麼成功,要麼失敗,不能部分通過

單一職責: 一個單元測試只測試一個行為

單元測試之間無互相調用

隔離外部調用,不依賴資料庫,網絡,外部文件,本地系統時間,環境變量,如果需要可以進行mock

不要在業務代碼裡插入測試邏輯

django單元測試一般寫在tests.py文件裡, 一個典型的單元測試用例

from django.test import TestCasefrom myapp.models import Animal  class AnimalTestCase(TestCase):    def setUp(self):        Animal.objects.create(name="lion", sound="roar")        Animal.objects.create(name="cat", sound="meow")     def test_animals_can_speak(self):        """Animals that can speak are correctly identified"""        lion = Animal.objects.get(name="lion")        cat = Animal.objects.get(name="cat")        self.assertEqual(lion.speak(), 'The lion says "roar"')        self.assertEqual(cat.speak(), 'The cat says "meow"')

寫完單元測試了,如何執行呢?可以執行下面的命令

python manage.py test  python manage.py test packageA.tests  python manage.py test packageA.tests.AnimalTestCase python manage.py test packageA.tests.AnimalTestCase.test_animals_can_speak 

擴展閱讀:

單元測試及最佳實踐 https://www.jianshu.com/p/3b6daabeb91e

   

項目開發完不代表一勞永逸, 因為一直會有新的需求和新的情況出現。 所以調優和代碼重構一直在路上。 

<settings區分不同環境>

正常創建完django 項目, settings只有一個settings.py。 但是我們有幾個部署環境, 一個本地開發環境, 一個測試環境, 一個生產環境。 

單純一個settings不能滿足需求,於是把settings按照不同的環境進行拆分。 拆分後建立一個settings文件夾,把配置文件放在settings下,目錄結構如:

settings   - default.py # 共用的變量放這裡   - local.py # 放差異化的變量, 第一行是from .default import *導入共用變量   - test.py # 同上   - prod.py # 同上

進行拆分之後,要使用不同的環境,就需要設置環境變量來指定要使用的settings配置

export DJANGO_SETTINGS_MODULE=oakvip.settings.prod

<引入設計模式>

以前的開發是線性的, 一直堆測試邏輯,寫一堆if--else,  代碼一多,就容易出現各種問題,比如說可讀性差,代碼耦合多靈活性差(增加需求,往往要寫很多的額外代碼,還很容易出錯),重複代碼多難維護等等。 所以後面的代碼重構引入了設計模式來改善這類問題。

 舉一個例子, 運營經常會有各種各樣的活動需求,需要增加活動模塊。 以前的做法是很多活動的代碼都混在一塊,每次要加新的活動模塊的時候,總是很容易影響之前的活動模塊,導致其它活動執行代碼時出錯。 後面我把活動的業務邏輯抽象出來,寫了一個基本類, 其它的活動都繼承這個類,邏輯不一樣的地方就重寫方法即可。 然後放一個統一的入口,根據活動的名稱路由到不同的活動代碼(這裡比較抽象,可以參考工廠模式, 模板模式,想了解更多設計模式相關的知識,請移步擴展閱讀)。 這樣做的好處是以後新增一個活動,只要新增一個繼承活動基類的子類就可以了。 其它代碼都不需要動。 

擴展閱讀:

Python與設計模式系列連載  https://yq.aliyun.com/articles/70448

<其它優化>

還有其它優化,或者說必要的操作, 比如說將日誌進行集中採集發送到ES並設置日誌監控, 將一些異步操作和定時任務用Celery框架管理起來,增加Redis緩存提升接口性能等等。限於篇幅,這裡就不展開了。後面用單獨的文章來介紹。 

到此,我們講了開發過程中的9個方面的內容,分別是: 版本選擇,目錄結構設計, 編程規範,模塊劃分, 表結構設計, 接口設計, 日誌記錄, 單元測試,以及代碼的重構調優。 

篇幅有限,不可能涵蓋開發所有方面的內容, 只能蜻蜓點水, 講得不夠深入。只希望借這三篇文章初步幫各位梳理開發的脈絡,對各位能夠有一點點啟發。 

開發過程中其實也會遇到各種各樣的問題,比如說並發問題,編碼問題,還有一些奇奇怪怪的報錯,都是在不斷的探索。 也許我會在後面的文章單獨我這些經驗分享出來。歡迎大家關注本系列的後續更新,也歡迎和我一起交流探討。

    年初立了一個FLAG,接下來我準備在公眾號裡寫一個專欄,站在初創企業的視角,來寫技術選型,項目開發的細節,項目管理的經驗等等,把遇到的問題踩到的坑統統記錄下來,都是些實戰的內容。

    內容包括不限於以下這些內容

如果你覺得文章有用,就順手點個」好看「吧。寫作不易,分享和轉發是最大的支持。

相關焦點

  • python測試開發django-1.開始hello world!
    前言當你想走上測試開發之路,用python開發出一個web頁面的時候,需要找一個支持python語言的web框架。
  • Python利用Django 構建Rest Api: 快速入門教程
    >近期薦文:年底特輯:新一代測試平臺開源實戰項目Python 3+Django 3 結合Vue.js框架構建前後端分離Web開發平臺實戰1.mkdir blog_apicd blog_apipython3 -m venv envsource env/bin/activate # On Windows use `env\Scripts\activate`pip install djangopip install djangorestframework
  • python測試開發django-76.ORM查詢之Q查詢
    查詢 or如有個 Card 模型,表裡面有以下數據查詢 card_user 名稱為 YOYO 或 yoyo的數據,使用 SQL 可以這樣查詢select * from yoyo_card where card_user = 'YOYO' or 'yoyo';filter() 查詢,查詢或關係MyDjango>python
  • Django 2.0 項目實戰: 擴展Django自帶User模型,實現用戶註冊與登錄
    總體開發思路我們要利用Django 2.0開發一個叫users的app,來實現以下6項功能。我們一共將分3篇文章來介紹。本文只介紹用戶的註冊和登錄部分。INSTALLED_APPS = [    'reg.apps.RegConfig',    'django.contrib.admin',    'django.contrib.auth',    'django.contrib.contenttypes',    'django.contrib.sessions',    'django.contrib.messages
  • 使用 Django 進行測試驅動開發
    這種專注可以幫助開發人員避免範圍蔓延。TDD 可以通過較短的開發周期提高開發效率。一次解決測試用例中的個別可以最大限度地減少幹擾因素。重大更改將更容易跟蹤和解決。減少了調試工作,提高了效率,並且將更多時間花在開發上。編寫測試時考慮到了需求。正因為如此,它們更有可能被寫成明確的,可以理解的。這樣的測試可以作為代碼庫的優質文檔。
  • Django開發在線教育網站1
    Django的基本結構現在,我們在使用Python進行網站開發的過程中,不可避免地會使用到Django框架;我們將從一個項目實戰出發來熟悉Django整個開發的流程;便於讓大家在項目開發的過程中,深入地理解Django的使用,以及各個功能模塊的作用
  • Python Django之路與您同行
    主要在這裡寫下自己的學習筆記、軟體測試思考及讀書感悟等,後續可能會系統的介紹一些python、django、移動端自動化測試、接口自動化測試、性能測試等。歡迎您的光臨!要想深入測試,必須了解功能邏輯,對數據流及網站架構比較清楚,這點也說過多次,真的很重要,必須要體現在工作當中,養成習慣,絕對不要對自己測試過的功能模塊其中的技術實現不清楚。這樣測試路會不好走!!!
  • 10道題教你使用python Django框架來實現web應用,值得收藏
    升級python版本後,第三方包的版本需要升級。有的第三方包不支持python3。這個時候可以嘗試使用2to3工具對第三包的python原始碼進行轉換,然後使用。這樣存在一定風險。無法轉換的,需要尋找替代包,應用伺服器上對應的代碼需要修改。關於django升級:django1.5開始支持python3。
  • Python——用 Django 寫 restful api 接口
    但目前在 python3.x 中,PyMySQL 取代了 MySQLdb。Django 是用 Python 開發的一個免費開源的 Web 框架,可以用於快速搭建高性能,優雅的網站。Django 中提供了開發網站經常用到的模塊,常見的代碼都為你寫好了,減少重複的代碼。
  • python測試開發django -142.Bootstrap 表單(form)
    收錄於話題 #django</p> </div> <div> <label><input type="checkbox"> Check box 複選框</label> </div> <button type
  • python測試開發django-132.Bootstrap Table設置表頭樣式(theadClasses)
    收錄於話題 #djangocolor: #042cff; background-color: #84f1cd; border-color: #84f1cd; }</style></head>給表頭設置自定義的 table-green 屬性// 作者-上海悠悠 QQ交流群:717225969
  • 一個完整的Django入門指南
    我們將使用Python 3,因為大部分主要的Python庫已經被移植到python 3,並且下一個主要的django版本(2.x)也將不再支持python 2。所以Python 3是正確的選擇。最好的方法是通過Homebrew安裝。
  • 【大話框架】django實戰講解
    123456<code class="language-python hljs ">>>> from django import template>>> from django.conf import settings
  • Django入門案例:圖書管理系統
    Web開發,也就是寫網站,是Django的一大應用方向。而Django就是目前最流行的python web框架之一。很多同學想了解這方面的學習案例,今天我們就給大家分享一個:用Django實現圖書管理系統無需寫sql,不需寫前端,利用Django自帶的Admin和ORM框架就能輕鬆實現一個多對多表關係的增刪改查。
  • 如何創建Django應用並連接資料庫
    >與本文密切相關的圖書推薦簡介Django是一個用Python編寫的免費的開源web框架,它具有可擴展性、可重用性和快速開發的特點。安裝並配置了Django開發環境之後,就可以繼續創建項目了。第一步:創建Django項目為了給我們的網站奠定基礎,需要使用django-admin命令創建項目,這是基礎。
  • Django實戰教程: 開發企業級應用智能文檔管理系統smartdoc(1)
    學好Django不僅可以開發中大型網站, 還可以開發專業的企業級 網絡應用(Web App)。
  • Python之Django文件上傳
    一、目標學習在Django下做個文件上傳的頁面、學習創建文件上傳目錄及設定二、試驗平臺windows7 , python3.7,Django2.1.5,三、概述本例較為簡單,僅介紹主要代碼,四、在項目根目錄創建靜態文件夾
  • 用Python加Django十分鐘建個網站
    而Django 是老牌基於Python的CMS框架了,一直聽說很強大,甚至曾經很紅的Ruby On Rails都參考了它的很多概念,今天我們就簡單學習一個首先當然要pip install django然後我們就可以新建一個目錄,在這個目錄下執行django-admin startproject HelloWorld它會創建HelloWord
  • 入門Python, 看這些資料就夠了
    最後,平時有空可以看看awesome-python-cn和python-tip上的內容,作為入門之外的擴展,開闊自己的視野【實驗樓Python3教程】連結:Python3 簡明教程 (https://www.shiyanlou.com/courses/596)簡介: 提供了開箱即用的實驗環境 ,省去了搭建環境的麻煩。 偏重實戰。
  • django-admin和manage.py用法
    開篇話:我們在Django開發過程中,命令行執行最多的應該就是python manage.py <command>,今天聊聊manage.py這個命令。記錄要點:django-admin和manage.py 能做同樣的事情 像我們常用的python manage.py runserver,用django-admin也可以操作:django-admim runserver 注意: django-admin需要提前提前配置好DJANGO_SETTINGS_MODULE環境變量