Khóa học lập trình Python
Python là một trong những ngôn ngữ lập trình thông dịch bậc cao ra đời từ năm 1991. Trong những năm gần đây, xu hướng sử dụng Python đang tăng nhanh trên thế giới. Python được sử dụng làm ngôn ngữ dạy nhập môn lập trình cho sinh viên các ngành kỹ thuật ở nhiều trường đại học ở Mỹ. Python được xem là ngôn ngữ quan trọng hàng đầu trong các lĩnh vực của ký nguyên công nghiệp 4.0 như AI, Machine Learning, Data Science. Ngoài ra xu hướng lập trình backend với Python cũng phát triển mạnh mẽ trong những năm gần đây với việc Django (framework phổ biến nhất về backend viết trên Python) đã được nhiều công ty sử dụng để phát triển website của mình như : Youtube, Quora, Dropbox, Reddit, Instagram …
Trong bảng xếp hạng về mức độ phổ biến của các ngôn ngữ lập trình, Python thường được xếp ở vị trí từ thứ 3 đến thứ 1 ( tùy theo mỗi loại xếp hạng)
Hình 1 . Bảng xếp hạng ngôn ngữ lập trình của pypl.com (tháng 2/2019)
Hình 2. Bảng xếp hạng ngôn ngữ lập trình của tiobe (tháng 3/2019)
Hình 3. Xếp hạng ngôn ngữ lập trình của redmonk (quý I/2019)
Hình 4. Xếp hạng ngôn ngữ lập trình của IEEE (năm 2018)
Python có ưu điểm:
Bên cạnh đó Python có một số nhược điểm:
Hình 5. Thêm lựa chọn “Add Python to PATH” khi cài đặt Python
Từ menu start của Windows tìm ứng dụng IDLE và mở ứng dụng này
Hình 6. Khởi động ứng dụng IDLE
Khi cửa sổ IDLE khởi động xong, gõ vào cửa sổ một lệnh để chạy thử:
print('Hello')
Hình 7. Chạy thử lệnh trong cửa sổ IDLE
Tạo mới một chương trình : Từ Menu File, chọn New File, chương trình sẽ tạo ra một cửa sổ soạn thảo mới, nhập vào cửa sổ này nội dung một chương trình đơn giản, ví dụ:
x = 1
y = 2
z = x + y
print(f'{x} + {y} = {z}')
Hình 8. Tạo mới một chương trình
Lưu lại chương trình thành file (ví dụ hello.py), sau đó chạy chương trình bằng cách từ Menu Run, chọn Run Module (F5). Chương trình sẽ chạy và in kết quả ra cửa sổ IDLE:
Hình 9. Kết quả chạy chương trình được in ra cửa sổ IDLE
IDLE cho phép soạn thảo và chạy chương trình Python nhưng không hỗ trợ nhiều các chức năng phát triển ứng dụng. Với các ứng dụng python có nhiều file, nên sử dụng IDE khác. Có thể sử dụng một trong các IDE:
Pycharm được xem là IDE hỗ trợ Python đầy đủ nhất, tuy nhiên Visual Studio Code tương đối nhẹ trong khi hỗ trợ Python tương đối tốt. Muốn sử dụng Visual Studio Code để phát triển ứng dụng Python, cần cài đặt Python Extension cho Visual Studio Code:
Hình 10 . Cài đặt python extension cho Visual Studio Code
Biến số được sử dụng để lưu trữ các giá trị trong quá trình tính toán của chương trình
Cách thức khai báo biến trong Python :
bienSo = <gia_tri>
Trong đó bienSo là tên của biến số cần khai báo, <gia_tri> là giá trị ban đầu chúng ta muốn đặt cho biến số. Giá trị này có thể là một hằng số hoặc một biểu thức của các biến số đã được khai báo.
Ví dụ:
x = 1
y = 2
z = x + y
Sau khi được khai báo, giá trị của biến số có thể được thay đổi bằng cách gán lại một biểu thức mới cho biến:
x = 1
x = 2
x = x + 1
Để xóa một biến khỏi bộ nhớ, có thể sử dụng lệnh del :
del x, y, z
Mỗi biến số sau khi được khai báo sẽ chứa giá trị nhất định, gọi là dữ liệu. Giá trị dữ liệu này sẽ thuộc vào một trong các kiểu dữ liệu mà Python hỗ trợ
Ở giai đoạn mới học, chúng ta chủ yếu quan tâm đến 2 kiểu dữ liệu : kiểu số và kiểu String. Các kiểu dữ liệu còn lại sẽ được tìm hiểu dần trong các phần sau.
Lệnh input được sử dụng để nhập dữ liệu từ bàn phím. Dữ liệu trả về của lệnh là một biến string chứa dòng văn bản mà người dùng đã nhập vào:
giatri = input('Nhập giá trị cho biến : ')
Ví dụ 1
ten = input('Mời bạn cho biết tên của bạn : ') |
Ví dụ 2:
a = input('Số thứ nhất : ') |
Lệnh print được sử dụng để in dữ liệu ra màn hình, cú pháp của lệnh này như sau:
print(<danh sách giá trị ngăn cách nhau bởi dấu phảy>)
Ví dụ:
x = 1 |
Các cấu trúc if trong Python:
<Lệnh>
<Lệnh 1>
else:
<Lệnh 2>
<Lệnh 1>
elif <điều kiện 2> :
<Lệnh 2>
elif <điều kiện 3> :
<Lệnh 3>
else:
<Lệnh 4>
Lưu ý:
Điều kiện logic trong lệnh if
Điều kiện logic trong lệnh if về bản chất là một biểu thức, nhưng chỉ nhận một trong hai giá trị True/False . Điều kiện này thường được tạo ra từ các phép toán :
Một số ví dụ minh họa
Ví dụ 1:
Nhập vào từ bàn phím 2 số và in ra giá trị nhỏ nhất của 2 số đó
a = input('Số thứ nhất : ') |
Ví dụ 2:
Chỉ số BMI (Body mass index) dùng để đánh giá thân hình của một người. Chỉ số này được đo bằng tỉ số giữa cân nặng (tính theo kg) và bình phương của chiều cao (tính theo mét). Dựa vào chỉ số BMI, người ta có thể đánh giá được thân hình của một người:
Nhập vào giá trị chiều cao và cân nặng của một người từ bàn phím và cho biết tình trạng thân hình của người đó.
height = input('Chiều cao (mét) : ') |
Cấu trúc lặp for được dùng để thực hiện một vòng lặp với số lần lặp biết trước. Cấu trúc lặp for như sau:
for bienLap in tapGiaTri:
<khoi_lenh>
Cách biểu diễn tập giá trị lặp trong vòng lặp for
range(<end>)
Biểu thức này thể hiện tập các số nguyên từ 0 đến trước giá trị <end>,(tức <end-1>)
Ví dụ:
range(5) → 0, 1, 2, 3, 4
range(start, end)
Biểu thức này thể hiện tập các số nguyên từ <start> đến <end-1>
Ví dụ:
range(1, 5) → 1, 2, 3, 4
range(start, end, increment)
Biểu thức này thể hiện tập các số nguyên từ <start> đến <end-1> và tăng đều với khoảng cách increment.
Ví dụ:
range(0, 10, 2) → 0, 2, 4, 6, 8
Một số ví dụ minh họa
Ví dụ 1:
Tính tổng các số từ 1 đến 100
S = 0 |
Ví dụ 2:
Viết chương trình in ra bảng cửu chương
for i in range(2, 10): |
Cấu trúc lặp while dùng để thực hiện một vòng lặp với số lần lặp không biết trước. Cấu trúc lặp while như sau:
while dieukien:
<khoi_lenh>
Một số ví dụ minh họa
Ví dụ 1:
Kiểm tra một số có phải số nguyên tố không
x = int(input('x=')) |
Ví dụ 2:
Chuyển một số từ hệ thập phân sang hệ nhị phân
x = int(input('x=')) |
Trong Python có 2 kiểu số : số nguyên (int) và số thực (float)
Số nguyên là các giá trị số không chứa dấu phảy:
x = 1
y = 2
z = -10000
Số thập phân là các số có chứa dấu phảy :
x = 1.0
y = 10.5
z = 9e38
Các phép tính trên kiểu dữ liệu số trong Python:
Với Python 3, phép chia 2 số nguyên cho kết quả là một số thập phân:
>>> print(4/3)
1.3333333333333333
Phép tính dạng rút gọn :
Các phép tính dạng rút gọn +=, -=, *=, /=, //=, %=, **=, … được sử dụng để tính giá trị phép tính trên 2 biến và gán kết quả vào biến thứ nhất. Ví dụ:
Các hàm toán học:
Các hàm toán học (trong thư viện math) cho phép thực hiện một số hàm lượng giác và giải tích trên số thập phân:
Để sử dụng các hàm toán học, cần khai báo thư viện math : import math
Ví dụ :
import math |
Trong Python, giá trị của kiểu dữ liệu String được đặt trong cặp 2 dấu nháy đơn (') hoặc 2 dấu nháu kép (") :
hoten = 'Nguyễn Văn An' |
Các hàm thông dụng trên kiểu dữ liệu String:
Ví dụ:
>>> str(100)
'100'
Ví dụ:
>>> 'Chào ' + 'bạn'
'Chào bạn'
Ví dụ:
>>> 'Chào bạn'.lower()
'chào bạn'
Ví dụ:
>>> 'Chào bạn'.upper()
'CHÀO BẠN'
Ví dụ:
>>> 'Tôi sống ở Hà Nội'.replace('Hà Nội', 'Huế')
'Tôi sống ở Huế'
Ví dụ:
>>> 'Hà Nội'.split()
['Hà', 'Nội']
>>> 'Hà Nội, Việt Nam'.split(',')
['Hà Nội', ' Việt Nam']
Ví dụ:
>>> ' Hà Nội '.strip()
'Hà Nội'
Ví dụ:
>>> len('Chào bạn')
8
Truy nhập đến từng kí tự của String:
Một giá trị string được tạo thành từ một dãy các kí tự, để lấy kí tự ở vị trí index (bắt đầu từ 0) của một giá trị string, chúng ta dùng cú pháp:
c = text[index]
Bản thân một giá trị string có thể xem là một tập hợp kí tự và có thể dùng vòng lặp for chạy qua tập kí tự này
Ví dụ :
text = 'Chào bạn'
for c in text:
print( c )
Một số phép tính trên kí tự:
Ví dụ:
>>> ord('A')
65
Ví dụ:
>>> chr(65)
'A'
Substring:
Mỗi đoạn nhỏ của một giá trị string được gọi là substring. Bản thân mỗi substring cũng là một giá trị kiểu string.
Các cách để lấy ra substring trong Python:
text[start:end]
Biểu thức này trả về đoạn kí tự từ vị trí start đến end-1 của string gốc.
Ví dụ:
>>> text = 'Chào bạn'; print(text[0:4])
Chào
text[start:]
Biểu thức này trả về đoạn kí tự từ vị trí start đến hết string gốc.
Ví dụ:
>>> text = 'Chào bạn'; print(text[5:])
bạn
text[:end]
Biểu thức này trả về đoạn kí tự từ đầu đến vị trí end của string gốc.
Ví dụ:
>>> text = 'Chào bạn'; print(text[:4])
Chào
Ví dụ:
>>> text = 'Chào bạn'; print(text[-3:])
bạn
Format string
Để ghép nhiều giá trị string thành một có thể dùng phép cộng string như đã trình bày ở phía trên:
x = 1 |
Tuy nhiên việc cộng nhiều string sẽ làm chương trình khó theo dõi, do đó có thể dùng hàm format của kiểu dữ liệu string để ghép nhiều string (và các kiểu dữ liệu khác) với nhau.Cách thức hiện như sau:
x = 1 |
Mỗi cặp {} trong template của string sẽ tương ứng với một biến/biểu thức nằm bên trong hàm format.
Ngoài ra, có thể viết tên biến/biểu thức ngay bên trong cặp {} nếu sử dụng tiền tố f ở trước template của string, cách thực hiện như sau:
x = 1 |
Một số ví dụ minh họa:
Ví dụ 1:
Kiểm tra tính hợp lệ của mật khẩu (là một giá trị string). Mật khẩu hợp lệ nếu:
password = input('Nhập mật khẩu : ') |
Ví dụ 2 :
Chuyển một số trong phạm vi 0-99 thành phát âm tiếng Việt. Ví dụ : 85 → tám mươi lăm
bangso = ['không', 'một', 'hai', 'ba', 'bốn', 'năm', 'sáu', 'bảy', 'tám', 'chín'] |
Dữ liệu kiểu Date & Time được cung cấp bởi thư viện datetime của hệ thống:
from datetime import date, datetime |
Kiểu dữ liệu date chứa thông tin về ngày, tháng, năm. Kiểu dữ liệu datetime chứa thông tin về ngày, tháng, năm, giờ, phút , giây, mili giây.
Để tạo mới một đối tượng kiểu date/datetime:
from datetime import date, datetime |
Lấy ngày giờ hiện tại của hệ thống:
from datetime import date, datetime |
Truy nhập đến các thành phần của date/datetime :
from datetime import date, datetime |
Chuyển đổi từ datetime sang String :
from datetime import datetime |
Chuyển đổi từ String sang datetime :
from datetime import datetime |
Khoảng thời gian :
Để đo sự chênh lệch giữa các mốc thời gian, thư viện datetime cung cấp kiểu dữ liệu timedelta. Kiểu dữ liệu này có thể hiểu là một bộ gồm 2 thành phần là số ngày (days) và số giây (seconds)
from datetime import timedelta |
Tính khoảng thời gian giữa 2 thời điểm :
from datetime import datetime |
Cộng một mốc thời điểm với một khoảng thời gian :
from datetime import datetime, timedelta |
Kiểu dữ liệu List dùng để chứa một dãy nhiều phần tử. Khi khai báo giá trị, các phần từ của một List được đặt trong cặp dấu [ ].
Ví dụ:
dayso = [1, 3, 5, 7, 9] |
Các phần tử của một List có thể không cùng kiểu dữ liệu.
Một số phép tính trên kiểu dữ liệu List
Ví dụ:
>>> ds = [1, 2, 3, 4, 5] ; print(len(ds))
5
Ví dụ:
>>> ds = [1, 2, 3, 4, 5] ; ds.append(6); print(ds)
[1, 2, 3, 4, 5, 6]
Ví dụ:
>>> ds = [1, 2, 3, 4, 5] ; ds.remove(3); print(ds)
[1, 2, 4, 5]
Ví dụ:
>>> ds = [1, 2, 3, 4, 5] ; ds.extend([6, 7]); print(ds)
[1, 2, 3, 4, 5, 6, 7]
Ví dụ:
>>> ds = [1, 2, 3, 4, 5] ; ds += [6, 7]; print(ds)
[1, 2, 3, 4, 5, 6, 7]
Ví dụ:
>>> ds = [1, 2, 3, 4, 5] ; ds.reverse(); print(ds)
[5, 4, 3, 2, 1]
Ví dụ:
>>> ds = [1, 2, 3, 4, 5] ; print(1 in ds)
True
Ví dụ:
>>> ds = [1, 2, 3, 4, 5] ; print(3 not in ds)
False
Ví dụ:
>>> ds = [1, 2, 3, 4, 5] ; print(ds.index(3))
2
Ví dụ:
>>> ds = [1, 3, 2, 5, 4] ; print(min(ds))
1
Ví dụ:
>>> ds = [1, 3, 2, 5, 4] ; print(max(ds))
5
Truy nhập đến từng phần tử của List
Tương tự kiểu String, kiểu List cũng có thể truy nhập đến từng phần tử hoặc lấy ra các đoạn con bằng cách sử dụng các cấu trúc:
Sắp xếp một List
Để sắp xếp các phần tử trong một List theo một thứ tự tăng dần hoặc giảm dần có thể sử dụng hàm:
sorted(lst [, key=key_function, reverse=True/False)])
Các tham số cần truyền vào cho hàm sắp xếp:
Ví dụ:
>>> print(sorted([1, 3, 2, 5, 4]))
[1, 2, 3, 4, 5]
>>> print(sorted([1, 3, 2, 5, 4], reverse=True))
[5, 4, 3, 2, 1]
>>> def number_of_element(x): return len(x)
...
>>> print(sorted([[1,2], [3,4,6],[1]], key=number_of_element)) # sắp xếp theo kích thước các phần tử trong list
[[1], [1, 2], [3, 4, 6]]
Giá trị hàm thuộc tính key_function đôi khi có thể được truyền vào hàm sorted dưới dạng “lambda function”:
print(sorted([[1,2], [3,4,6],[1]], key=lambda x : len(x)))
Biểu thức
lambda x : len(x)
Tương đương với giá trị của một hàm f trong đó f được định nghĩa bằng
def f(x) : return len(x)
Một số ví dụ minh họa
Ví dụ 1:
In ra các số nguyên tố nhỏ hơn 1000
ds = [] |
Ví dụ 2:
In ra 10 dòng đầu của tam giác Pascal (các hệ số của khai triển nhị thức (a+b)n )
(a+b)0 = 1 → 1
(a+b)1 = a + b → 1 1
(a+b)2 = a2 + 2ab + b2 → 1 2 1
(a+b)3 = a3 + 3a2b + 3ab2 + b3 → 1 3 3 1
…
N = 10 |
Dữ liệu kiểu bộ nhóm (Tuple) tương tự với kiểu danh sách (List), chỉ khác là các phần tử sau khi khai báo thì không sửa (thêm, xóa) được. Các phần tử của dữ liệu kiểu nhóm được đặt trong cặp dấu ( )
dayso = (1, 3, 5, 7, 9) |
Các phép tính trên Tuple
Các phép tính trên Tuple gần giống với phép tính trên List, tuy nhiên Tuple chỉ hỗ trợ các phép tính đọc thông tin của Tuple, không hỗ trợ các phép tính làm thay đổi nội dung của Tuple
Kiểu dữ liệu Set dùng để chứa các phần tử của một tập hợp. So với kiểu List, kiểu Set có điểm khác là:
Các phép tính trên Set
Ví dụ:
>>> s = set([1, 2]); s.add(3); print(s)
{1, 2, 3}
Ví dụ:
>>> s = set([1, 2, 3]); s.remove(2); print(s)
{1, 3}
Ví dụ:
>>> s = set([1, 2, 3]); print(1 in s)
True
Ví dụ:
>>> s = set([1, 2, 3]); print(3 not in s)
False
Ví dụ:
>>> s1 = set([1, 2, 3]); s2 = set([3, 4, 5]); print(s1.union(s2))
{1, 2, 3, 4, 5}
Ví dụ:
>>> s1 = set([1, 2, 3]); s2 = set([3, 4, 5]); print(s1.intersection(s2))
{3}
Ví dụ:
>>> s1 = set([1, 2, 3]); s2 = set([3, 4, 5]); print(s1.difference(s2))
{1, 2}
Một số ví dụ minh họa
Ví dụ 1:
s1 = set([1, 2]) |
Ví dụ 2:
weekdays = set(['Thứ hai', 'Thứ ba', 'Thứ tư', 'Thứ năm', 'Thứ sáu']) |
Kiểu dữ liệu Dictionary dùng để chứa một bảng biến đổi 1-1 giữa 2 tập hợp :
Cú pháp khai báo kiểu Dictionary trong Python:
d = {
<key1> : <value1>,
<key2> : <value2>,
...
}
Ví dụ:
d = {"một" : 1, "hai" : 2, "ba" : 3}
Để truy nhập đến phần tử có key là <key> trong Dictionary, Python sử dụng phép toán []:
d[<key>]
Ví dụ:
>>> d = {"một" : 1, "hai" : 2, "ba" : 3}; print(d['một'])
1
Trong trường hợp nếu giá trị <key> không nằm trong tập nguồn của Dictionary, Python sẽ đưa ra thông báo Exception:
>>> d = {"một" : 1, "hai" : 2, "ba" : 3}; print(d['bốn'])
Traceback (most recent call last):
File "<pyshell#118>", line 1, in <module>
d = {"một" : 1, "hai" : 2, "ba" : 3}; print(d['bốn'])
KeyError: 'bốn'
Để tránh gặp lỗi này, có thể dùng hàm get của Dictionary để truyền giá trị mặc định trong trường hợp <key> không nằm trong tập nguồn của Dictionary:
d.get(<key>, <gia_tri_mac_dinh>)
Ví dụ:
>>> d = {"một" : 1, "hai" : 2, "ba" : 3}; print(d.get('bốn', -1))
-1
Một số ví dụ minh họa
Ví dụ 1:
Đếm tần suất của các từ trong một câu
text = 'Một năm có mười hai tháng, tháng hai có hai mươi tám ngày, các tháng còn lại có ba mươi hoặc ba mươi mốt ngày' |
Ví dụ 2:
Chuyển phát âm tiếng Việt của một số trong phạm vi 1-99 thành giá trị số đó. Ví dụ : tám mươi lăm → 85
bang_so1 = {'một' : 1, 'hai' : 2, 'ba' : 3, 'bốn' : 4, 'năm' : 5, 'sáu' : 6, 'bảy' : 7, 'tám' : 8, 'chín' : 9, 'mười' : 10} |
Cấu trúc để khai báo hàm trong Python:
def tenham(<danh_sach_bien>):
<noi_dung_ham>
return <ket_qua>
Ví dụ:
def square(x): |
Hàm trả về nhiều giá trị
Một hàm có thể trả về nhiều giá trị, danh sách các giá trị trả về ở được ngăn cách nhau bởi dấu phảy
Ví dụ:
def calcAreaAndPerimeter(width, height): |
Trong trường hợp này, có thể hiểu kết quả trả về là một giá trị kiểu Tuple
Giá trị mặc định của tham số trong hàm
Khi khai báo hàm, có thể khai báo giá trị mặc định cho các tham số. Khi gọi hàm, nếu một tham số bị thiếu nhưng đã được khai báo giá trị mặc định trong khai báo hàm thì tham số đó sẽ được truyền vào giá trị mặc định.
Ví dụ:
def getAddress(street, city, country='Việt Nam'): |
Khi xây dựng một chương trình lớn, thông thường các chức năng sẽ được chia nhỏ thành các module để việc phát triển và bảo trì được dễ dàng.
Ví dụ, chương trình main.py như sau:
# main.py |
Khi chương trình lớn, thay vì việc để tất cả các hàm trong 1 file, chúng ta tạo các file nhỏ để chứa các hàm xử lý, ví dụ file add.py với nội dung sau:
# add.py |
Trong file main.py, chúng ta sử dụng lệnh import đến file add.py để gọi các hàm:
# main.py |
Ngoài việc import toàn bộ module như trên, có thể import từng hàm trong module với lệnh :
from <module> import <functions>
Ví dụ:
# main.py |
Ngoài ra, các biến khai báo trong module cũng có thể được import tương tự như các hàm.
Ví dụ :
# const.py |
Mở file
Lệnh mở file trong Python:
f = open(<file_name>, <mode>, encoding=<encoding>)
Trong đó :
Đọc file
data = f.read()
Nếu file được mở theo chế độ văn bản ('r'), kết quả đọc file là một biến string. Nếu file được mở theo chế độ nhị phân ('rb'), kết quả đọc file là một biến có kiểu dữ liệu bytes.
Nếu mở file theo chế độ văn bản ('r') có thể đọc nội dung file theo từng dòng với lệnh :
lines = f.readlines() |
Kết quả trả về là một List, mỗi phần tử là một string chứa nội dung các dòng của file
Nếu mở file theo chế độ văn bản ('r') có thể đọc từng dòng của file với cấu trúc:
for line in f: |
Ghi file:
f.write(<string>)
Ví dụ:
f = open('test.txt', 'w') |
f.write(<bytes>)
Ví dụ:
f = open('test.dat', 'wb') |
Đóng file
f.close() |
Truy xuất file với cấu trúc 'with'
Sau khi mở file với lệnh open, phải đóng file với lệnh close để tránh bị thiếu nội dung file khi kết thúc chương trình. Để tránh việc quên đóng file sau khi mở, có thể dùng cấu trúc with:
with open(<file_name>, <mode>, <encoding>) as f:
# Process file
Với cấu trúc with, file sẽ tự động đươc đóng khi chương trình chạy xong khối lệnh bên trong with.
Ví dụ:
with open('test.txt', 'w') as f: |
Một số ví dụ minh họa
Ví dụ 1:
Đọc vào một file văn bản, tạo ra một file văn bản mới chứa các dòng của file nguồn, bỏ đi các dòng trống.
File input.txt:
Một năm có 365 hoặc 366 ngày
Năm thường có 365 ngày
Năm nhuận có 366 ngày
File output.txt:
Một năm có 365 hoặc 366 ngày
Năm thường có 365 ngày
Năm nhuận có 366 ngày
infile = open('input.txt', encoding='utf-8') |
Ví dụ 2:
Một file csv chứa bảng điểm một môn học của các học sinh một lớp học. Mỗi dòng của file là thông tin điểm của một học sinh bao gồm : họ tên, điểm hệ số 1, điểm hệ số 2, điểm hệ số 3, các giá trị này ngăn cách nhau bởi dấu phảy. Viết chương trình để đọc file csv đầu vào và tạo ra một file csv mới có thêm cột điểm trung bình.
File input.csv
Nguyễn Văn An, 8, 7, 8
Nguyễn Văn Bình, 6, 6, 8
Nguyễn Thị Chi, 8, 8, 9
Lê Văn Cường, 8, 7, 9
Phạm Thu Trang, 7, 8, 8
File output.csv
Nguyễn Văn An, 8, 7, 8, 7.7
Nguyễn Văn Bình, 6, 6, 8, 7.0
Nguyễn Thị Chi, 8, 8, 9, 8.5
Lê Văn Cường, 8, 7, 9, 8.2
Phạm Thu Trang, 7, 8, 8, 7.8
fi = open('input.csv', encoding='utf-8') |
Ngoại lệ là các lỗi có thể phát sinh trong quá trình chạy chương trình. Ví dụ:
Khi một lỗi xảy ra, nếu không được xử lý, Python sẽ đưa ra thông báo lỗi và dừng chương trình.
Ví dụ:
print(int('a')) |
Chương trình trên khi chạy sẽ đưa ra thông báo:
ValueError: invalid literal for int() with base 10: 'a'
Cách xử lý ngoại lệ trong Python :
try: |
Khi ngoại lệ xảy ra, chương trình sẽ chạy vào khối lệnh bên dưới dòng except:, sau đó đoạn chương trình tiếp theo khối try/except vẫn tiếp tục được thực hiện.
Để xử lý riêng với từng loại ngoại lệ, có thể chỉ định rõ loại của ngoại lệ trong khai báo except:
try: |
Ví dụ:
try: |
Trong trường hợp muốn thực hiện một hành động sau khi kết thúc một công việc bất chấp ngoại lệ có xảy ra trong quá trình thực hiện công việc đó hay không, có thể dùng cấu trúc:
try: |
Khi một chương trình Python thực hiện ở chế độ mặc định của hệ thống, nó có thể sử dụng toàn bộ các thư viện đã được cài đặt (thông qua lệnh import ) Tuy nhiên điều này cũng có thể gây ra xung đột vì một số chương trình yêu cầu phiên bản cụ thể của một số thư viện (trong khi phiên bản đã được cài trong hệ thống có thể cũ hoặc mới hơn phiên bản mong muốn của chương trình). Trong trường hợp đó, Python cung cấp khả năng làm việc với môi trường ảo (Virtual environment) để tránh tình trạng xung đột thư viện trên. Mỗi môi trường ảo sẽ có tập các thư viện độc lập với nhau và không chia sẻ với thư viện mặc định của hệ thống.
Tạo mới Virtual environment:
mkdir project1
cd project1
python -m venv .
Activate Virtual environment:
Script\activate
Sau khi thực hiện lệnh activate trên, con trỏ dòng lệnh sẽ xuất hiện kí hiệu (project1) ở đầu:
(project1) C:<path_to_project_dir>
Khởi động Python ở cửa sổ dòng lệnh này và thử import một thư viện đã cài đặt trong hệ thống:
(project1) C:<path_to_project_dir> python
>>> import requests
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ModuleNotFoundError: No module named 'requests'
Chương trình sẽ đưa ra thông báo lỗi không tìm thấy thư viện kể cả thư viện đã được cài trong hệ thống trước đó hay không. Để cài đặt thư viện riêng cho Virtual Environment, sử dụng lệnh :
pip install <module>
Trong trường hợp cần chỉ định chính xác phiên bản của thư viện cần cài đặt:
pip install <module>==<version>
Ví dụ:
pip install requests==2.21.0
Trong Python, một Class thường có cấu trúc như sau:
class ClassName: |
Ví dụ:
class Person: |
Cú pháp để khởi tạo một đối tượng của một Class:
obj = ClassName(var1, var2, …)
Để truy nhập đến phương thức của đối tượng:
obj.method1(...)
obj.method2(...)
Để truy nhập đến thuộc tính đối tượng:
obj.attr1 |
Ví dụ:
person = Person('Nguyễn Văn An', 'Hà Nội') |
Để khai báo một class mới thừa kế từ một class đã có:
class ChildName(ParentClass): |
Lớp con sẽ kế thừa các thuộc tính và phương thức của lớp cha.
Ngoài ra lớp con có thể “ghi đè” phương thức đã có ở lớp cha bằng cách khai báo một phương thức cùng tên.
Ví dụ:
class Person: |
Thread được sử dụng để tạo ra một luồng xử lý song song với luồng xử lý chính của chương trình.
Ví dụ:
import time |
Chương trình trên tạo ra một thread chạy song song với chương trình chính. Trong chương trình chính, khi gặp lệnh time.sleep(5), chương trình sẽ ngừng trong 5 giây mà không thực hiện hoạt động nào. Tuy nhiên, bên trong thread các hoạt động vẫn được thực hiện, do đó khi chạy chương trình chúng ta vẫn thấy lệnh print bên trong thread in dữ liệu ra màn hình trong khoảng 5 giây ngừng hoạt động của chương trình chính .
Có thể tạo nhiều thread một lúc, khi đó các thread sẽ chạy song song với nhau. Ví dụ:
from threading import Thread |
Đồng bộ giữa các thread : Khi các thread chạy song song nhau, trình tự thực hiện các đoạn chương trình bên trong các thread sẽ không phụ thuộc lẫn nhau. Trong trường hợp cần đợi cho một thread thực hiện xong trước khi bắt đầu thực hiện các công việc khác, có thể sử dụng lệnh thread.join() như trong ví dụ sau:
import time |
Nhược điểm của thread:
Các đoạn code Python thông thường đều sử dụng GIL. Chỉ một số hàm của các thư viện của Python không sử dụng GIL như các hàm truy nhập IO (bàn phìm, File, Download, Upload qua internet …). Do đó về cơ bản, việc sử dụng nhiều Thread không giúp tăng tốc độ xử lý chương trình.
Process cũng giống như Thread cho phép xử lý các tác vụ song song với nhau, nhưng cho phép các công việc được thực hiện đồng thời trên nhiều core CPU một lúc.
Ví dụ:
from multiprocessing import Process |
Nếu so với cách sử dụng Thread, đoạn chương trình trên sẽ thực hiện nhanh hơn vì các tác vụ tính toán được thực hiện trên nhiều core CPU cùng lúc.
Nhược điểm của Process:
Ví dụ sau minh họa sự khác nhau giữa Thread và Process :
from threading import Thread |
Chương trình khi chạy sẽ in ra kết quả là [1000, 1000, 1000, 1000, 1000], đúng như mong đợi
from multiprocessing import Process |
Chương trình khi chạy sẽ in ra kết quả là [0, 0, 0, 0, 0]. Nguyên nhân là do biến a ở trong process là một biến mới, không phải biến a khai báo ở đầu chương trình.
Pool cũng giống như Process, tức cho phép các tác vụ xử lý được thực hiện trên nhiều core CPU. Pool thường được sử dụng thay cho Process khi cần tập hợp kết quả các Process với nhau vào một biến chung của chương trình chính (điều mà Process không làm được như ở ví dụ minh họa phía trên)
Ví dụ:
from multiprocessing import Pool |
Trong Python, để chuyển đổi giữa Python Object thành JSON string và ngược lại, có thể sử dụng 2 hàm của thư viện json:
Ví dụ:
import json |
import xml.etree.ElementTree as ET |
import xml.etree.cElementTree as ET |
import base64 |
import base64 |
Để đọc, ghi file csv, có thể sử dụng thư viện csv của Python.
import csv |
with open('output.csv', 'w', newline='', encoding='utf-8') as fo: |
Để thực hiện gọi API qua HTTP POST/GET, nên sử dụng thư viện requests:
pip install requests
Ví dụ 1 :
Lấy về nội dung một trang web:
import requests |
Ví dụ 2:
Sử dụng requests để gọi API dịch vụ của các platform.
Các platform như Google cloud, Amazon, Microsoft đều cung cấp các dịch vụ như tìm kiếm, bản đồ, AI (dịch, xử lý ảnh, nhận dạng chữ viết ,...). Thông thường các platform đều hỗ trợ việc gọi service qua Restful API. Trong trường hợp đó, có thể sử dụng thư viện requests để thực hiện gọi API.
Ví dụ sau minh họa cách dùng dịch vụ của IBM Watson để thực hiện dịch một văn bản từ một ngôn ngữ sang một ngôn ngữ khác. Trước hết, để truy nhập được dịch vụ của Watson, cần đăng ký tài khoản (miễn phí) tại link https://cloud.ibm.com/registration :
Hình 11 . Đăng ký tài khoản IBM Watson
Sau khi đăng ký xong tài khoản, xác nhận tài khoản và đăng nhập vào Watson, từ thanh tìm kiếm gõ “Translator” và chọn “Language Translator-sj” :
Hình 12 . Tìm dịch vụ Language Translator trong Watson
Khi vào màn hình của Language Translator, thực hiện đăng ký dịch vụ free, sau đó vào tab Manage để xem thông tin truy nhập dịch vụ : bao gồm API Key & URL
Hình 13 . Xem thông tin truy nhập dịch vụ tại tab Manage
Để xem hướng dẫn cách gọi service, click vào “Getting started tutorial”, Watson sẽ chuyển qua trang hướng dẫn
Hình 14 . Hướng dẫn sử dụng service của Watson
Để sử dụng dịch vụ dịch văn bản, Watson hướng dẫn cách gọi qua curl như sau:
curl -X POST -u "apikey:{apikey}" --header "Content-Type: application/json" --data "{\"text\": [\"Hello, world! \", \"How are you?\"], \"model_id\":\"en-es\"}" "{url}/v3/translate?version=2018-05-01"
Trong đó {apikey} và {url} là các thông tin truy nhập dịch vụ đã có ở phía trên. Trên linux, sử dụng lệnh curl ở trên sẽ nhận được kết quả:
{
"translations": [
{
"translation": "¡ Hola, mundo! "
},
{
"translation": "¿Cómo estás?"
}
],
"word_count": 5,
"character_count": 26
}
Trên windows, có thể dùng Postman để gọi dịch vụ với các thiết lập sau:
Hình 15 . Dùng postman để gọi service của Watson
Nếu muốn gọi service một cách tự động, hoặc gọi nhiều lần, cần sử dụng đến thư viện REST Client của các ngôn ngữ lập trình. Với python, việc này được thực hiện như ở chương trình dưới đây:
import requests |
File cấu hình (tên mở rộng .ini) chứa các thông tin khởi tạo trước khi bắt đầu chương trình. Một file cấu hình thường có nhiều Section, tên mỗi section được đặt trong cặp ngoặc [], bên dưới mỗi Section là các trường thông tin để ở dạng <key>=<value>. Ví dụ, file server.ini chứa thông tin về môi trường deploy server có dạng như sau:
[PROD]
port = 8080
log_level=WARN
[DEV]
port = 8081
log_level=DEBUG
import configparser |
import configparser |
Để lấy tham số từ dòng lệnh, có thể sử dụng thư viện sys. Với một chương trình, danh sách tham số truyền vào dòng lệnh được lấy từ biến:
sys.argv : ~ một list string, mỗi phần tử là một tham số dòng lệnh
Trong đó sys.argv[0] luôn là tên file .py được chạy, sys.argv[1] trở đi là các tham số truyền vào chương trình.
Ví dụ :
import sys |
Các lệnh thường dùng khi thao tác với file và thư mục:
import os |
import os |
import os |
import os |
import os |
import os |
import os |
import os |
import os |
import os |
import os |
import os |
import subprocess |
import subprocess |
TCP-IP là giao thức cho phép kết nối giữa nhiều Client và một Server. Khi kết nối được hình thành, Client và Server có thể trao đổi dữ liệu với nhau qua các packet. Quá trình kết nối giữa client và server được diễn ra theo các bước sau:
Ví dụ kết nối TCP/IP với Python:
import socket |
import socket |
Việc gửi mail trong Python được thực hiện nhờ thư viện smtp
import smtplib, ssl |
Lưu ý : Để gửi mail tự động được với gmail cần bật tính năng cho phép ứng dụng ít bảo mật của google.
Việc viết Unit Test là tương đối quan trọng vì đảm bảo các chức năng cơ bản thực hiện theo đúng logic đã được thiết kế, giảm trừ các lỗi phát sinh trong quá trình phát triển sản phẩm về sau.
Trong Python, việc viết Unit Test thường sử dụng thư viện unittest, với lớp đối tượng unittest.TestCase
Ví dụ:
import unittest |
Lựa chọn theo đúng phiên bản Python và kiến trúc cpu (32 bit/64bit) của phiên bản Python đã cài đặt.
Mở cửa sổ cmd, di chuyển vào trong thư mục chứa file thư viện đã download, gõ lệnh sau để cài đặt thư viện:
pip install <tên file thư viện đã download>
import MySQLdb |
Các tham số cần truyền vào để kết nối đến database theo thứ tự là : host, username, password, dbname
import MySQLdb |
import MySQLdb |
import MySQLdb |
pip install flask
>> import flask
>> print(flask.__version__)
Để bắt đầu xây dựng ứng dụng với flask, tạo một file app.py có nội dung như dưới đây:
from flask import Flask
app = Flask(__name__)
@app.route('/')
def hello_world():
return 'Hello World'
if __name__ == '__main__':
app.run()
Khởi động ứng dụng như một chương trình python bình thường:
python app.py
Từ cửa sổ trình duyệt (Edge/Chrome/Firefox) truy nhập ứng dụng theo địa chỉ http://localhost:5000 :
Hình 16. Truy nhập ứng dụng flask tại địa chỉ http://localhost:5000
Khi một ứng dụng web chạy, server sẽ lắng nghe tại một địa chỉ (IP) và cổng (Port). Với flask, ứng dụng sẽ sử dụng địa chỉ IP mặc định là 127.0.0.1 (localhost) và port mặc định là 5000. Để thay đổi các giá trị IP và port này, có thể sử dụng các tham số truyền vào từ hàm khởi tạo ứng dụng:
app.run(host='<IP>', port=<port>)
Các tham số:
Ví dụ:
app.run(host='0.0.0.0', port=8080)
Khi truy nhập ứng dụng web theo url (nhập vào trình duyệt), mỗi endpoint tương ứng với phần sau của url truy nhập (sau khi đã bỏ đi IP và port). Mỗi endpoint sẽ thực hiện một chức năng khác nhau và trả về kết quả tương ứng cho người dùng.
Để khai báo endpoint trong flask, sử dụng kí hiệu @app.route('<endpoint>'), ví dụ:
from flask import Flask
app = Flask(__name__)
@app.route('/hello')
def hello_world():
return 'Hello World'
if __name__ == '__main__':
app.run()
Sau khi chạy chương trình trên, nếu từ trình duyệt truy nhập vào địa chỉ http://localhost:5000 sẽ gặp lỗi Not found, do endpoint mặc định '/' đã không được khai báo trong chương trình. Để sử dụng ứng dụng, phải truy nhập vào địa chỉ của endpoint mới là : http://localhost:5000/hello
Một ứng dụng flask có thể khai báo nhiều endpoint để phục vụ các chức năng khác nhau cho người dùng truy nhập đến:
from flask import Flask
app = Flask(__name__)
@app.route('/')
def homePage():
return 'Home page'
@app.route('/hello')
def hello_world():
return 'Hello World'
@app.route('/goodbye')
def goodbye():
return 'Goodbye, see you later.'
if __name__ == '__main__':
app.run()
Với ứng dụng trên, có thể truy nhập đến các URL tương ứng với các endpoint đã khai báo : http://localhost:5000, http://localhost:5000/hello, http://localhost:5000/goodbye
Trong ứng dụng web, với cùng một endpoint truy nhập, người dùng vẫn có thể truyền lên server các tham số khác nhau. Có thể hiểu đây là các tham số sẽ được truyền vào hàm xử lý của endpoint.
Để thực hiện truyền tham số từ trình duyệt (client) lên server, có 2 phương thức truyền:
http://localhost:5000/hello?name=NVA&address=Hanoi
Các cặp giá trị ngăn cách nhau bởi kí tự &, toàn bộ phần tham số được ngăn cách với URL gốc của endpoint bằng dấu ?
<form action='http://localhost:5000/hello' method='POST'>
<p>Name : <input name='name'></p>
<p>Address : <input name='address'></p>
<input type='submit'>
</form>
Để lấy tham số do client truyền lên endpoint xử lý của flask, trước hết cần khai báo sử dụng module request của flask :
from flask import Flask, request
Các tham số do client gửi lên server được lấy qua đối tượng request.args , đối tượng này có kiểu dữ liệu là dictionary. Ví dụ:
from flask import Flask, request
app = Flask(__name__)
@app.route('/hello')
def hello_world():
name = request.args.get('name', '')
return f'Hello {name}'
if __name__ == '__main__':
app.run()
Để sử dụng ứng dụng trên, truyền tham số name vào phía cuối của URL truy nhập endpoint, ví dụ:
http://localhost:5000/hello?name=Nguyễn+Văn+An
Các tham số do client gửi lên server được lấy qua đối tượng request.form , đối tượng này có kiểu dữ liệu là dictionary. Ví dụ:
from flask import Flask, request
app = Flask(__name__)
@app.route('/hello', methods=['POST'])
def hello_world():
name = request.form.get('name', '')
return f'Hello {name}'
if __name__ == '__main__':
app.run()
Để sử dụng ứng dụng trên, tạo mới một file test.html với nội dung sau:
<form action='http://localhost:5000/hello' method='POST'>
<p>Name : <input name='name'></p>
<input type='submit'>
</form>
Dùng trình duyệt mở file test.html trên (Từ File Explorer → Open with → Edge/Chrome/FireFox), trong màn hình ứng dụng trên trình duyệt, nhập giá trị cho trường input, sau đó chọn submit :
Hình 17. Mở file html bằng trình duyệt
Sau khi submit xong, trình duyệt sẽ nhận được kết quả do server trả về:
Hình 18. Kết quả do server trả về sau khi submit form
Thông thường mã hóa dữ liệu khi gọi POST method có dạng chuẩn là form-data. Tuy nhiên, trong một số trường hợp, ví dụ giao tiếp giữa các server hoặc gọi service từ client theo dạng AJAX thì dạng mã hóa dữ liệu thường sử dụng là JSON. Trong trường hợp này để lấy tham số trong request, chúng ta thay đối tượng request.form bằng đối tượng request.json :
from flask import Flask, request
app = Flask(__name__)
@app.route('/hello', methods=['POST'])
def hello_world():
name = request.json.get('name', '')
return f'Hello {name}'
if __name__ == '__main__':
app.run()
Để gọi ứng dụng trên, có thể dùng curl hoặc postman :
Sử dụng curl :
curl -X POST -H "Content-Type: application/json" --data "{\"name\":\"Nguyễn Văn An\"}" http://localhost:5000/hello
Sử dụng Postman:
Hình 19. Sử dụng postman để gọi POST method của flask
Trường hợp tổng quát, một endpoint có thể hỗ trợ nhiều phương thức (GET/POST) và nhiều dạng mã hóa dữ liệu, để lấy tham số từ request, chúng ta cần kiểm tra phương thức & loại mã hóa dữ liệu mà client gửi tới server:
from flask import Flask, request
app = Flask(__name__)
@app.route('/hello', methods = ['POST', 'GET'])
def hello_world():
if request.method == 'POST':
if 'name' in request.form:
name = request.form['name']
return f'Hello {name} from POST method (form-data)'
elif 'name' in request.json:
name = request.json['name']
return f'Hello {name} from POST method (JSON)'
else:
return 'Hello from POST method'
else:
name = request.args.get('name', '')
return f'Hello {name} from GET method'
if __name__ == '__main__':
app.run()
Ngoài cách lấy tham số từ request, có thể mapping tham số trong khai báo url. Sử dụng cách này, tham số do client gửi lên sẽ được chuyển thành tham số trong hàm xử lý của endpoint. Ví dụ:
from flask import Flask
app = Flask(__name__)
@app.route('/hello/<name>')
def hello(name):
return f'Hello {name}'
if __name__ == '__main__':
app.run(debug = True)
Để sử dụng ứng dụng, truyền tham số name vào cuối URL theo cấu trúc : http://localhost:5000/hello/<name>, ví dụ:
http://localhost:5000/hello/Nguyễn Văn An
Khi endpoint trả về kết quả dưới dạng string, nếu client là trình duyệt thì nội dung này sẽ được hiện lên trong cửa sổ trình duyệt để người dùng xem. Trên thực tế, nội dung trả về thường khá dài (kèm theo style và format) do đó phía server sẽ không trả về kết quả dưới dạng một biến string, mà trả về nội dung thông qua các file template với tên mở rộng html.
Thư mục chứa template có tên là templates và đặt ngang hàng với file chạy ứng dụng (app.py) , ví dụ:
app.py
templates/
Index.html
Chương trình sử dụng template để trả về nội dung html cho client:
from flask import Flask, render_template
app = Flask(__name__)
@app.route('/')
def hello():
return render_template('index.html')
if __name__ == '__main__':
app.run()
File index.html đặt trong thư mục templates và chứa nội dung muốn trả về cho client, ví dụ:
<html>
<body>
<b>Hello, world!</b>
</body>
</html>
Trong hàm xử lý endpoint thường thực hiện một số tính toán (truy nhập database, file, …), sau đó sẽ tạo ra một số biến để chứa dữ liệu kết quả. Các biến này cần được truyền xuống template để tạo ra nội dung tùy biến cho mỗi lần request khác nhau từ người dùng. Việc truyền biến từ hàm xử lý endpoint (còn gọi là controller), xuống các file template (còn gọi là lớp view), được thực hiện bằng cách truyền biến trực tiếp vào hàm render_template. Ví dụ :
from flask import Flask, render_template
app = Flask(__name__)
@app.route('/hello/<user>')
def hello_name(user):
return render_template('index.html', user=user)
if __name__ == '__main__':
app.run()
Trong template, nội dung các biến từ server truyền xuống sẽ được đặt trong cặp dấu {{ }}, ví dụ file index.html ứng với chương trình phía trên cần có nội dung sau:
<html> |
Để sử dụng ứng dụng trên, từ trình duyệt truy nhập đến địa chỉ http://localhost:5000/hello/<name> , chương trình sẽ hiện ra lời chào theo tham số <name> đã truyền vào.
Ngoài cấu trúc lấy giá trị biến được truyền xuống template như trên, trong template còn có các cấu trúc lệnh tương tự với chương trình python thông thường. Hai cấu trúc thường xuyên sử dụng là :
Cấu trúc điều khiển if:
{% if <cond> %}
<html content>
{% else %}
<html content>
{% endif %}
Cấu trúc lặp for:
{% for item in item_list %}
{{ item }}
{% endfor %}
Ví dụ : Chương trình dưới đây trả về một danh sách sinh viên trong nội dung hiển thị:
from flask import Flask, render_template
app = Flask(__name__)
@app.route('/get_students')
def getStudents():
students = [
{'id' : '100001', 'name' : 'Nguyễn Văn An', 'address' : 'Hà Nội'},
{'id' : '100002', 'name' : 'Nguyễn Thị Bình', 'address' : 'TP. HCM'}
]
return render_template('students.html', students=students)
if __name__ == '__main__':
app.run(debug = True)
File app.py
{% if students %}
<table border='1'>
<tr>
<th>Id</th>
<th>Name</th>
<th>Address</th>
</tr>
{% for student in students %}
<tr>
<td> {{ student.id }} </td>
<td> {{ student.name }} </td>
<td> {{ student.address }} </td>
</tr>
{% endfor %}
</table>
{% else %}
No students
{% endif %}
File templates/students.html
Truy nhập ứng dụng từ trình duyệt với địa chỉ http://localhost:5000/get_students, màn hình hiển thị sẽ có dạng như ở dưới đây:
Hình 20. Màn hình ứng dụng sử dụng các cấu trúc điều khiển & lặp trong template để tạo nội dung hiển thị
Khi đang trong một endpoint, thay vì chạy đến hết quá trình xử lý mà chúng ta muốn chuyển qua một url khác thì có thể sử dụng hàm redirect . Hàm này nhận tham số là url cần chuyển hướng tới. Ví dụ :
from flask import Flask, request, redirect, url_for
app = Flask(__name__)
@app.route('/hello/<name>')
def hello(name):
return f'Hello {name}'
@app.route('/')
def homePage():
user = request.args.get('user', 'world')
return redirect(url_for('hello', name=user))
if __name__ == '__main__':
app.run()
Trong chương trình trên, hàm url_for của flask có tác dụng tạo ra url dựa trên tên hàm của một endpoint xử lý và các tham số đi kèm với hàm.
Truy nhập ứng dụng tại địa chỉ : http://localhost:5000?user=<user>, chương trình sẽ được chuyển qua địa chỉ http://localhost:5000/hello/<user>
Trong trường hợp xảy ra lỗi trong hàm xử lý endpoint, để kết thúc nhanh chương trình, có thể dùng hàm abort, kèm theo mã lỗi. Ví dụ :
from flask import Flask, request, redirect, url_for |
Ở ứng dụng trên, nếu truy nhập vào địa chỉ http://localhost:5000/hello?name=<name> , chương trình sẽ đưa ra lời chào theo tham số name đã truyền vào. Nếu truy nhập vào địa chỉ http://localhost:5000/hello, chương trình sẽ không xác định được tham số name và đưa ra thông báo lỗi.
Ở html form, để thực hiện upload file, cần thêm thuộc tính enctype='multipart/form-data'. Ở phía server, trong hàm xử lý endpoint, các file do client upload lên có thể lấy được từ đối tượng request.files. Đối tượng này có kiểu dữ liệu là dictionary.
Ví dụ:
from flask import Flask, request, render_template
app = Flask(__name__)
@app.route('/upload', methods=['POST'])
def uploadFile():
file = request.files.get('file')
if file and file.filename != '':
file.save(file.filename)
return 'File uploaded'
@app.route('/')
def homePage():
return render_template('index.html')
if __name__ == '__main__':
app.run()
File app.py
<form method='POST' enctype='multipart/form-data' action='/upload'>
<input type='file' name='file'>
<input type='submit'>
</form>
File templates/index.html
Để sử dụng ứng dụng trên, truy nhập địa chỉ http://localhost:5000, màn hình upload file sẽ hiện lên :
Hình 21. Màn hình upload file
Sau khi chọn file và submit form, chương trình sẽ đưa ra thông báo file đã upload thành công:
Hình 22. Thông báo file đã upload thành công
File do client gửi lên sẽ được lưu trong thư mục chạy ứng dụng (thư mục chứa file app.py)
Flask_sqlalchemy (dựa trên sql_alchemy) là thư viện cho phép truy nhập database theo dạng ORM (Object relational mapping). Khi sử dụng ORM, việc truy nhập database được thực hiện nhờ các lệnh python và mỗi đối tượng trong database được thể hiện bằng một Object class của python.
Cài đặt flask_sqlalchemy :
pip install flask_sqlalchemy
Việc cấu hình database được thực hiện bằng lệnh:
app.config['SQLALCHEMY_DATABASE_URI'] = '<db_url>'
db = SQLAlchemy(app)
Trong đó db_url là url chứa thông tin về database sử dụng (engine, IP, port, user, password). Một số URL tương ứng với các database engine thông dụng:
Ngoài Sqlite thì với các engine database khác, cần cài đặt thư viện database client tương ứng để có thể kết nối được với database. Các thư viện cần cài đặt:
Các model database trong flask_sqlalchemy cần kế thừa class db.Model trong đó db là biến đã được khai báo trong phần cấu hình database ở bước trước. Ví dụ:
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///students.db'
db = SQLAlchemy(app)
class Student(db.Model):
id = db.Column(db.Integer, primary_key = True)
studentNo = db.Column('student_no', db.String(20), unique=True)
studentName = db.Column('student_name', db.String(50))
address = db.Column('address', db.String(50))
def __init__(self, studentNo, studentName, address):
self.studentNo = studentNo
self.studentName = studentName
self.address = address
Ở chương trình trên, chúng ta khai báo một model Student với các trường:
>>> from app import db
>>> db.create_all()
>>> from app import Student
>>> student1 = Student('101', 'Nguyễn Văn An', 'Hà Nội')
>>> student2 = Student('102', 'Nguyễn Thị Bình', 'TP.HCM')
>>> db.session.add(student1)
>>> db.session.add(student2)
>>> db.session.commit()
>>> students = Student.query.all()
>>> for student in students:
>>> print(student.id, student.studentNo, student.studentName, student.address)
>>> student1 = Student.query.get(1)
>>> print(student1.id, student1.studentNo, student1.studentName, student1.address)
>>> student1 = Student.query.get(1)
>>> student1.address = 'TP. Hà Nội'
>>> db.session.commit()
>>> student1 = Student.query.get(1)
>>> db.session.delete(student1)
>>> db.session.commit()
pip install django
Từ cửa sỗ cmd, chạy python, sau đó kiểm tra phiên bản django đã cài đặt với các lệnh:
>> import django
>> print(django.get_version())
django-admin startproject mysite
Project mới sẽ được tạo ra trong thư mục mysite với các file như sau:
mysite/
manage.py
mysite/
__init__.py
settings.py
urls.py
wsgi.py
Từ cửa sổ cmd, di chuyển vào trong thư mục project (mysite) và khởi động server với lệnh:
cd mysite
python manage.py runserver
Hình 23 . Khởi động server django
Theo mặc định, server sẽ lắng nghe tại địa chỉ http://127.0.0.1:8000
Hình 24. Truy cập ứng dụng web django tại địa chỉ http://127.0.0.1:8000
Để thay đổi địa chỉ ip và cổng mà server lắng nghe, có thể dùng lệnh:
python manage.py runserver <ip:port>
Ví dụ:
python manage.py runserver 8080 # nghe tại cổng 8080
python manage.py runserver 0.0.0.0:8080 # nghe tại cổng 8080, tất cả ip
python manage.py runserver 0:8080 # nghe tại cổng 8080, tất cả ip
Mỗi project django thường được chia thành nhiều ứng dụng nhỏ (app). Để tạo mới một ứng dụng trong một project django, sử dụng lệnh:
python manage.py startapp <app>
Trong đó <app > là tên ứng dụng muốn tạo mới. Sau khi được tạo mới, cấu trúc thư mục ứng dụng như sau:
app/
admin.py
apps.py
migrations/
__init__.py
models.py
tests.py
views.py
__init__.py
Các thành phần trong ứng dụng:
Ngoài các file trên, cần tạo thêm một thư mục với tên templates đặt trong thư mục ứng dụng để chứa các file tempate html cho tầng view. Cấu trúc thư mục ứng dụng sẽ như sau:
app/
admin.py
apps.py
migrations/
__init__.py
models.py
tests.py
views.py
__init__.py
templates/
*.html
Sau khi tạo xong ứng dụng, cần sửa file settings.py của project để thêm khai báo tên ứng dụng vào mục INSTALLED_APPS :
INSTALLED_APPS = [
'app', # new
….
Cấu hình database :
Database engine và các thông tin kết nối sử dụng trong project hiện tại được lưu trong phần DATABASES của file settings.py.Theo mặc định, database engine được sử dụng là sqlite, toàn bộ database của project được chứa trong file db.sqlite3 nằm trong thư mục gốc của project:
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
}
}
Để chuyển sang sử dụng các database engine khác, trước hết cần cài đặt driver cho database engine. Ví dụ, cài đặt driver cho MySQL :
pip instal MySQLdb
Sau đó bổ sung các thông tin kết nối database trong file settings.py :
DATABASES = { |
Sau khi cấu hình database xong, thực hiện khởi tạo database cho ứng dụng với lệnh:
python manage.py migrate
Django sử dụng ORM để tương tác với Database. Các model Database được khai báo trong file models.py của ứng dụng và cần kế thứa lớp django.db.models.Model.
Ví dụ:
from django.db import models |
Khai báo một đối tượng trong file models.py
Sau khi khai báo đối tượng xong, để update các thay đổi mới vào database, cần thực hiện lệnh:
python manage.py makemigrations app
python manage.py migrate
Sau khi thực hiện lệnh trên, django sẽ tạo ra các bảng trong database như sau:
Hình 25. Các bảng được django tạo ra sau khi thực hiện lệnh migrate
Việc truy xuất đến database được thực hiện thông qua lớp đại diện Student (Object Relational Mapper). Để test việc truy xuất dữ liệu từ cửa sổ cmd, có thể sử dụng lệnh:
python manage.py shell
Một số thao tác truy xuất database cơ bản:
from app.models import Student
Student.objects.all()
Student.objects.create(studentNo='10001', studentName='Nguyễn Văn An', address='Hà Nội')
student = Student.objects.get(pk=1)
student = Student.objects.get(pk=1)
student.address = 'TP.Hà Nội'
student.save()
students = Student.objects.filter(address='TP.Hà Nội', studentName__startswith='Nguyễn')
Với các trường dữ liệu String, ngoài phép so sánh giống nhau ( <field>=<value> ), có thể sử dụng các phép so sánh:
students = Student.objects.filter(address='TP.Hà Nội').order_by('studentNo') # ASC
students = Student.objects.filter(address='TP.Hà Nội').order_by('-studentNo') # DESC
students = Student.objects.raw('Select * from app_student where address=%s and student_name LIKE %s', ['TP.Hà Nội', '%Nguyễn%'])
student = Student.objects.get(pk=1)
student.delete()
Các thành phần trong mô hình MVC của Django như sau:
Hình 26. Các thành phần trong mô hình MVC của ứng dụng Django
Khai báo url:
Trước hết cần khai báo địa chỉ url cho trang. Đây là địa chỉ sẽ được dùng để truy nhập đến trang. Việc khai báo được thực hiện trong file urls.py của project, tuy nhiên để dễ quản lý, thường tạo mới file urls.py trong thư mục của từng app, sau đó include file urls.py của app vào trong file urls.py của projects.
from django.contrib import admin |
File urls.py của project (mysite/urls.py)
from django.urls import path |
File urls.py của ứng dụng (app/urls.py)
Tạo hàm xử lý trong views.py:
from django.shortcuts import render Tạo hàm xử lý trong views.py |
Tạo template html:
Tạo file hello.html trong thư mục app/templates với nội dung sau:
Hello world!
File hello.html (trong thư mục app/templates)
Khởi động server, truy nhập địa chỉ http://localhost:8000/app/hello
Hình 27. Màn hình một trang được tạo theo mô hình MVC của django
Các biến từ controller truyền xuống template sẽ lưu trong biến context truyền vào hàm render trong phần controller:
from django.shortcuts import render |
File views.py : Truyền biến từ controller xuống template qua context
Hello {{ name }}!
File hello.html : Sử dụng biến từ controller truyền xuống trong file template
Để sử dụng biến do controller truyền xuống trong file template, cần sử dụng các cấu trúc điều khiển của ngôn ngữ template của django. Một số cấu trúc thông dụng của ngôn ngữ template:
{{ bien_so }}
{% if <dieu_kien> %}
<html>
{% else %}
<html>
{% endif %}
{% for item in item_list %}
{{ item }}
{% endfor %}
Ví dụ sau minh họa cách tạo một trang hiển thị danh sách học sinh trong hệ thống:
from django.urls import path |
File app/urls.py
from django.shortcuts import render |
File app/views.py
{% if student_list %} |
File app/templates/list_student.html
Tham số về chi tiết đối tượng (thường là id) có thể được mapping trong đường dẫn của url.
Ví dụ:
from django.urls import path |
File app/urls.py
from django.shortcuts import render, get_object_or_404 |
File app/views.py
<p> Student Number : {{ student.studentNo }} </p> |
File app/templates/student_detail.html
Với GET method, tham số được lấy từ request.GET['param'], với POST method tham số được lấy từ request.POST['param']
Ví dụ:
from django.urls import path |
File app/urls.py
from django.shortcuts import render |
File app/views.py
Hello {{ name }} |
File app/templates/hello.html
Đối với form dạng multipart (chứa file), để lấy file từ client upload lên, sử dụng request.FILES.get['file_variable']
Ví dụ:
from django.urls import path |
File app/urls.py
import os |
File app/views.py
<form method="post" enctype='multipart/form-data'> |
File app/templates/upload.html
Khi cần tạo form nhập liệu với nhiều trường thông tin, cách tốt nhất là sử dụng form của django vì sẽ hỗ trợ việc validate và lấy giá trị biến từ html form một cách tự động.
Để sử dụng form của django, cần tạo một file với tên forms.py trong thư mục ứng dụng, trong file này khai báo các form kế thừa lớp django.forms.Form
Ví dụ sau minh họa cách sử dụng form của django để tạo một trang thêm mới thông tin học sinh :
from django.urls import path |
File app/urls.py
from django.db import models |
File app/models.py
from django import forms
class StudentForm(forms.Form):
studentNo = forms.CharField(label='Student Number', max_length=20)
studentName = forms.CharField(label='Student Name', max_length=50)
address = forms.CharField(label='Address', max_length=100)
File app/forms.py
import os |
File app/views.py
<form method="post"> |
File app/templates/add_student.html
<p> Student Number : {{ student.studentNo }} </p> |
File app/templates/student_detail.html
Validate các trường trong form:
Ngoài các validator có sẵn của django như require, min_length, max_length, … có thể bổ sung các validator tùy chọn cho từng trường thông tin hoặc cho cả form.
Để thêm validator cho một trường, chúng ta thêm hàm clean_<filed_name> vào trong form để thực hiện kiểm tra giá trị của trường. Để thêm validator cho toàn bộ form, chúng ta dùng hàm clean cho toàn bộ form
Ví dụ:
from django import forms |
File app/forms.py
Sử dụng custom html cho form:
Việc render form theo cú pháp {{ form }} trong template rất nhanh và thuận tiện, tuy nhiên trong nhiều trường hợp chúng ta phải thực hiện render form theo cấu trúc html riêng, khi đó có thể sử dụng các thẻ html thông thường và dùng {{ form.<field>.value }} để truy nhập tới từng trường dữ liệu của form.
<form method="post"> |
File app/templates/add_student.html
Theo mặc định, django cung cấp giao diện admin tại địa chỉ http://127.0.0.1:8000/admin/.
Hình 28. Đăng nhập vào màn hình admin của django
Để tạo tài khoản admin cho django, sử dụng lệnh :
python manage.py createsuperuser
Sau khi có tài khoản admin và đăng nhập, màn hình admin của django sẽ có dạng như sau:
Hình 29. Màn hình admin của django sau khi đăng nhập
Giao diện admin cho phép thêm mới/ chỉnh sửa/ xóa các đối tượng dữ liệu do django quản lý như User, Group. Ngoài ra, giao diện admin còn cho phép thêm các đối tượng dữ liệu mà các ứng dụng tạo ra vào danh sách quản lý để thực hiện các chức năng thêm mới/ chỉnh sửa/ xóa một cách nhanh chóng.
Ví dụ:
Tạo đối tượng Student trong file models.py như sau :
from django.db import models |
File app/models.py
Đăng ký đối tượng trong admin.py :
from django.contrib import admin |
File app/admin.py
Sau khi khởi động lại server và đăng nhập lại, đối tượng Student sẽ xuất hiện trong màn hình admin :
Hình 30. Màn hình admin của django sau khi thêm đăng ký đối tượng Student
Sau khi đã đăng ký đối tượng, có thể dùng màn hình admin để thực hiện tạo mới và sửa thông tin các bản ghi trong bảng Student :
Hình 31. Dùng màn hình admin để thêm mới/chỉnh sửa bản ghi
Trước hết, tạo mới một project:
django-admin startproject mysite
cd mysite
python manage.py migrate
Các chức năng quản lý người dùng của django nằm trong module django.contrib.auth. Các url mặc định để thực hiện các chức năng login/logout của module này gồm :
Để sử dụng các url này, chúng ta thêm khai báo trong file urls.py của project như sau:
from django.contrib import admin |
Theo mặc định, django sẽ tìm kiếm template cho các chức năng quản lý User tại thư mục templates/registration. Do đó chúng ta tạo mới thư mục templates/registration trong thư mục gốc của project, sau đó tạo mới các file html như sau:
mysite/
templates
home.html
registration
login.html
signup.html
File template home.html được tạo ra để chứa nội dung trang mặc định sau khi login/logout. Ngoài ra phần đăng ký mới người dùng (signup) cũng không nằm trong django.contrib.auth do đó chúng ta cũng phải tạo mới chức năng này. Việc thực hiện đăng ký url cho các chức năng này trong file urls.py của project như sau:
from django.contrib import admin |
Trong file settings.py của project, thêm ở cuối file khai báo sau:
LOGIN_REDIRECT_URL = 'home' |
Đồng thời update cấu hình của thư mục chứa templates :
TEMPLATES = [ |
Tạo màn hình đăng ký (file signup.html):
<h2>Sign up</h2> |
File templates/registration/signup.html
Tạo file mysite/views.py để thêm hàm xử lý view cho chức năng đăng ký người dùng :
from django.contrib.auth import login, authenticate |
File mysite/views.py
Tạo màn hình đăng nhập (file login.html):
<h2>Login</h2> |
File templates/registration/login.html
Tạo màn hình trang chủ (file home.html):
{% if user.is_authenticated %} |
File templates/home.html
Khởi động server, truy nhập địa chỉ http://localhost:8000
Hình 32. Màn hình trang chủ khi chưa đăng nhập
Sử dụng chức năng signup để tạo tài khoản mới:
Hình 33. Màn hình tạo tài khoản mới
Sau khi tạo tài khoản xong, chương trình sẽ tự redirect về trang chủ
:
Hình 34. Màn hình trang chủ sau khi đăng nhập
Tích hợp chức năng đăng nhập cho ứng dụng:
Đối với các trang của ứng dụng, phần template có thể sử dụng điều kiện {{ user.is_authenticated }} để biết người dùng đã đăng nhập hay chưa. Đối với các hàm của views.py, có thể sử dụng ký hiệu @login_required để bắt buộc đăng nhập với các chức năng cần bảo mật.
Ví dụ, thêm một view mới với tên loginRequiredView như sau:
from django.contrib import admin |
File urls.py
from django.contrib.auth import login, authenticate |
File mysite/views.py
You logged in as {{ user.username }} |
File templates/login_required.html
Khởi động server và truy nhập các địa chỉ http://localhost:8000/login-required-view, nếu chưa đăng nhập, chương trình sẽ chuyển qua màn hình đăng nhập trước sau đó mới cho phép xem nội dung trang này.
Django-rest-framework (DRF) là framework cho phép tạo ra các Restful webservice một cách nhanh chóng, phục vụ cho các ứng dụng Single Page Application (SPA) như Angular/React/Vue.
Cài đặt DRF:
pip install djangorestframework
Tạo mới project:
django-admin startproject mysite
cd mysite
python manage.py migrate
Tạo mới một ứng dụng trong project:
python manage.py startapp app
Thêm ứng dụng vừa tạo vào file settings.py của project:
INSTALLED_APPS = [ |
Tạo mới webservice trong file views.py của project:
from rest_framework.decorators import api_view |
File app/views.py
Thêm cấu hình url cho webservice:
from django.urls import path |
File app/urls.py
from django.contrib import admin |
File urls.py của project
Khởi động ứng dụng và truy cập api tại địa chỉ http://localhost:8000/app/hello, chúng ta sẽ thấy nội dung như sau:
Hình 35 .Test thử webservice từ trình duyệt
Ví dụ:
from rest_framework.decorators import api_view |
Cách lấy dữ liệu từ request với POST và GET method (file app/views.py)
Ngoài việc sử dụng kí hiệu @api_view để tạo endpoint, có thể sử dụng View Class để tạo endpoint. View Class cần kế thừa class rest_framework.views.APIView và cần cung cấp các hàm get, post, … tương ứng với các endpoint xử lý cho các method.
Ví dụ:
from rest_framework.views import APIView |
Cách Sử dụng View Class để tạo API (file app/views.py )
from django.urls import path |
Thực hiện mapping url cho View Class (file app/urls.py)
Serializer được dùng để chuyển đổi giữa JSON object và database object.
Ví dụ:
Xây dựng ứng dụng webservice để quản lý thông tin học sinh.
Tạo mới project và ứng dụng:
django-admin startproject mysite
cd mysite
python manage.py migrate
python manage.py startapp students
Thêm ứng dụng vào setting.py của project :
INSTALLED_APPS = [ |
Tạo database model trong file students/models.py:
from django.db import models |
File students/models.py
Cập nhập database:
python manage.py makemigrations students
python manage.py migrate
Tạo mới file students/serializers.py với nội dung sau:
from rest_framework import serializers |
File students/serializers.py
Class StudentSerializer kế thừa class ModelSerializer của DRF, có nhiệm vụ thực hiện chuyển đổi từ database model sang JSON object và ngược lại.
Tạo các api xử lý dữ liệu trong file students/views.py :
from rest_framework.views import status |
File students/views.py
Việc sử dụng Serializer giúp cho việc chuyển đổi giữa database model sang JSON thực hiện nhanh chóng, ví dụ :
StudentSerializer(student).data |
Sẽ tương đương với :
{ |
Cuối cùng, thêm mapping url cho các API vừa tạo:
from django.contrib import admin |
File urls.py của project
from django.urls import path |
File students/urls.py
Khởi động ứng dụng và sử dụng postman để test các API:
Hình 36 .Dùng postman test API tạo mới bản ghi
Hình 37 .Dùng postman test API lấy bản ghi theo id
Cross-Origin Resource Sharing (CORS) là việc cho phép gọi webservice (từ trình duyệt) giữa các website có origin (tên miền/ip) khác nhau. Thông thường việc gọi webservice từ một địa chỉ (ví dụ http://www.site1) sang một địa chỉ ở site khác (ví dụ http://www.site2) sẽ bị trình duyệt chặn lại nếu trong response trả về từ site 2 không có thông tin chỉ ra rằng có thể được phép gọi service từ site 1 đến.
Để cho phép các site khác địa chỉ gọi service đến ứng dụng, cần đặt cấu hình CORS cho ứng dụng server chỉ rõ những site nào sẽ được phép gọi service đến.
Với Django, việc này được thực hiện như sau:
Cài đặt thư viện django-cors-headers:
pip install django-cors-headers
Thêm 'corsheaders' vào danh sách ứng dụng trong file settings.py của project:
INSTALLED_APPS = [
...
'corsheaders',
...
]
Bổ sung cấu hình MIDDLEWARE_CLASSES trong file settings.py:
MIDDLEWARE_CLASSES = [
...
'corsheaders.middleware.CorsMiddleware',
...
]
Thêm vào cuối file settings.py :
CORS_ORIGIN_ALLOW_ALL = True
CORS_ALLOW_CREDENTIALS = True
CORS_ORIGIN_WHITELIST = (
'localhost:9999',
)
Trong đó là CORS_ORIGIN_WHITELIST danh sách các site được phép gọi service đến ứng dụng.
Để test xem việc cấu hình CORS đã thành công chưa, chúng ta tạo một file test.html với nội dung sau:
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script> |
Mở cửa sổ cmd trong thư mục chứa file test.html, gõ lệnh:
python -m http.server 9999
Truy nhập địa chỉ http://localhost:9999/test.html từ trình duyêt, click nút “Test API”, nếu trả về kết quả thì CORS đã được cấu hình thành công cho project của django.
Ứng dụng Single Page Application (SPA) hiện nay đang rất phổ biến. Với các ứng dụng SPA, việc render nội dung website sẽ hoàn toàn do phía client thực hiện, server chỉ thực hiện cung cấp các dịch vụ dữ liệu dưới dạng webservice. Phía client gọi webservice của server, nhận kết quả và sinh ra nội dung hiển thị tương ứng.
Các framework SPA frontend phổ biến là : Angular, React, Vue.
Phần này minh họa nguyên lý hoạt động của SPA mà không sử dụng framework frontend nào. Chúng ta dựa vào ví dụ webservice đã xây dựng ở phần trước:
http://localhost:8000/students/all
Phía client sẽ dựa vào kết quả của webservice để sinh ra nội dung html:
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script> |
Truy nhập từ địa chỉ http://localhost:9999/test.html để test ứng dụng SPA .
JWT là một trong các cơ chế bảo mật khi thực hiện gọi webservice. Cơ chế authentication qua JWT như sau:
Sử dụng JWT với DRF:
Cài đặt thư viện djangorestframework_simplejwt :
pip install djangorestframework_simplejwt
Thêm chức năng authentication cho API:
from rest_framework.response import Response |
Nếu sử dụng View Class, việc thêm permission như sau:
from rest_framework.views import APIView |
Trong file urls.py project, bổ sung thêm khai báo sau url như sau:
from django.contrib import admin |
Bố sung vào cuối file settings.py của project thông tin sau:
REST_FRAMEWORK = { |
Sau khi xây dựng xong API, nếu truy nhập API từ trình duyệt, chúng ta thấy sẽ có lỗi 403 vì thiếu thông tin đăng nhập:
Hình 38 .Lỗi 403 khi truy nhập API mà không có thông tin đăng nhập
Để gọi được API, phải theo quy trình sau:
{
"username" : "<username>",
"password" : "<password>"
}
Trong đó <username>, <password> là thông tin tài khoản của django (có thể tạo ra từ lệnh python manage.py createsuperuser)
Kết quả trả về sẽ chứa 2 trường:
{
"refresh" : "<refresh>"
}
Trong đó <refresh> là giá trị đã nhận được khi gọi hàm /api/token ở phía trên. Kết quả sẽ chứa trường access là giá trị mới của token để truy nhập đến API của server.
Minh họa các bước trên sử dụng postman như sau:
Hình 39 .Lấy token bằng cách POST đến địa chỉ /api/token
Hình 40 .Thêm giá trị Authorization vào header request để truy nhập được API
Để dễ hình dung hơn, chúng ta xây dựng ứng dụng SPA với 2 màn hình sau:
Do ứng dụng SPA độc lập với ứng dụng backend của django, cần cấu hình CORS cho project của django để phía client truy nhập được API. Chi tiết xem ở phần về CORS đã trình bày phần trước.
Ứng dụng SPA gồm 2 file : login.html (màn hình đăng nhập), và index.html (màn hình chính)
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css"> |
File login.html
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css"> |
File index.html
Để chạy ứng dụng SPA, từ thư mục chứa các file index.html & login.html, gõ lệnh:
python -m http.server 9999
Từ trình duyệt, truy nhập địa chỉ http://localhost:9999, màn hình đăng nhập sẽ xuất hiện
Hình 41 .Màn hình đăng nhập của ứng dụng SPA
Sau khi đăng nhập, ứng dụng SPA sẽ chuyển qua màn hình chính, tại đây có thể thực hiện gọi API đến server:
Hình 42 . Gọi API đến server sau khi đã đăng nhập
OAUTH2 là cơ chế đăng nhập và xác thực qua một Authentication server riêng. Nguyên tắc đăng nhập và xác thực của OAUTH2 như sau:
Xây dựng ứng dụng đăng nhập qua OAUTH2 với django:
Cài đặt thư viện django-oauth-toolkit:
pip install django-oauth-toolkit
Ứng dụng này sẽ thực hiện việc đăng nhập & xác thực người dùng (tương tự như google, facebook, github, …)
Tạo mới và cấu hình ứng dụng:
Tạo project và ứng dụng:
django-admin startproject oauth2provider
cd oauth2provider
python manage.py migrate
Bổ sung ứng dụng trong file settings.py của project:
INSTALLED_APPS = [ |
Bổ sung cấu hình url trong file urls.py của project:
from django.contrib import admin |
Update database :
python manage.py migrate
Cấu hình CORS cho project :
Do service của OAUTH2 provider sẽ được gọi từ ứng dụng từ một địa chỉ khác nên cần cấu hình CORS cho project. Chi tiết xem ở phần về CORS đã trình bày ở phần trước.
Tạo màn hình đăng nhập :
OAUTH2 provider cần cung cấp màn hình login để xác thực người dùng. Cách thức tạo màn hình đăng nhập đã trình bày trong phần “Tích hợp chức năng đăng ký người dùng & đăng nhập của django”. Ở đây, có thể tóm tắt cách thực hiện như sau:
TEMPLATES = [ |
<h2>Login</h2> |
python manage.py createsuperuser
Hình 43 . Màn hình đăng nhập của ứng dụng OAUTH2 Provider
Sau khi đăng nhập thành công, ứng dụng sẽ chuyển sang màn hình lỗi 404, do chưa có màn hình trang chủ ứng dụng, chúng ta bỏ qua thông báo này.
Hình 44 . Màn hình 404 sau đăng nhập (bỏ qua thông báo này)
Tạo một test API :
API này đóng vai trò như service của resource server và được gọi đến từ ứng dụng client. Để xây dựng API này, chúng ta bổ sung cấu hình url trong file urls.py của project như sau:
from django.contrib import admin |
Tạo mới file oauth2provider/views.py với nội dung sau:
from oauth2_provider.views.generic import ProtectedResourceView |
Sau khi tạo xong API, khởi động server và truy nhập API từ địa chỉ http://localhost:8000/api/test, chúng ta sẽ thấy lỗi 403, vì không có thông tin đăng nhập trong header của request.
Đăng ký mới một ứng dụng OAuth2 Client:
Truy nhập địa chỉ http://localhost:8000/o/applications/ để tạo mới một ứng dụng OAuth2 Client. Thông tin các trường nhập vào như sau:
Hình 45 . Đăng ký mới một ứng dụng OAuth2 Client
Lấy token để test thử API :
Sau khi đăng ký xong client, có thể thực hiện lấy token để test việc gọi API. Cách lấy token như sau:
Hình 46. Màn hình Authorize của OAUTH2
Hình 47. Sử dụng Postman để lấy token
Lưu ý việc lấy token có thể không thành công nếu thời gian từ khi nhận code đến khi thực hiện request bị lâu (timeout), trong trường hợp đó phải lấy lại code bằng cách thực hiện lại link Authorize ở bước 1.
Hình 48. Sử dụng token để gọi API
Ứng dụng client sẽ thực hiện đăng nhập thông qua ứng dụng OAUTH2 provider đã xây dựng ở trên, khi đăng nhập thành công, ứng dụng client sẽ lưu token lại để thực hiện các lần gọi API về sau.
Tạo mới project cho ứng dụng:
django-admin startproject oauth2client
cd oauth2client
python manage.py migrate
Tạo màn hình trang chủ cho ứng dụng :
Thêm khai báo url trong file urls.py của project:
from django.contrib import admin |
Tạo mới file oauth2client/views.py với nội dung sau:
from django.shortcuts import render, redirect |
Lưu ý thay đổi các giá trị CLIENT_ID và CLIENT_SECRET theo các giá trị đã đăng ký.
Tạo mới thư mục templates trong thư mục gốc của project, thêm cấu hình thư mục template trong file setting.py của project :
TEMPLATES = [ |
Tạo file templates/home.html với nội dung sau:
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script> |
Khởi động server tại cổng 9999:
python manage.py runserver 9999
Truy nhập ứng dụng tại địa chỉ http://localhost:9999. Ứng dụng sẽ tự động chuyển sang màn hình đăng nhập của OAUTH2 provider tại http://localhost:8000. Sau khi đăng nhập xong, OAUTH2 provider sẽ redirect trở về http://localhost:9999 và tại màn hình này có thể gọi API đến resource server.
Hình 49. Gọi API từ ứng dụng client sau khi đã đăng nhập