[不做怎麼知道系列之Android開發者的30天後端養成故事 Day14] - 後端的核心 #資料庫 #關聯式 #MySQL

哈囉,我們又見面了,前面經歷了三天 DevOps 的訓練,今天回歸後端重要的一個環節,也就是資料庫操作,今天來瞭解一下 MySQL 這個老派卻還很常見的關聯式資料庫(Relational Database)吧,構構。

MySQL 和 Django 預設使用的 SQLite 差在哪裡 ?

可以參考 (2019) SQLite 與 MySQL 的差別 | Medium,我覺得這篇解釋得不錯 ~

簡單來說,兩者之間的差異可以比喻成 MySQL 是 大卡車、SQLite 是 機車,大卡車可以同時載很多貨物,可是開著大卡車很不方便,要臨時停下來上個廁所、買東西都很麻煩,也不是人人都會開大卡車;雖然也不是人人都會騎機車,但相對多數,機車的載貨量小,可是很方便,想做什麼就路邊停著,雖然這樣違法 XD,我知道你 get 到我想講的點了,哈哈。

同理,MySQL 處理資料的吞吐量大,可 同時 處理多請求時的資料 (可以想成多人同時在瀏覽你的網頁),可透過 TCP/IP 要資料,缺點是需要開著 Server,適合用在大型專案上;SQLite 則是處理資料的吞吐量小、不能同時 處理多請求的資料,但好處是輕量(檔案小、安裝快),不用開 Server (Serverless),適合用在行動裝置上,Android 就是用 SQLite

我們之後講 MongoDB 時,再來比較 關聯式(Relational) 和 非關聯式(NoSQL) 的差別吧,現在先來感受一下關聯式的用法。

安裝 MySQL

Windows 的安裝過程可以參考 (2016) Windows 上的 MySQL 安裝教學 (使用 MySQL Installer),安裝過程中有一個簡單的原則,如果你不知道那是什麼,就都安裝就對了,如果不知道是什麼,就選預設的。

我在安裝時,選擇的是 Developer Default 的方案,再來就是它建議要安裝的都安裝。安裝得選擇太多,作為一個資料庫新手,也無從講起每個選擇代表的意思,有興趣可以等入門了之後,再去慢慢瞭解。安裝的過程中,會覺得它非常囉嗦,但請耐住性子,稍微看過去每個步驟在做什麼,然後用預設的選項即可,唯一要注意的是 root 使用者的帳號密碼,不要設完就自己忘記了,作為學習用的,可以先設個簡單的帳號密碼,以免忘記。

安裝完 MySQL 之後呢 ?

然後,我就面臨到一個問題,我不知道我還能做些什麼,因為我對 SQL 語法不熟,MySQL Workbench 的介面也很難上手,也無法確認 Django 要怎麼做,才算是真的有連動到 MySQL,直到我找到了這一篇 (2018) Django+MySQL 实例入门 | 掘金,超實用的文章,我整篇幾乎都是照著它做出來的,它的 django 語法是 1.x 版的 (本篇是 Django 3.0.3 版),大部分程式碼差不多,來一起跟著做吧。

安裝後會出現兩個視窗

一個是 MySQL Workbench、一個是 MySQL Shell,這兩個的作用很明顯,一個是圖形化介面、一個是指令介面,沒錯,淺顯易懂,可是我覺得圖形化介面並沒有我想像中的好用,所以我在這邊選擇用 shell 的方式來瞭解 MySQL

先開啟 MySQL Shell

開啟 MySQL Shell 之後,預設會在 js mode,也就是可以用 javascript 來取用 MySQL,我猜應該是我安裝的時候有裝到 js connector,不過今天我們不用這個,我們用純 SQL 指令。

切到 SQL mode

mysql> \sql

連接到 MySQL server,填入密碼

mysql> \connect root@127.0.0.1:3306

1
2
3
4
5
6
7
8
Creating a session to 'root@127.0.0.1:3306'
Please provide the password for 'root@127.0.0.1:3306': *****
Save password for 'root@127.0.0.1:3306'? [Y]es/[N]o/Ne[v]er (default No):

Fetching schema names for autocompletion... Press ^C to stop.
Your MySQL connection id is 120
Server version: 8.0.19 MySQL Community Server - GPL
No default schema selected; type \use <schema> to set one.

查詢目前所有的 database 有哪些

mysql> show databases;

等同於 mysql> show schemas;

別忘了在輸入 SQL 語法時,要有 ; 分號作為一個指令的結束

建立一個叫作 django_mysqldatabase

mysql> create database django_mysql;

(注意 django_mysql 這個 database 的名字,就是等等要用在 django settings.py 設定 DB 的名字)

進入到 database 裡

mysql> use django_mysql;

離開 database

恩…,目前還沒有找到指令,可以先關掉整個 shell,再重新開一次 shell

將之前在 django shop 裡面寫的 Product model 轉成 MySQL table

settings.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
...
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.mysql',
'''
注意 `django_mysql` 這個名字,
要跟你在 MySQL shell 裡
創建的 database 名字,一模一樣
不然等一下 makemigrations 會出錯
'''
'NAME': 'django_mysql',
'USER': 'root',
'PASSWORD': 'admin',
'HOST': '127.0.0.1',
'POST': 3306,
}
}
...

先把舊的 sqlite migrations 刪掉,然後對 shop 這個 app 來 makemigrations

$ python manage.py makemigrations shop

結果:

1
2
3
Migrations for 'shop':
shop\migrations\0001_initial.py
- Create model Product

$ python manage.py migrate

1
2
3
4
Operations to perform:
Apply all migrations: admin, auth, contenttypes, sessions, shop
Running migrations:
Applying shop.0001_initial... OK

再回 MySQL shell 檢查是不是有新增 table 成功

mysql> show tables;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
+----------------------------+
| Tables_in_django_mysql |
+----------------------------+
| auth_group |
| auth_group_permissions |
| auth_permission |
| auth_user |
| auth_user_groups |
| auth_user_user_permissions |
| django_admin_log |
| django_content_type |
| django_migrations |
| django_session |
| product <-- |
+----------------------------+
11 rows in set (0.0010 sec)

除了 product 之外的 table,都是 django 預設的。如果你看的到這邊的 tables,就代表 你已經成功將 django 和 MySQL 連動了 ! 恭喜夫人賀喜老爺 ~

接下來就是後端資料庫的「增、查、改、刪」的基礎步驟練習。

檢查 table 內的欄位們是否正確 (desc,describe)

mysql> desc product;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
+----------------+---------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+----------------+---------------+------+-----+---------+----------------+
| id | int | NO | PRI | NULL | auto_increment |
| name | varchar(100) | NO | | NULL | |
| price | decimal(65,0) | NO | | NULL | |
| img | varchar(100) | NO | | NULL | |
| on_sale | tinyint(1) | YES | | NULL | |
| tag | varchar(20) | YES | | NULL | |
| percent_off | decimal(30,1) | YES | | NULL | |
| sale_price | decimal(30,0) | YES | | NULL | |
| bought_counter | decimal(30,0) | NO | | NULL | |
| created_date | datetime(6) | NO | | NULL | |
| published_date | datetime(6) | YES | | NULL | |
+----------------+---------------+------+-----+---------+----------------+
11 rows in set (0.0016 sec)

(增) 透過 django 加點資料進到 product 的 table

views.py

1
2
3
4
5
6
7
8
9
10
11
12
from django.http import HttpResponse
from .models import Product
import random

def insert_view(request):
for i in range(5):
product = Product()
product.name = "測試" + str(random.randint(0, 5))
product.price = random.randint(1, 500)
product.save()

return HttpResponse("批次新增資料完成")

urls.py

1
2
3
4
5
...
urlpatterns = [
...,
path('insert/', insert_view),
]

把 django 跑起來 !
$ python manage.py runserver

再到瀏覽器訪問 127.0.0.1:8000/insert/

確認資料是不是真的有塞進去 product 的 table 裡

sql> select * from product;

我這邊不小心執行了三次 XD

(查) 透過 django 查詢 table 裡的資料

views.py 新增一個 lookup_view 的頁面

1
2
3
4
5
6
7
8
def lookup_view(request):
result = Product.objects.all()
mlist = []
for item in result:
content = {"product name": item.name, "price": float(item.price)}
mlist.append(content)

return HttpResponse(mlist)

urls.py

1
2
3
4
5
6
...
urlpatterns = [
...,
path('insert/', insert_view),
path('lookup/', lookup_view),
]

結果:

(改) 透過 django 修改 table 中的資料

views.py

1
2
3
4
5
def modify_view(request):
product = Product.objects.get(id=1)
product.name = "我是後來才修改的產品"
product.save()
return HttpResponse('修改完成,請到 SQL shell 下指令\nsql> select * from product where id=1')

urls.py

1
2
3
4
5
6
7
...
urlpatterns = [
...,
path('insert/', insert_view),
path('lookup/', lookup_view),
path('modify/', modify_view),
]

結果:

只查詢其中一項 id=1 的資料

sql> select * from product where id=1;

查詢全部

sql> select * from product;

(刪) 透過 django 刪除 table 中的資料

views.py

1
2
3
4
def delect_view(request):
product = Product.objects.get(id=1)
product.delete()
return HttpResponse('刪除 id=1 的資料成功,請到 SQL shell 下指令\nsql> select * from product')

urls.py

1
2
3
4
5
6
7
8
...
urlpatterns = [
...,
path('insert/', insert_view),
path('lookup/', lookup_view),
path('modify/', modify_view),
path('delete/', delete_view)
]

結果:

檢查

sql> select * from product;

id=1 的那一項已經被我們刪掉了

如果是要刪除不存在的資料呢 ?

也就是再執行一次我們的 delete 頁面,因為裡面已經沒有 id=1 的資料了,所以會報 DoesNotExist 的 Error message

單日心得總結

今天剛入門 MySQL 的時候,真的有點不知所措,MySQL Workbench 這個圖形化介面的工具,實在對新手很不友善,我一直想找我的預設 database 到底有哪些,我都已經用指令找出來了,可是在 workbench 就是看不到,結果到很久很久後,我才發現有個很小顆的 Schemas 的按鈕。

按下 Schemas 的按鈕,我才發現原來藏在這 ! 該死,浪費我時間 QQ

但工具這種東西就是這樣,當你不會用的時候,都會覺得它是爛東西,可是當你掌握它的操作邏輯時,就換成是你駕馭它了,隨時可以用它來達到你想要的功能,真的要用過才會覺得沒什麼。

我是 RS,這是我的 不做怎麼知道系列 文章,我們 明天見。


  • 喜歡我的文章嗎? 趕快來看看我都發了什麼文章吧:我的文章目錄
  • 歡迎閱讀我的上一篇: [不做怎麼知道系列之Android開發者的30天後端養成故事 Day13] - 老闆,來碗自動化測試吧 #讓科技幫你省時間 #持續整合 #CircleCI
  • 歡迎閱讀我的下一篇: [不做怎麼知道系列之Android開發者的30天後端養成故事 Day15] - 後端核心中的新星 #資料庫 #MongoDB #NoSQL
  • Copyright: Copyright is owned by the author. For commercial reprints, please contact the author for authorization. For non-commercial reprints, please indicate the source.
  • Copyrights © 2021-2022 Sam Ho
  • Visitors: | Views:

請我喝杯咖啡吧~

支付宝
微信