小入門 Django(做個疫情資料包告)

渡碼發表於2021-11-08

Django 是 Python web框架,發音 [ˈdʒæŋɡo] ,翻譯成中文叫“姜狗”。

為什麼要學框架?其實我們自己完全可以用 Python 程式碼從0到1寫一個web網站,但那樣就要寫網路服務、資料庫讀寫等底層程式碼。而框架的作用是把這些底層基建已經搭建好了,我們只寫業務邏輯即可。

舉個例子,樓房就是框架,我們不關心底層的腳手架、鋼筋水泥是如何搭建的,只要有了這樣的框架我們就可以住進去,而裡面的房間要怎麼設計、裝飾才是我們關心的。

1. 初識Django

我使用的 Python 版本是 3.8,先執行下面語句先安裝 Django

pip install Django

安裝完成後,執行下面語句建立 Django 專案

django-admin startproject duma

專案的名稱可以自定義,我建立的專案名是 duma。

命令執行完畢後,在當前目錄會生成 duma 目錄,該目錄包含以下原始檔。

duma/
    manage.py
    duma/
        __init__.py
        settings.py
        urls.py
        asgi.py
        wsgi.py

簡單介紹下這幾個檔案的作用:

  • manage.py: 管理 Django 專案的命令列工具,就像一個工具箱,後面會經常用到
  • mysite/settings.py:Django 專案的配置檔案,如:配置該專案使用什麼資料庫、包含哪些應用等
  • mysite/urls.py:Django 專案的 URL 宣告
  • mysite/asgi.py:作為你的專案的執行在 ASGI 相容的 Web 伺服器上的入口。暫時用不到
  • mysite/wsgi.py:作為你的專案的執行在 WSGI 相容的Web伺服器上的入口。暫時用不到

後面的學習中,我們會使用、修改這上面的檔案,那時候對他們的作用會有更深的體會。

執行下面命令,啟動web服務,驗證 duma 專案是否建立成功。

python manage.py runserver

執行命令,會看到有以下資訊輸出

Starting development server at http://127.0.0.1:8000/

在瀏覽器訪問 http://127.0.0.1:8000/

img

看到上面的頁面,說明專案建立成功。

image.png

接下來我們要在 duma 專案中建立一個應用(app)。一個專案裡可以有多個應用,如電商專案裡可以有商城應用、支付應用和會員應用等等。

執行這行命令,建立一個應用

python manage.py startapp ncov

這裡建立了一個名為 ncov 的應用,用它來做一個疫情資料包告。專案根目錄會發現有個 ncov 目錄,包含以下檔案

ncov/
    __init__.py
    admin.py
    apps.py
    migrations/
        __init__.py
    models.py
    tests.py
    views.py

先不介紹它們的作用,這些檔案後面基本都會用到,到時候會詳細介紹。

2. Hello, World

“Hello, World” 是學習任何程式語言的演示程式,現在我們用 Django 實現一個“Hello, World” web應用。

首先,在 “nocv/views.py” 檔案中建立 index 函式

from django.http import HttpResponse


def index(request):
    return HttpResponse('Hello, World!')

然後,在 ncov 目錄中建立 urls.py 檔案,它用來定義 ncov 應用包含的 url。如:在電商商城應用中,會有商城首頁 url 和商品詳情的 url。

在 urls.py 檔案中新增一個url,使之與 index 函式對應起來。

from django.urls import path

from . import views

urlpatterns = [
    path('', views.index, name='index'),
]

第一個引數是 url 的路徑,這裡是空字串代表 ncov 應用的根路徑;第二個引數是該 url 對應的檢視;第三個引數是該 url 的名稱,可自定義。

最後,在 “duma/urls.py” 新增程式碼,將 ncov 應用的 url 註冊到 duma 專案中,新增後的程式碼如下

from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('ncov/', include('ncov.urls')),

]

在瀏覽器訪問 ncov 應用根路徑 http://127.0.0.1:8000/ncov/

img

如果看到如上圖的頁面就代表成功了。如果啟動的服務關閉了,需要在 duma 目錄執行 python manager.py runserver 命令重新啟動web服務。

img

當訪問 ncov 應用根路徑的時候,瀏覽器會產生一個 http 請求,duma專案的web服務接到該請求後,根據 urls.py 中的配置,呼叫 “ncov/views.py” 檔案的 index 函式來處理該請求,index 函式中用 HttpResponse 將字串 “Hello, World” 構造為一個 http 響應結果並返回給瀏覽器,瀏覽器接到該響應結果後,在頁面上顯示 “Hello, World” 字串。

細心的話,你會發現 HttpResponse('Hello, World!') 跟 print('Hello, World') 很像,後者是我們學習 Python 語言時第一個演示程式。它倆都是輸出 “Hello, World” 字串,前者輸出在瀏覽器上,後者輸出在控制檯(命令列)上。

這就是框架的威力,我們只關注業務邏輯,底層的 http 如何請求、如何響應以及如何返回給瀏覽器都是框架幫我們做好了。

3. 連線資料庫

一個電商網站會展現很多商品,這些商品資訊都儲存在資料庫中。同樣的,ncov應用也需要把疫情統計資料儲存在資料庫中。

開啟 “duma/settings.py” 檔案,找到 DATABASES配置,如下

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': BASE_DIR / 'db.sqlite3',
    }
}

這裡有一些預設的配置。

“default.ENGINE”代表資料庫引擎是 sqlite3,是一個輕量資料庫。你也可以將資料庫引擎改成 MySQL、MongoDB等。

“default.NAME”是資料庫名稱,對於sqlite資料庫來說這裡填資料庫的路徑, BASE_DIR 代表專案根目錄,此時再看下專案根目錄可以發現有 db.sqlite3檔案,它是 Django 建立的,後面我們就用它來儲存資料。

不知道你會不會有這樣的疑問,說好的資料庫,怎麼是個檔案?實際上資料庫的底層就是檔案,只不過是在檔案之上建立了一套引擎可以將檔案中的內容以表格展示,並提供增加、刪除、修改、查詢的功能。就好比程式設計師的本質也是人,只不過從事程式設計工作所以被稱為程式設計師。

有了資料庫,還需要在資料庫裡建立表。一般來說,可以用資料庫命令直接建表。但由於我們用的是框架,所以就可以用 Django 來操作。

在 “ncov/models.py” 檔案中建立一個 Django 模型

from django.db import models


class CyStat(models.Model):
    stat_dt = models.CharField(max_length=10) # 日期
    cy_name = models.CharField(max_length=50) # 國家名稱
    confirm = models.IntegerField() # 累計確診
    dead = models.IntegerField() # 累計死亡
    heal = models.IntegerField() # 累計治癒
    today_confirm = models.IntegerField() # 現有確診
    today_new_confirm = models.IntegerField() # 新增確診

這裡定義 CyStat 類用來表示每個國家每天的疫情統計資料。包括 7 個屬性,用 models 中的類物件來初始化。

stat_dt 和 cy_name 定義為 models.CharField型別,代表字元型別。日期是 2021-11-01 這樣的格式,佔用10個字元,所以 max_length=10;對國家名稱來說一般不超過 50 個字元,所以它的 max_length=50。

其他幾個欄位都是統計數字,用整型即可。

有了資料模型只是第一步,我們要怎麼獲取資料呢?這時候就需要將模型與資料庫中的表關聯起來。

首先,將 ncov 應用註冊到 duma 專案裡,在 “duma/settings.py” 檔案中找到 INSTALLED_APPS 配置,並在陣列中新增 ncov 應用,新增後 INSTALLED_APPS 陣列如下

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'ncov.apps.NcovConfig'  # 註冊 ncov 應用
]

接著,執行下面命令

python manage.py makemigrations ncov

執行後,可以看到輸出以下資訊

Migrations
for 'ncov':
  ncov/migrations/0001_initial.py
    - Create model CyStat

該命令會在 “ncov/migration” 目錄下建立 0001_initial.py 檔案,如果看原始碼可能看不出它的功能,我們可以執行下面語句將其轉成 sql 就容易理解了。

python manage.py sqlmigrate ncov 0001

執行後,輸出

BEGIN;
--
-- Create model CyStat
--
CREATE TABLE "ncov_cystat" ("id" integer NOT NULL PRIMARY KEY AUTOINCREMENT, "stat_dt" varchar(10) NOT NULL, "cy_name" varchar(50) NOT NULL, "confirm" integer NOT NULL, "dead" integer NOT NULL, "heal" integer NOT NULL, "today_confirm" integer NOT NULL, "today_new_confirm" integer NOT NULL);
COMMIT;

可以發現實際上就是一條建表sql,表名是應用名和模型類名的組合,用下劃線連線。除了 id 自動新增外,其他欄位名稱和定義與模型類屬性一致。

最後,執行下面命令來完成建表操作

python manage.py migrate

我們可以開啟 db.sqlite3 資料庫來檢視是否成功。Mac電腦自帶 sqlite3 命令直接開啟,Windows 電腦可以安裝 SQLite Administrator客戶端。

在專案根目錄執行,開啟資料庫檔案

sqlite3 db.sqlite3

執行 .tables 檢視資料庫中的表

sqlite> .tables
auth_group django_admin_log
auth_group_permissions django_content_type
auth_permission django_migrations
auth_user django_session
auth_user_groups ncov_cystat
auth_user_user_permissions

可以發現名為 ncov_cystat 的表,它就是按照 CyStat 類建立的表。除此之外還有很多其他表,它們是 Django 框架自帶的,我們可以先忽略。

這樣我們將模型 CyStat 類與資料庫中的 ncov_cystat 表對應的,後續我們需要查詢或者修改資料直接操作 CyStat 類就可以了,而不用寫 sql。

這裡我們又可以發現使用 Django 框架的一個優勢 —— 將模型類與資料庫隔離(行話叫解耦)。帶來的好處是,如果未來我們的專案上線後想把 sqlite 資料庫換成 MySQL,我們只需要在 settings.py 檔案中修改 DATABASES 的資料庫引擎和資料庫名稱,重新執行建表命令即可。表的定義以及對錶的查詢、更新邏輯完全不用改。

img

4. 編寫web頁面

最後一節,我們來編寫web頁面展現資料。有了上面的基礎我們知道,應該在 views.py 檔案中查詢 ncov_cystat 表的資料,然後將資料返回給瀏覽器。

首先需要向 ncov_cystat 表中匯入一些資料,可以參考之前的文章《用Python繪製全球疫情變化地圖》自己抓取。

我也準備了一部分資料放在 “ncov/sql/插入疫情資料.sql” 原始碼包裡,複製 1 ~ 60 行 sql 在 sqlite 客戶端執行即可。

sqlite> insert into ncov_cystat(stat_dt, cy_name, confirm, dead, heal, today_confirm, today_new_confirm) VALUES ("2021-09-03", "cn", 123169, 5685, 115024, 2460, 33);
sqlite> insert into ncov_cystat(stat_dt, cy_name, confirm, dead, heal, today_confirm, today_new_confirm) VALUES ("2021-09-04", "cn", 123199, 5685, 115105, 2409, 30);
...

讀取資料,返回給瀏覽器。修改 “ncov/views.py” 檔案中的 index 函式

from django.shortcuts import render

from .models import CyStat


def index(request):
    cy_stats = CyStat.objects.filter(cy_name='cn').order_by('-stat_dt')[:7]
    context = {
        'cy_stats': cy_stats
    }

    return render(request, 'ncov/index.html', context)

CyStat.objects 會返回 ncov_cystat 表裡所有記錄,filter 用來按照欄位過濾表中的資料,'cn'代表中國,cy_name='cn' 表示我們只保留國內資料,order_by 用來按照某欄位(列)對返回的結果排序,欄位名前加 ‘-’ 代表降序,這裡我們只取最近 7 天的資料。

現在我們不能像 “Hello, World” 那樣直接返回,因為那種方式返回的是一個字串,沒有任何樣式。我們返回的應該是一個 HTML 檔案,所以需要呼叫 reder 函式,返回 “ncov/index.html”。

在 ncov 目錄裡建立 “templates/ncov/index.html” 檔案,編寫以下程式碼

<h3>國內疫情資料</h3>

<table border="1">
    <tr>
        <td>日期</td>
        <td>現有確診</td>
        <td>新增確診</td>
    </tr>
    {% for stat in cy_stats %}
    <tr>
        <td> {{ stat.stat_dt }} </td>
        <td> {{ stat.today_confirm }} </td>
        <td> {{ stat.today_new_confirm }} </td>
    </tr>
    {% endfor %}
</table>

該檔案中使用表格來展示資料,你會發現這並不是一個純 HTML 檔案。準確來說index.html 是Django 定義的一種模板語言,它支援按照一定的語法寫 Python 程式碼,比如說裡面的 for 迴圈、stat物件的使用。

render 函式可以執行解析模板語言,生成純 HTML 檔案,返回給瀏覽器。

在瀏覽器訪問 http://127.0.0.1:8000/ncov/ ,可以看到如下頁面

img

雖然資料能展示出來了,但有些醜,需要優化下前端樣式。

img

剛剛說的 HTML 和 Django 模板語言都是標記語言,語法都比較簡單,之前沒學過的朋友可以找些教程簡單補一下。

要展示比較漂亮的圖片,一般要藉助 js 實現,有 js 的基礎的朋友可以自己寫前端頁面。如果沒有可以用 pyecharts ,它支援用 Python 程式碼製作圖表。

下載 pyecharts GitHub 專案(https://github.com/pyecharts/pyecharts)原始碼,將 “pyecharts/render/templates” 目錄中的原始檔複製到 “ncov/templates” 目錄中,結果如下

img

繼續修改 index 函式,改為使用 pyecharts API 返回折線圖。

from django.http import HttpResponse
from django.shortcuts import render
from pyecharts.charts import Line, Map
from pyecharts import options as opts

from .models import CyStat


def index(request):
    cy_stat = CyStat.objects.filter(cy_name='cn').order_by('-stat_dt')[:14]

    stat_list = [x.stat_dt for x in cy_stat]
    stat_list.reverse()

    today_confirm_list = [x.today_confirm for x in cy_stat]
    today_confirm_list.reverse()

    today_new_confirm_list = [x.today_new_confirm for x in cy_stat]
    today_new_confirm_list.reverse()

    c = (
        Line()
        .add_xaxis(stat_list)
        .add_yaxis("現有確診", today_confirm_list)
        .add_yaxis("新增確診", today_new_confirm_list)
        .set_global_opts(title_opts=opts.TitleOpts(title="國內疫情資料"))
    )
    return HttpResponse(c.render_embed())

頁面效果如下

img

這樣的效果才像點樣。

img

學到這裡,我們已經入門 Django 了,留個作業,看看你能否做出下面的效果。

img

全部程式碼(包括作業)關注公眾號 渡碼 回覆 “django入門” 獲取。今天介紹的只是 Django 一小部分內容,如果大家反饋較好後面會繼續更新,有問題也可以隨時提問。

duma

相關文章