Python操作國(guó)產(chǎn)金倉(cāng)數(shù)據(jù)庫(kù)KingbaseES全流程
前言
現(xiàn)在國(guó)產(chǎn)化替代是大趨勢(shì),國(guó)產(chǎn)數(shù)據(jù)庫(kù)的應(yīng)用范圍越來(lái)越廣,金倉(cāng)數(shù)據(jù)庫(kù)(KingbaseES)作為其中的佼佼者,在政務(wù)、金融等領(lǐng)域部署得特別多。今天我就帶大家從0到1,親手實(shí)現(xiàn)用Python操作KingbaseES數(shù)據(jù)庫(kù),還會(huì)基于Flask框架搭一個(gè)可視化的網(wǎng)頁(yè)管理系統(tǒng),數(shù)據(jù)的增刪改查(CRUD)全流程都能覆蓋到,Python開(kāi)發(fā)者和數(shù)據(jù)庫(kù)管理員跟著學(xué),都能掌握這套實(shí)用技能。
一、前置知識(shí)與環(huán)境準(zhǔn)備
開(kāi)始動(dòng)手前,咱們得把相關(guān)的環(huán)境和工具準(zhǔn)備好,這樣后面開(kāi)發(fā)的時(shí)候才不會(huì)卡殼。
1.1 核心技術(shù)棧
這次做項(xiàng)目用的技術(shù)都不復(fù)雜,也不用復(fù)雜的框架,輕量化還好上手,具體有這些:
- 后端框架:Flask(這是個(gè)輕量級(jí)的Python Web框架,用來(lái)快速搭網(wǎng)頁(yè)服務(wù)特別方便)
- 數(shù)據(jù)庫(kù)驅(qū)動(dòng):psycopg2(KingbaseES兼容PostgreSQL協(xié)議,直接用這個(gè)驅(qū)動(dòng)就能連接數(shù)據(jù)庫(kù))
- 前端技術(shù):HTML + 原生CSS(不用額外學(xué)前端框架,咱們把核心功能實(shí)現(xiàn)了就行)
- 數(shù)據(jù)庫(kù):KingbaseES V8/R3(我這次用的是比較常見(jiàn)的版本,其他版本操作步驟都一樣)
1.2 環(huán)境安裝步驟
步驟1:安裝Python(已經(jīng)裝了的話這步就跳過(guò))
直接去Python官網(wǎng)下載3.7及以上版本,安裝的時(shí)候記得勾選“Add Python to PATH”,這樣后面在命令行里就能直接調(diào)用python和pip命令了。
步驟2:安裝依賴庫(kù)
打開(kāi)命令行,輸下面這些命令,就能把項(xiàng)目需要的依賴庫(kù)裝好:
# 安裝Flask框架 pip install flask # 安裝KingbaseES數(shù)據(jù)庫(kù)驅(qū)動(dòng)(兼容PostgreSQL) pip install psycopg2-binary
步驟3:確認(rèn)KingbaseES數(shù)據(jù)庫(kù)狀態(tài)
- 首先得確保KingbaseES數(shù)據(jù)庫(kù)已經(jīng)啟動(dòng)了,而且能通過(guò)IP(比如本地的
127.0.0.1)和端口(默認(rèn)常用端口是54321)訪問(wèn)到。 - 提前創(chuàng)建好數(shù)據(jù)庫(kù),我這次用的是
TEST庫(kù),大家可以用KingbaseES的圖形化工具KingbaseManager來(lái)創(chuàng)建。 - 還要準(zhǔn)備好數(shù)據(jù)庫(kù)的賬號(hào)密碼,我這里用的是默認(rèn)管理員賬號(hào)
system,密碼大家可以自己修改。
二、項(xiàng)目架構(gòu)設(shè)計(jì)
為了讓代碼結(jié)構(gòu)清晰,后面維護(hù)起來(lái)也方便,咱們采用“后端邏輯+前端模板”的分層設(shè)計(jì),整個(gè)項(xiàng)目的目錄結(jié)構(gòu)是這樣的:
kingbase_web_manager/ # 項(xiàng)目根目錄
├── app.py # 后端核心邏輯(包含F(xiàn)lask服務(wù)、數(shù)據(jù)庫(kù)操作、路由定義)
└── templates/ # 前端HTML模板文件夾(用繼承式設(shè)計(jì),減少重復(fù)代碼)
├── base.html # 基礎(chǔ)模板(放公共頭部、樣式、消息提示這些)
├── index.html # 首頁(yè)(用來(lái)展示用戶列表)
├── init_schema.html # 表結(jié)構(gòu)初始化頁(yè)面
├── create_user.html # 新增用戶頁(yè)面
├── user_detail.html # 用戶詳情頁(yè)面
└── update_balance.html # 余額修改頁(yè)面
- 后端:通過(guò)Flask定義路由,處理前端發(fā)來(lái)的HTTP請(qǐng)求,再調(diào)用
psycopg2去操作KingbaseES數(shù)據(jù)庫(kù)。 - 前端:用HTML模板繼承的方式,
base.html作為父模板,這樣所有頁(yè)面風(fēng)格能統(tǒng)一,也能少寫很多重復(fù)代碼。
三、后端核心實(shí)現(xiàn)(app.py)
后端就像是整個(gè)系統(tǒng)的“大腦”,負(fù)責(zé)連接數(shù)據(jù)庫(kù)、處理業(yè)務(wù)邏輯,最后把數(shù)據(jù)返回給前端。下面我分模塊給大家講核心代碼怎么寫。
3.1 初始化Flask與數(shù)據(jù)庫(kù)配置
首先創(chuàng)建app.py文件,先把Flask應(yīng)用初始化好,再配置KingbaseES數(shù)據(jù)庫(kù)的連接參數(shù):
from flask import Flask, jsonify, request, render_template, redirect, url_for, flash
import psycopg2 # KingbaseES數(shù)據(jù)庫(kù)驅(qū)動(dòng)
import psycopg2.extras as extras # 擴(kuò)展功能(如DictCursor,讓查詢結(jié)果為字典格式)
# 初始化Flask應(yīng)用
app = Flask(__name__)
# 配置secret_key(用于flash消息提示,防止跨站請(qǐng)求偽造)
app.secret_key = 'kingbase_web_manager_2024'
# --------------------------
# KingbaseES數(shù)據(jù)庫(kù)連接配置
# --------------------------
DB_CFG = dict(
host="127.0.0.1", # 數(shù)據(jù)庫(kù)IP(本地為127.0.0.1,遠(yuǎn)程需填實(shí)際IP)
port=54321, # KingbaseES默認(rèn)常見(jiàn)端口(不同環(huán)境可能為5432,需確認(rèn))
dbname="TEST", # 數(shù)據(jù)庫(kù)名稱(提前創(chuàng)建好)
user="system", # 數(shù)據(jù)庫(kù)賬號(hào)(管理員賬號(hào))
password="jcsjk520.",# 數(shù)據(jù)庫(kù)密碼(替換為你的實(shí)際密碼)
connect_timeout=5, # 連接超時(shí)時(shí)間(5秒)
)
# --------------------------
# 數(shù)據(jù)庫(kù)連接工具函數(shù)
# --------------------------
def get_conn():
"""獲取KingbaseES數(shù)據(jù)庫(kù)連接"""
try:
# 使用psycopg2.connect連接數(shù)據(jù)庫(kù),**DB_CFG表示解包配置字典
conn = psycopg2.connect(**DB_CFG)
return conn
except Exception as e:
# 連接失敗時(shí)拋出異常(后續(xù)路由會(huì)捕獲處理)
raise Exception(f"數(shù)據(jù)庫(kù)連接失敗: {str(e)}")
3.2 表結(jié)構(gòu)初始化(建表)
第一次用的時(shí)候,得先創(chuàng)建用戶表(t_user),咱們可以通過(guò)/init-schema這個(gè)路由,在網(wǎng)頁(yè)端觸發(fā)建表操作:
@app.route('/init-schema', methods=['GET', 'POST'])
def init_schema():
"""初始化用戶表結(jié)構(gòu)(GET請(qǐng)求顯示頁(yè)面,POST請(qǐng)求執(zhí)行建表)"""
# 1. POST請(qǐng)求:用戶點(diǎn)擊"初始化"按鈕,執(zhí)行建表SQL
if request.method == 'POST':
# 定義建表SQL(if not exists確保表不存在時(shí)才創(chuàng)建,避免重復(fù)執(zhí)行報(bào)錯(cuò))
create_table_sql = """
create table if not exists t_user(
id serial primary key, # 自增主鍵(用戶ID)
name varchar(64) not null, # 用戶名(非空)
balance numeric(12,2) default 0, # 余額(默認(rèn)0,支持兩位小數(shù))
created_at timestamp default current_timestamp # 創(chuàng)建時(shí)間(默認(rèn)當(dāng)前時(shí)間)
);
"""
try:
# 使用with語(yǔ)句自動(dòng)管理連接和游標(biāo)(無(wú)需手動(dòng)關(guān)閉)
with get_conn() as conn, conn.cursor() as cur:
cur.execute(create_table_sql) # 執(zhí)行建表SQL
# 建表成功,通過(guò)flash傳遞成功消息(前端會(huì)顯示)
flash('表結(jié)構(gòu)初始化成功!', 'success')
except Exception as e:
# 建表失敗,傳遞錯(cuò)誤消息
flash(f'初始化失敗: {str(e)}', 'danger')
# 無(wú)論成功與否,跳轉(zhuǎn)回首頁(yè)
return redirect(url_for('index'))
# 2. GET請(qǐng)求:顯示初始化表結(jié)構(gòu)的頁(yè)面
return render_template('init_schema.html')
3.3 用戶數(shù)據(jù)增刪改查實(shí)現(xiàn)
接下來(lái)咱們實(shí)現(xiàn)核心的CRUD功能,每個(gè)功能對(duì)應(yīng)一個(gè)路由,專門處理前端的請(qǐng)求,然后操作數(shù)據(jù)庫(kù)。
3.3.1 首頁(yè):展示所有用戶(查-列表)
首頁(yè)/這個(gè)路由會(huì)查詢t_user表里的所有數(shù)據(jù),然后把數(shù)據(jù)傳給前端模板展示出來(lái):
@app.route('/')
def index():
"""首頁(yè):展示所有用戶列表"""
try:
# 查詢所有用戶數(shù)據(jù)(按ID升序,可選)
query_sql = "select id, name, balance, created_at from t_user order by id;"
# 使用DictCursor,讓查詢結(jié)果為字典(便于前端通過(guò)鍵名獲取值)
with get_conn() as conn, conn.cursor(cursor_factory=extras.DictCursor) as cur:
cur.execute(query_sql)
# fetchall()獲取所有結(jié)果,轉(zhuǎn)換為列表(每個(gè)元素是字典)
users = [dict(row) for row in cur.fetchall()]
# 渲染首頁(yè)模板,傳遞用戶列表數(shù)據(jù)
return render_template('index.html', users=users)
except Exception as e:
# 查詢失敗,顯示錯(cuò)誤消息,傳遞空列表
flash(f'數(shù)據(jù)庫(kù)查詢錯(cuò)誤: {str(e)}', 'danger')
return render_template('index.html', users=[])
3.3.2 新增用戶(增)
通過(guò)/user/create這個(gè)路由實(shí)現(xiàn)新增用戶的功能,還能讓用戶輸入用戶名和初始余額:
@app.route('/user/create', methods=['GET', 'POST'])
def create_user():
"""新增用戶(GET顯示表單,POST提交數(shù)據(jù))"""
if request.method == 'POST':
# 1. 獲取前端表單提交的數(shù)據(jù)(request.form用于獲取POST表單數(shù)據(jù))
name = request.form.get('name') # 用戶名(必填)
balance = request.form.get('balance', 0.0) # 初始余額(可選,默認(rèn)0)
# 2. 數(shù)據(jù)校驗(yàn):用戶名不能為空
if not name:
flash('用戶名不能為空!', 'danger')
return render_template('create_user.html') # 校驗(yàn)失敗,返回表單頁(yè)面
try:
# 3. 數(shù)據(jù)格式轉(zhuǎn)換:余額轉(zhuǎn)為浮點(diǎn)數(shù)(防止非數(shù)字輸入)
balance = float(balance) if balance else 0.0
# 4. 執(zhí)行插入SQL(returning id返回新增用戶的ID)
insert_sql = "insert into t_user(name, balance) values (%s, %s) returning id;"
with get_conn() as conn, conn.cursor() as cur:
cur.execute(insert_sql, (name, balance)) # %s為參數(shù)占位符(防止SQL注入)
user_id = cur.fetchone()[0] # 獲取返回的用戶ID
# 5. 新增成功,跳轉(zhuǎn)回首頁(yè)
flash(f'用戶創(chuàng)建成功!用戶ID: {user_id}', 'success')
return redirect(url_for('index'))
except ValueError:
# 余額非數(shù)字時(shí)捕獲異常
flash('初始余額必須為數(shù)字!', 'danger')
except Exception as e:
# 其他錯(cuò)誤(如數(shù)據(jù)庫(kù)異常)
flash(f'用戶創(chuàng)建失敗: {str(e)}', 'danger')
# GET請(qǐng)求:顯示新增用戶表單
return render_template('create_user.html')
3.3.3 查看用戶詳情(查-單條)
通過(guò)用戶ID查詢單條用戶數(shù)據(jù),然后展示詳細(xì)信息:
@app.route('/user/<int:user_id>')
def get_user(user_id):
"""查看單個(gè)用戶詳情(通過(guò)URL路徑傳遞user_id)"""
try:
# 查詢指定ID的用戶數(shù)據(jù)
query_sql = "select id, name, balance, created_at from t_user where id=%s;"
with get_conn() as conn, conn.cursor(cursor_factory=extras.DictCursor) as cur:
cur.execute(query_sql, (user_id,)) # 傳遞user_id參數(shù)
user = dict(cur.fetchone()) if cur.rowcount > 0 else None # 轉(zhuǎn)換為字典
# 校驗(yàn)用戶是否存在
if not user:
flash('該用戶不存在!', 'danger')
return redirect(url_for('index')) # 不存在則跳轉(zhuǎn)回首頁(yè)
# 渲染詳情頁(yè)面,傳遞用戶數(shù)據(jù)
return render_template('user_detail.html', user=user)
except Exception as e:
flash(f'查詢用戶失敗: {str(e)}', 'danger')
return redirect(url_for('index'))
3.3.4 修改用戶余額(改)
通過(guò)/user/<user_id>/balance這個(gè)路由調(diào)整用戶余額,不管是增加還是減少都能實(shí)現(xiàn):
@app.route('/user/<int:user_id>/balance', methods=['GET', 'POST'])
def update_balance(user_id):
"""修改用戶余額(GET顯示表單,POST提交修改)"""
if request.method == 'POST':
try:
# 1. 獲取余額變動(dòng)值(delta:正數(shù)增加,負(fù)數(shù)減少)
delta = float(request.form.get('delta', 0))
# 2. 執(zhí)行更新SQL(balance = balance + %s 實(shí)現(xiàn)增量更新)
update_sql = "update t_user set balance = balance + %s where id=%s;"
with get_conn() as conn, conn.cursor() as cur:
cur.execute(update_sql, (delta, user_id))
# 校驗(yàn)用戶是否存在(rowcount為受影響行數(shù),0表示無(wú)此用戶)
if cur.rowcount == 0:
flash('該用戶不存在!', 'danger')
return redirect(url_for('index'))
# 3. 更新成功,跳轉(zhuǎn)回用戶詳情頁(yè)
flash('余額更新成功!', 'success')
return redirect(url_for('get_user', user_id=user_id))
except ValueError:
# 變動(dòng)值非數(shù)字時(shí)捕獲異常
flash('余額變動(dòng)值必須為數(shù)字!', 'danger')
except Exception as e:
flash(f'余額更新失敗: {str(e)}', 'danger')
# GET請(qǐng)求:顯示余額修改表單(先查詢當(dāng)前用戶信息)
try:
query_sql = "select id, name, balance from t_user where id=%s;"
with get_conn() as conn, conn.cursor(cursor_factory=extras.DictCursor) as cur:
cur.execute(query_sql, (user_id,))
user = dict(cur.fetchone()) if cur.rowcount > 0 else None
if not user:
flash('該用戶不存在!', 'danger')
return redirect(url_for('index'))
# 渲染修改余額表單,傳遞當(dāng)前用戶信息
return render_template('update_balance.html', user=user)
except Exception as e:
flash(f'查詢用戶信息失敗: {str(e)}', 'danger')
return redirect(url_for('index'))
3.3.5 刪除用戶(刪)
通過(guò)/user/<user_id>/delete這個(gè)路由刪除指定用戶,為了防止誤刪,還得讓用戶確認(rèn)一下操作:
@app.route('/user/<int:user_id>/delete', methods=['POST'])
def delete_user(user_id):
"""刪除用戶(僅支持POST請(qǐng)求,避免GET請(qǐng)求誤觸發(fā))"""
try:
# 執(zhí)行刪除SQL
delete_sql = "delete from t_user where id=%s;"
with get_conn() as conn, conn.cursor() as cur:
cur.execute(delete_sql, (user_id,))
if cur.rowcount == 0:
flash('該用戶不存在!', 'danger')
else:
flash('用戶刪除成功!', 'success')
except Exception as e:
flash(f'用戶刪除失敗: {str(e)}', 'danger')
# 刪除后跳轉(zhuǎn)回首頁(yè)
return redirect(url_for('index'))
# --------------------------
# 啟動(dòng)Flask服務(wù)
# --------------------------
if __name__ == '__main__':
# debug=True:開(kāi)發(fā)模式(代碼修改后自動(dòng)重啟,錯(cuò)誤信息顯示在網(wǎng)頁(yè)上)
app.run(debug=True)
四、前端模板實(shí)現(xiàn)(templates文件夾)
前端咱們用“繼承式模板”來(lái)設(shè)計(jì),base.html里定義公共的樣式和頁(yè)面結(jié)構(gòu),其他模板都繼承它,這樣能少寫很多重復(fù)代碼。
4.1 基礎(chǔ)模板(base.html)
所有頁(yè)面的公共部分,像頭部標(biāo)題、樣式、消息提示這些,都放在這個(gè)模板里:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Kingbase用戶管理系統(tǒng)</title>
<!-- 公共CSS樣式(統(tǒng)一頁(yè)面風(fēng)格) -->
<style>
.container { max-width: 1200px; margin: 0 auto; padding: 20px; }
.flash { padding: 12px; margin: 15px 0; border-radius: 4px; font-size: 14px; }
.success { background-color: #d4edda; color: #155724; border: 1px solid #c3e6cb; }
.danger { background-color: #f8d7da; color: #721c24; border: 1px solid #f5c6cb; }
table { width: 100%; border-collapse: collapse; margin: 20px 0; }
th, td { padding: 12px 15px; border: 1px solid #ddd; text-align: left; }
th { background-color: #f8f9fa; font-weight: bold; }
tr:hover { background-color: #f8f9fa; }
.btn { display: inline-block; padding: 8px 16px; margin: 0 5px; text-decoration: none;
color: #fff; border-radius: 4px; border: none; cursor: pointer; font-size: 14px; }
.btn-primary { background-color: #007bff; }
.btn-success { background-color: #28a745; }
.btn-danger { background-color: #dc3545; }
.form-group { margin-bottom: 20px; }
label { display: block; margin-bottom: 8px; font-weight: bold; }
input { padding: 10px; width: 350px; border: 1px solid #ddd; border-radius: 4px;
font-size: 14px; box-sizing: border-box; }
h1, h2 { color: #333; margin-bottom: 20px; }
.operate-btn-group { margin: 20px 0; }
</style>
</head>
<body>
<div class="container">
<!-- 頁(yè)面標(biāo)題(點(diǎn)擊可返回首頁(yè)) -->
<h1><a href="/" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" style="text-decoration: none; color: #333;">Kingbase用戶管理系統(tǒng)</a></h1>
<!-- 消息提示區(qū)域(顯示success/danger消息) -->
<div>
{% with messages = get_flashed_messages(with_categories=true) %}
{% if messages %}
{% for category, message in messages %}
<div class="flash {{ category }}">{{ message }}</div>
{% endfor %}
{% endif %}
{% endwith %}
</div>
<!-- 子模板內(nèi)容區(qū)域(由其他頁(yè)面填充) -->
{% block content %}{% endblock %}
</div>
</body>
</html>
4.2 首頁(yè)模板(index.html)
這個(gè)模板繼承base.html,主要用來(lái)展示用戶列表和操作按鈕:
{% extends "base.html" %} <!-- 繼承基礎(chǔ)模板 -->
{% block content %} <!-- 填充content區(qū)域 -->
<!-- 操作按鈕組(初始化表結(jié)構(gòu)、新增用戶) -->
<div class="operate-btn-group">
<a href="/init-schema" rel="external nofollow" class="btn btn-primary">初始化表結(jié)構(gòu)</a>
<a href="/user/create" rel="external nofollow" class="btn btn-success">添加新用戶</a>
</div>
<!-- 用戶列表標(biāo)題 -->
<h2>用戶列表</h2>
<!-- 若有用戶數(shù)據(jù),展示表格;否則提示無(wú)數(shù)據(jù) -->
{% if users %}
<table>
<tr>
<th>用戶ID</th>
<th>用戶名</th>
<th>賬戶余額(元)</th>
<th>創(chuàng)建時(shí)間</th>
<th>操作</th>
</tr>
{% for user in users %} <!-- 循環(huán)遍歷用戶列表 -->
<tr>
<td>{{ user.id }}</td>
<td>{{ user.name }}</td>
<td>{{ user.balance }}</td>
<td>{{ user.created_at }}</td>
<td>
<!-- 查看詳情按鈕 -->
<a href="/user/{{ user.id }}" rel="external nofollow" rel="external nofollow" class="btn btn-primary">查看</a>
<!-- 調(diào)整余額按鈕 -->
<a href="/user/{{ user.id }}/balance" rel="external nofollow" rel="external nofollow" class="btn btn-success">調(diào)整余額</a>
<!-- 刪除按鈕(POST請(qǐng)求,需用表單包裹) -->
<form action="/user/{{ user.id }}/delete" method="post" style="display: inline;">
<button type="submit" class="btn btn-danger"
onclick="return confirm('確定要?jiǎng)h除該用戶嗎?刪除后不可恢復(fù)!')">
刪除
</button>
</form>
</td>
</tr>
{% endfor %}
</table>
{% else %}
<p style="color: #666; font-size: 16px;">暫無(wú)用戶數(shù)據(jù),請(qǐng)先初始化表結(jié)構(gòu)并添加用戶。</p>
{% endif %}
{% endblock %}

4.3 表結(jié)構(gòu)初始化模板(init_schema.html)
這個(gè)模板提供一個(gè)初始化表結(jié)構(gòu)的確認(rèn)按鈕,點(diǎn)擊就能觸發(fā)建表:
{% extends "base.html" %}
{% block content %}
<h2>初始化用戶表結(jié)構(gòu)</h2>
<p style="font-size: 16px; margin: 20px 0;">
點(diǎn)擊下方按鈕創(chuàng)建用戶表(t_user),若表已存在則不會(huì)重復(fù)創(chuàng)建。
</p>
<!-- 提交表單(POST請(qǐng)求觸發(fā)建表) -->
<form method="post">
<button type="submit" class="btn btn-success">確認(rèn)初始化表結(jié)構(gòu)</button>
<a href="/" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" class="btn btn-primary">返回首頁(yè)</a>
</form>
{% endblock %}

4.4 新增用戶模板(create_user.html)
這里有個(gè)表單,用戶可以輸入用戶名和初始余額,用來(lái)新增用戶:
{% extends "base.html" %}
{% block content %}
<h2>添加新用戶</h2>
<form method="post">
<!-- 用戶名輸入框(必填) -->
<div class="form-group">
<label for="name">用戶名 <span style="color: red;">*</span></label>
<input type="text" id="name" name="name" required
placeholder="請(qǐng)輸入用戶名(如:張三)">
</div>
<!-- 初始余額輸入框(可選) -->
<div class="form-group">
<label for="balance">初始余額(元)</label>
<input type="number" id="balance" name="balance" step="0.01" min="0"
placeholder="請(qǐng)輸入數(shù)字(默認(rèn)0,如:100.50)">
</div>
<!-- 提交與返回按鈕 -->
<button type="submit" class="btn btn-success">創(chuàng)建用戶</button>
<a href="/" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" class="btn btn-primary">返回首頁(yè)</a>
</form>
{% endblock %}

4.5 用戶詳情模板(user_detail.html)
這個(gè)模板用來(lái)展示單個(gè)用戶的詳細(xì)信息,看得更清楚:
{% extends "base.html" %}
{% block content %}
<h2>用戶詳情</h2>
{% if user %}
<table>
<tr>
<th style="width: 150px;">用戶ID</th>
<td>{{ user.id }}</td>
</tr>
<tr>
<th>用戶名</th>
<td>{{ user.name }}</td>
</tr>
<tr>
<th>賬戶余額(元)</th>
<td>{{ user.balance }}</td>
</tr>
<tr>
<th>創(chuàng)建時(shí)間</th>
<td>{{ user.created_at }}</td>
</tr>
</table>
<!-- 操作按鈕 -->
<div class="operate-btn-group">
<a href="/user/{{ user.id }}/balance" rel="external nofollow" rel="external nofollow" class="btn btn-success">調(diào)整余額</a>
<a href="/" rel="external nofollow" rel="external nofollow" rel="external nofollow" rel="external nofollow" class="btn btn-primary">返回用戶列表</a>
</div>
{% endif %}
{% endblock %}

4.6 余額修改模板(update_balance.html)
這里有個(gè)輸入框,用戶可以輸入余額變動(dòng)值,用來(lái)調(diào)整用戶余額:
{% extends "base.html" %}
{% block content %}
<h2>調(diào)整用戶余額</h2>
{% if user %}
<!-- 顯示當(dāng)前用戶信息 -->
<p style="font-size: 16px; margin: 10px 0;">
當(dāng)前用戶:<strong>{{ user.name }}</strong>(ID:{{ user.id }})
</p>
<p style="font-size: 16px; margin: 10px 0;">
當(dāng)前余額:<strong>{{ user.balance }} 元</strong>
</p>
<!-- 余額變動(dòng)表單 -->
<form method="post">
<div class="form-group">
<label for="delta">余額變動(dòng)值(元)</label>
<input type="number" id="delta" name="delta" step="0.01" required
placeholder="正數(shù)增加,負(fù)數(shù)減少(如:50.00 或 -20.50)">
<small style="color: #666; display: block; margin-top: 5px;">
提示:輸入正數(shù)表示增加余額,輸入負(fù)數(shù)表示減少余額(如輸入-10表示減少10元)
</small>
</div>
<!-- 提交與返回按鈕 -->
<button type="submit" class="btn btn-success">確認(rèn)更新</button>
<a href="/user/{{ user.id }}" rel="external nofollow" rel="external nofollow" class="btn btn-primary">返回詳情</a>
</form>
{% endif %}
{% endblock %}

五、系統(tǒng)運(yùn)行與測(cè)試
代碼都寫完之后,咱們就可以啟動(dòng)系統(tǒng),測(cè)試一下功能能不能正常用了。
5.1 啟動(dòng)步驟
- 首先得確保KingbaseES數(shù)據(jù)庫(kù)已經(jīng)啟動(dòng)了,而且DB_CFG里配置的IP、端口、賬號(hào)密碼都是對(duì)的。
- 找到項(xiàng)目根目錄(就是app.py所在的文件夾),打開(kāi)命令行,輸入下面這個(gè)命令:
python app.py
- 看到下面這樣的輸出,就說(shuō)明Flask服務(wù)啟動(dòng)成功了:
* Serving Flask app 'app' * Debug mode: on WARNING: This is a development server. Do not use it in a production deployment. * Running on http://127.0.0.1:5000 Press CTRL+C to quit
5.2 功能測(cè)試流程
步驟1:初始化表結(jié)構(gòu)
- 打開(kāi)瀏覽器,訪問(wèn)
http://127.0.0.1:5000,點(diǎn)擊“初始化表結(jié)構(gòu)”按鈕,頁(yè)面會(huì)提示“表結(jié)構(gòu)初始化成功”。 - 這時(shí)候大家可以用KingbaseManager工具查一下,
TEST庫(kù)里已經(jīng)創(chuàng)建好t_user表了。
步驟2:新增用戶
- 點(diǎn)擊頁(yè)面上的“添加新用戶”,輸入用戶名(比如“張三”),再填個(gè)初始余額(比如“200”),然后點(diǎn)擊“創(chuàng)建用戶”,頁(yè)面會(huì)提示“用戶創(chuàng)建成功”。
- 回到首頁(yè),就能看到剛才新增的用戶數(shù)據(jù)了。
步驟3:查看用戶詳情
- 找到剛才新增的用戶,點(diǎn)擊用戶那一行的“查看”按鈕,就能進(jìn)入詳情頁(yè),用戶的ID、姓名、余額、創(chuàng)建時(shí)間都能看到。
步驟4:修改用戶余額
- 在詳情頁(yè)點(diǎn)擊“調(diào)整余額”,輸入變動(dòng)值(比如“50”,意思是增加50元),然后點(diǎn)擊“確認(rèn)更新”,頁(yè)面會(huì)提示“余額更新成功”。
- 再回到詳情頁(yè),就會(huì)發(fā)現(xiàn)余額已經(jīng)變成250元了。
步驟5:刪除用戶
- 回到首頁(yè),找到要?jiǎng)h除的用戶,點(diǎn)擊“刪除”按鈕,會(huì)彈出一個(gè)確認(rèn)框,點(diǎn)擊“確定”,頁(yè)面提示“用戶刪除成功”。
- 這時(shí)候首頁(yè)就不會(huì)再顯示這個(gè)用戶的數(shù)據(jù)了。
六、常見(jiàn)問(wèn)題與解決方案
開(kāi)發(fā)和測(cè)試的時(shí)候,大家可能會(huì)遇到一些問(wèn)題,我整理了幾個(gè)常見(jiàn)的,還給出了對(duì)應(yīng)的解決辦法:
6.1 數(shù)據(jù)庫(kù)連接失敗
- 錯(cuò)誤提示:
數(shù)據(jù)庫(kù)連接失敗: could not connect to server: Connection refused - 原因:要么是KingbaseES數(shù)據(jù)庫(kù)沒(méi)啟動(dòng),要么是IP或者端口配置錯(cuò)了。
- 解決方案:
- 先檢查KingbaseES服務(wù)有沒(méi)有啟動(dòng),可以通過(guò)服務(wù)管理器或者
sys_ctl命令來(lái)查看。 - 確認(rèn)一下
DB_CFG里的host(遠(yuǎn)程的話要填實(shí)際IP,本地就是127.0.0.1)和port(默認(rèn)是54321,有些環(huán)境可能是5432)對(duì)不對(duì)。 - 還要確保數(shù)據(jù)庫(kù)賬號(hào)密碼是對(duì)的,而且
system賬號(hào)有操作TEST庫(kù)的權(quán)限。
- 先檢查KingbaseES服務(wù)有沒(méi)有啟動(dòng),可以通過(guò)服務(wù)管理器或者
6.2 建表失敗
- 錯(cuò)誤提示:
初始化失敗: permission denied for schema public - 原因:
system賬號(hào)對(duì)public模式?jīng)]有創(chuàng)建表的權(quán)限。 - 解決方案:
- 用KingbaseManager登錄
TEST庫(kù),執(zhí)行下面這個(gè)授權(quán)SQL:
- 用KingbaseManager登錄
grant create on schema public to system;
執(zhí)行完之后,再重新做一次初始化操作就行。
6.3 余額修改時(shí)提示“非數(shù)字”
- 錯(cuò)誤提示:
余額變動(dòng)值必須為數(shù)字! - 原因:在輸入余額變動(dòng)值的時(shí)候,里面混了非數(shù)字字符,比如字母、中文這些。
- 解決方案:輸入的時(shí)候注意一下,只填數(shù)字、小數(shù)點(diǎn)(比如“30.5”)或者負(fù)號(hào)(比如“-10”)就可以了。
七、項(xiàng)目擴(kuò)展建議
這個(gè)項(xiàng)目目前實(shí)現(xiàn)了基礎(chǔ)的用戶管理功能,大家可以根據(jù)自己的實(shí)際需求再擴(kuò)展一下,比如這些方向:
- 用戶認(rèn)證:加個(gè)登錄功能(可以用Flask-Login),這樣能防止沒(méi)授權(quán)的人隨便訪問(wèn)系統(tǒng)。
- 分頁(yè)查詢:要是用戶數(shù)據(jù)特別多,在首頁(yè)加個(gè)分頁(yè)功能會(huì)更方便,用SQL的
limit和offset就能實(shí)現(xiàn)。 - 數(shù)據(jù)校驗(yàn):新增用戶的時(shí)候,加個(gè)用戶名唯一性校驗(yàn),先查一下數(shù)據(jù)庫(kù)里有沒(méi)有重名的,避免重復(fù)創(chuàng)建。
- 日志記錄:用
logging模塊記錄數(shù)據(jù)庫(kù)的操作日志,后面要是出了問(wèn)題,排查起來(lái)會(huì)更方便。 - 生產(chǎn)環(huán)境部署:要是要放到生產(chǎn)環(huán)境用,記得把
debug模式關(guān)掉,用Gunicorn當(dāng)WSGI服務(wù)器,再配個(gè)Nginx做反向代理,這樣更穩(wěn)定。
八、總結(jié)
這篇文章從環(huán)境準(zhǔn)備、架構(gòu)設(shè)計(jì)、代碼實(shí)現(xiàn)到功能測(cè)試,把用Python操作KingbaseES數(shù)據(jù)庫(kù),還有搭網(wǎng)頁(yè)管理系統(tǒng)的過(guò)程都講清楚了。咱們用Flask框架和psycopg2驅(qū)動(dòng),實(shí)現(xiàn)了數(shù)據(jù)增刪改查的全流程,前端用原生HTML/CSS,頁(yè)面簡(jiǎn)單又好用。
希望這篇文章能幫大家快速掌握用Python操作國(guó)產(chǎn)數(shù)據(jù)庫(kù)的技巧,給國(guó)產(chǎn)化項(xiàng)目開(kāi)發(fā)提供點(diǎn)參考。
以上就是Python操作國(guó)產(chǎn)金倉(cāng)數(shù)據(jù)庫(kù)KingbaseES全流程的詳細(xì)內(nèi)容,更多關(guān)于Python操作數(shù)據(jù)庫(kù)KingbaseES的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
python三元運(yùn)算符實(shí)現(xiàn)方法
這篇文章主要介紹了python實(shí)現(xiàn)三元運(yùn)算符的方法,大家參考使用吧2013-12-12
python3.12.7降級(jí)到3.10.0的方法步驟
本文主要介紹了python3.12.7降級(jí)到3.10.0的方法步驟,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2024-12-12
Python操作JSON實(shí)現(xiàn)網(wǎng)絡(luò)數(shù)據(jù)交換
這篇文章主要介紹了Python操作JSON實(shí)現(xiàn)網(wǎng)絡(luò)數(shù)據(jù)交換,JSON的全稱是 JavaScript Object Notation,是一種輕量級(jí)的數(shù)據(jù)交換格式,關(guān)于JSON的更多相關(guān)內(nèi)容感興趣的小伙伴可以參考一下2022-06-06
PyTorch函數(shù)torch.cat與torch.stac的區(qū)別小結(jié)
Pytorch中常用的兩個(gè)拼接函數(shù)torch.cat() 和 torch.stack(),本文主要介紹了這兩個(gè)函數(shù)的用法加區(qū)別,具有一定的參考價(jià)值,感興趣的可以了解一下2023-09-09
tensorflow轉(zhuǎn)換ckpt為savermodel模型的實(shí)現(xiàn)
這篇文章主要介紹了tensorflow轉(zhuǎn)換ckpt為savermodel模型的實(shí)現(xiàn),具有很好的參考價(jià)值,希望對(duì)大家有所幫助,一起跟隨小編過(guò)來(lái)看看吧2020-05-05
解決Alexnet訓(xùn)練模型在每個(gè)epoch中準(zhǔn)確率和loss都會(huì)一升一降問(wèn)題
這篇文章主要介紹了解決Alexnet訓(xùn)練模型在每個(gè)epoch中準(zhǔn)確率和loss都會(huì)一升一降問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-06-06
pip安裝提示Twisted錯(cuò)誤問(wèn)題(Python3.6.4安裝Twisted錯(cuò)誤)
這篇文章主要介紹了pip安裝提示Twisted錯(cuò)誤問(wèn)題(Python3.6.4安裝Twisted錯(cuò)誤),本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-05-05

