Đây là Bài 6 trong chuối các bài hướng dẫn sử dụng Python Flask cơ bản. Quay về Bài 5: Tạo giao diện để xem dữ liệu khách hàng
Vậy là ở các bài trước, bạn đã học cách quản lý cơ sở dữ liệu cách khách hàng. Bạn đã biết cách thêm, liệt kê và xem thông tin khách hàng. Bạn cũng đã học cách tạo bluepring, route, và template cho các chức năng.
Trong bài này, chúng ta sẽ tiếp tục với chức năng tạo hóa đơn cho 1 khách hàng. Bạn sẽ học các thao tác phức tạp hơn trong template, và sử dụng câu lệnh JOIN của SQL.
Trước khi bắt đầu, chúng ta hãy cùng xem cấu trúc của bảng hóa đơn (invoice) đã được tạo từ đầu khóa học:
CREATE TABLE invoice (
id INTEGER PRIMARY KEY AUTOINCREMENT,
customer_id INTEGER NOT NULL,
invoice_date DATE,
description TEXT NOT NULL,
sub_total FLOAT,
discount FLOAT,
tax FLOAT,
FOREIGN KEY (customer_id) REFERENCES customer (id)
);
Bảng này có 1 khóa phụ customer_id, được kế nối với khóa chính “id” của bảng customer.
Tạo blueprint cho invoice và đăng ký trong app
Tại thư mục gốc flask-customer/mycustomer, bạn hãy tạo file blueprint invoice.py với đoạn code sau:
from werkzeug.exceptions import abort
# import các mod cần thiết cho 1 blueprint cơ bản:
from flask import (
Blueprint, flash, g, redirect, render_template, request, session, url_for
)
# import hàm get_db từ module db mà chúng ta đã viết ở bài trước. Hàm này phục vụ cho việc truy vấn CSDL.
from mycustomer.db import get_db
from . import customer
# tạo blueprint cho module invoice
bp = Blueprint('invoice', __name__, url_prefix='/invoice')
- Bạn có thể thấy bluepring này mang tên “invoice”
- Đường dẫn cho các route của blueprint này trên url được bắt đầu bởi “/invoice”
Tiếp đó, mở file flask-customer/mycustomer/__init__.py và thêm dòng code sau để đăng ký blueprint với app:
...
def create_app(test_config=None):
# tạo app
app = Flask(__name__, instance_relative_config=True)
...
...
from . import customer
app.register_blueprint(customer.bp)
# Thêm 2 dòng dưới đây:
from . import invoice
app.register_blueprint(invoice.bp)
return app
Chức năng tạo invoice mới
Mở blueprint của invoice (invoice.py) và thêm route create phục vụ cho việc tạo invoice mới:
# tạo blueprint cho module invoice
bp = Blueprint('invoice', __name__, url_prefix='/invoice')
# Thêm route mới từ block code này:
@bp.route('/create', methods=('GET', 'POST'))
def create():
# Nếu là submit form từ giao diện, xử lý lấy data và insert vào csdl
if request.method == 'POST':
# lấy data từ form mà user submit
customer_id = request.form['customer_id']
invoice_date = request.form['invoice_date']
description = request.form['description']
sub_total = request.form['sub_total']
discount = request.form['discount']
tax = request.form['tax']
error = None
# kiểm tra các trường bắt buộc cần có:
if not customer_id:
error = 'Customer is required.'
if not invoice_date:
error = 'Invoice date is required.'
if not description:
error = 'Invoice description is required.'
if not sub_total:
error = 'Invoice subtotal is required.'
if error is not None:
flash(error)
else:
# nếu không có lỗi thiếu data nào, tiếp tục insert vào cơ sở dữ liệu
db = get_db()
cur = db.execute(
'INSERT INTO invoice (customer_id, invoice_date, description, sub_total, discount, tax)'
' VALUES (?, ?, ?, ?, ?, ?)',
(customer_id, invoice_date, description, sub_total, discount, tax)
)
db.commit()
# sau khi insert xong, đưa user về trang xem khách hàng,
# tại trang đó chúng ta cũng sẽ liệt kê ra các invoice đã nhập cho khách hàng này
return redirect(url_for('customer.view', id=customer_id))
# route được gọi nhưng không phải qua phương thức POST.
# Tức là trường hợp hiển thị form cho user xem và điền, nên cần lấy thông tin và đưa user về template create của invoice:
# get list of customer
customer_list = customer.list_customer()
return render_template('invoice/create.html', customer_list=customer_list)
Ở bước tiếp theo, chúng ta cần tạo template cho view create invoice. Template này chứa form để người sử dụng nhập các thông tin cần thiết.
Như bạn thấy, khi route create của blueprint invoice được gọi, nếu gọi theo phương thức không phải là POST, chúng ta sẽ điều hướng cho user tới template create.
Template ‘templates/invoice/create.html’ cần có đoạn code sau:
{% extends 'base.html' %}
{% block scripts %}
{{ super() }}
{{ datepicker.loader() }} {# to load jQuery-ui #}
{{ datepicker.picker(id=".datepicker") }}
{% endblock %}
{% block content %}
<h1>New Invoice</h1>
<form method="post">
<div class="form-group">
<label for="customer_id">Customer</label>
<select name="customer_id" id="customer_id" class="form-control" required>
{% for customer in customer_list %}
<option value="{{ customer['id'] }}">{{ customer['name'] }}</option>
{% endfor %}
</select>
<label for="invoice_date">Invoice Date</label>
<input name="invoice_date" id="invoice_date" value="{{ request.form['invoice_date'] }}" class="form-control datepicker" required/>
<label for="description">Description</label>
<textarea name="description" id="description" class="form-control" required>{{ request.form['description'] }}</textarea>
<label for="sub_total">Sub-total</label>
<input name="sub_total" id="sub_total" value="{{ request.form['sub_total'] }}" class="form-control" required/>
<label for="discount">Discount</label>
<input name="discount" id="discount" value="{{ request.form['discount'] }}" class="form-control" />
<label for="tax">Tax</label>
<input name="tax" id="tax" value="{{ request.form['tax'] }}" class="form-control" />
</div>
<input type="submit" value="Save" class="btn btn-default">
</form>
{% endblock %}
- Chúng ta dùng plugin datepicker phục vụ cho việc lựa chọn ngày tháng của hóa đơn được dễ dàng. Câu lệnh để cài đặt thư viện này như sau:
pip install Flask-Datepicker
- Xem thêm thông tin về datepicker tại đây: Python Flask Datepicker
- Datepicker được load bởi đoạn code:
{% block scripts %}
{{ super() }}
{{ datepicker.loader() }} {# to load jQuery-ui #}
{{ datepicker.picker(id=".datepicker") }}
{% endblock %}
- Có nghĩa là bất cứ input nào có class chứa “datepicker” sẽ được sử dụng plugin này.
- Trong template của chúng ta, chỉ có 1 input là invoice date được khai báo để sử dụng datepicker:
<input name="invoice_date" id="invoice_date" value="{{ request.form['invoice_date'] }}" class="form-control datepicker" required/>
- Bạn cũng có thể thấy là ở route create của invoice, khi điều hướng user tới view create.html, chúng ta cũng truyền theo danh sách customer.
- Tại template create chúng ta đã sử dụng danh sách đó để tạo dropdown customer:
<select name="customer_id" id="customer_id" class="form-control" required>
{% for customer in customer_list %}
<option value="{{ customer['id'] }}">{{ customer['name'] }}</option>
{% endfor %}
</select>
- Chú ý cách sử dụng vòng lặp for để in các option cho dropdown.
Tạo menu New Invoice cho app.
Thêm đoạn code sau vào templates/base.html:
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
<ul class="nav navbar-nav">
<li><a href="{{ url_for('customer.create') }}">New Customer</a></li>
<li><a href="{{ url_for('invoice.create') }}">New Invoice</a></li> <!-- THÊM MỚI DÒNG NÀY -->
<li><a href="{{ url_for('customer.index') }}">List Customer</a></li>
</ul>
</div>
Reload lại trang web và bạn sẽ thấy menu item New Invoice. Click vào đó, điền form và bạn đã tạo thành công 1 invoice mới!
Bài tiếp theo: Python Flask: Liệt kê hóa đơn khi xem thông tin khách hàng

