以前 Python 後端開發框架中,對 Tornado 和 Flask 接觸比較多,前者適合做爲服務框架,後者因爲輕量經常使用來構建簡單的後臺或服務。python
Django 之於上面兩個 Web 框架,其自身實現了不少工具類庫,顯得更爲笨重,但上手以後,不少功能不須要再本身實現,比較方便。docker
因爲已經集成諸多功能,Django 也更常被用來做爲後臺開發框架。數據庫
最近新接手一個後臺開發任務也是基於 Django 開發,所以本文對最近這兩週的所學作個自我總結。django
在具體講代碼以前,我先講講整個系統的架構。後端
剛接手任務時,數據庫使用 MySQL5.5,對應的 Django 版本爲 2.0(支持MySQL5.5的最高版本)。設計模式
考慮到系統對接了不一樣產品線,一些數據格式存在不肯定性,我將數據庫改成支持 JSON 格式的 MySQL5.7,Django 也對應升級到最新版 2.2。固然,使用其餘數據庫也是能夠的,例如 PostgreSQL 或者 MongoDB,爲了避免影響原來開發人員的操做習慣,我仍是繼續使用 MySQL。api
同時考慮到平臺存在上傳、解析 Excel 文件這種耗時操做,我引入 Celery + RabbitMQ 實現這類操做的異步執行,優化用戶體驗。bash
Django 還有不少功能我暫時未使用到,其自己功能之強大着實減小了我造輪子的代碼量,但因爲初學,也確實花了很大功夫琢磨每一個功能。架構
下面講講這兩週接觸到的 Django 的幾個模塊以及基礎用法,更詳細的功能請查看官方文檔。app
在正式講功能模塊以前,必須得先介紹一下 Django 自帶的強大的項目管理工具: django-admin
。
一般咱們建立一個 Django 項目的過程是這樣:
# 安裝 Django
pip install django
# 建立一個 Django 項目
django-admin startproject myproject
複製代碼
在建立好 Django 項目後,項目根路徑會生成一個 manage.py
文件。該文件實際執行的就是 django-admin
的功能,只不過已經配置好一些系統變量。
例如在項目內建立一個名爲 mysite
的 App ,那麼能夠:
# 建立一個 App
python manage.py startapp mysite
複製代碼
記住這個 mysite ,這是你的應用名而不只僅是文件夾名稱,後面的開發代碼都會準確引用這個名稱。
用 manage.py
管理工具,能夠方便地實現服務啓動、數據庫遷移等操做,特別是數據庫遷移是我最經常使用也最愛用的功能,後面會提到。
Django 中,有 Project
(項目) 與 App
(應用) 的概念,其區別以下:
Project
是整個項目的最高層級,能夠在 Project
中對項目所需的依賴、環境等進行配置;
App
依託於項目,一個項目能夠有多個應用。
通常來講,一個獨立的管理後臺項目配置一個應用便可,將不一樣的用戶、項目、任務等功能都同屬於一個 App
。而若是須要開發一個內部管理後臺,集成 2 個不一樣的系統,這兩個系統中各有項目、任務等模塊,那麼咱們就建立 2 個 App
,由於這兩個應用中的功能互不相關。
Django 可用 ORM(對象關係映射) 模式操做數據庫,舉個例子:
# 獲取當前用戶的用戶名
operator = request.user
return operator.username
複製代碼
簡單的兩行代碼,底層已經由 Django 實現了鏈接數據庫(鏈接池)、查詢用戶表、返回 username 字段值等功能。
猶記得我在使用 Tornado 寫服務時,須要本身維護一個數據庫單例,關心服務啓動時數據庫實例的初始化順序等等...
使用 ORM 就須要你對每張表建立一個 Model
,內含你對錶字段的定義,例如字段類型、默認值、外鍵約束等。
舉個例子,一個 Task
表的 Model :
models.py
class Task(models.Model):
status = models.IntegerField(default=0)
assignee = models.ForeignKey(settings.AUTH_USER_MODEL,
related_name='+',
null=True,
on_delete=models.SET_NULL)
create_time = models.DateTimeField(auto_now_add=True)
update_time = models.DateTimeField(auto_now=True)
複製代碼
這裏也有要注意的地方:若是不特殊配置,Model 類名會被做爲數據庫表名的一部分,完整爲:<APP_NAME>_<MODEL_NAME>,例如本例爲 mysite_task
。
建立數據庫表的 Model 以後,咱們就能夠用 Migrations 功能建立表的初始化腳本,以及真正在數據庫中建表:
# 初始化遷移腳本
python manage.py makemigrations [<APP_NAME>]
# 執行遷移操做,將數據庫腳本在對應的數據庫中執行一遍
python manage.py migrate
複製代碼
Migration的功能強大在於,當你須要更新某個字段(例如將爲某字段設置默認值)時,你能夠直接修改 Model 中的字段配置便可,而後再次執行 makemigrations
,Django 會爲你新建一個數據庫腳本記錄這次更新,你再執行 migrate
便能將這次更新寫入到數據庫中。
不只如此,咱們還能夠新建一個空的 migrations 腳本,編寫數據插入操做。有這個腳本,咱們能夠在系統遷移時都保持一部分測試數據:
from django.db import migrations
# 插入方法
def insert_task(apps, schema_editor):
Task = apps.get_model('mysite', "Task")
db_alias = schema_editor.connection.alias
Task.objects.using(db_alias).bulk_create([
Task(name='task1'),
])
# 回滾方法
def rollback_task(apps, schema_editor):
Task = apps.get_model('mysite', "Task")
db_alias = schema_editor.connection.alias
Task.objects.using(db_alias).filter(name="task1").delete()
class Migration(migrations.Migration):
dependencies = [
('mysite', '0001_initial'),
]
operations = [
migrations.RunPython(insert_task, rollback_task),
]
複製代碼
每次遷移,Django都會存有記錄,可以判斷當前已經執行的操做,待執行的操做,存在風險的操做等。可使用 showmigrations
查看當前各個應用的數據庫遷移腳本執行狀況:
python manage.py showmigrations
複製代碼
Django 是 MVT 模式,View 至關於 MVC 設計模式中的 Controller(控制器) 而非 View(視圖)。真正的視圖由 Template(模版)或者靜態資源提供。
既然做爲控制器,那麼 View 的功能就相對清晰了:對接收到的請求數據(Request)作相應處理,並返回處理結果數據(Response)。
View 能夠是類,也能夠是函數+View裝飾器的模式,直接上官方文檔的代碼吧:
基於類的 View:
from django.http import HttpResponse
from django.views.generic import ListView
from books.models import Book
class BookListView(ListView):
model = Book
def head(self, *args, **kwargs):
last_book = self.get_queryset().latest('publication_date')
response = HttpResponse('')
# RFC 1123 date format
response['Last-Modified'] = last_book.publication_date.strftime('%a, %d %b %Y %H:%M:%S GMT')
return response
複製代碼
基於裝飾器的 View:
from django.views.decorators.http import require_http_methods
@require_http_methods(["GET", "POST"])
def my_view(request):
# I can assume now that only GET or POST requests make it this far
# ...
pass
複製代碼
這部分更多地涉及到業務邏輯,不夠通用所以很少贅述了。
URL 模塊定義了 View 的路由地址:
mysite/urls.py
from django.urls import path
from books.views import BookListView
urlpatterns = [
path('books/', BookListView.as_view()),
]
複製代碼
能夠配置多層路由,而後在最上層 URL 使用 includes
爲子路由添加 Prefix:
myproject/urls.py
path('api/v1/', include('mysite.urls')),
複製代碼
做爲測試開發工程師,對單元測試模塊仍是須要了解下的(惋惜目前正處於開發前期,測試用例可能須要在項目基本穩定以後再補充了)。
和大部分 Web 框架同樣,Django 的測試模塊也是提供了一個 HTTP Client,使用相似接口測試的模式進行「單元測試」。
我在編寫 Tornado 單元測試基礎框架時候,是將 Tornado 與數據庫的鏈接 Mock 掉,實際並未操做數據庫。但在 Django 中,因爲數據庫的鏈接並不是由我本身實現,也由於使用了 ORM 模式,因此想真正與其餘服務解耦進行單測有些困難,目前就先保持這種測試模式了。
仍是以官方示例代碼爲例吧,編寫一個測試套件類:
import unittest
from django.test import Client
class SimpleTest(unittest.TestCase):
def setUp(self):
# Every test needs a client.
self.client = Client()
def test_details(self):
# Issue a GET request.
response = self.client.get('/customer/details/')
# Check that the response is 200 OK.
self.assertEqual(response.status_code, 200)
# Check that the rendered context contains 5 customers.
self.assertEqual(len(response.context['customers']), 5)
複製代碼
運行單元測試:
python manage.py test
複製代碼
在我接手該項目時候,大部分的代碼都糅合在一個 Python 文件中,讓我有代碼潔癖的人看着着實難受。
由於我本身也是才接觸 Django,爲了數理目錄結構我也踩了很多坑,包括將各個模塊拆分紅不一樣的應用等騷操做...
我就直接分享我目前正在使用的大體代碼結構吧:
myapp/ # 存放應用具體實現的代碼等
app_mod1/
models.py
views.py
app_mod2/
models.py
views.py
admin.py
app.py
models.py
views.py
urls.py
myproject/ # 存放項目配置文件
settings.py
urls.py
dockerfiles/ # 存放先後端Dockerfile
docs/ # 存放項目相關的文檔
tests/ # 存放測試套件
manage.py
README.md
複製代碼
在這個項目中,我還引入了 Django-REST-Framework(DRF) 框架的序列化和分頁模塊,Django-MySQL 對 JSON/List 格式數據的支持,以及 Celery 實現異步與定時任務等操做,總體來講兩週時間對 Django 以及擴展模塊的學習算是滿意,不過背後也是我天天下班繼續工做到11點,週末在家查文檔學習的辛苦。
Django 自己功能強大,是一個很是成熟的 Web 開發框架,可是上手有必定難度,須要下功夫好好消化官方文檔閱讀源碼,而且應用在本身的項目中。
後期會繼續分享本項目開發過程當中的一些經驗技巧,歡迎持續關注!可私聊加羣。