从零开始到应用:使用 Python 构建数据库驱动的 Streamlit 应用程序
Streamlit 是这样一个开源 Python 库,它允许您以最小的努力构建自定义网络应用程序。它负责前端组件,让您可以专注于编写 Python 代码。使用 Streamlit 分享,人们还可以轻松免费部署他们的 Streamlit 应用程序。另一方面,SQLite 是一个轻量级、无服务器的数据库引擎,包含在 Python 中。它不需要额外的设置,并允许您将应用程序的数据保存在本地文件中,使其非常便携且易于使用。
从零到应用:使用 Python 构建数据库驱动的 Streamlit 应用程序
作者使用 Python 和 Streamlit+SQLite 创建的个人费用应用
设计数据库
对于第一个版本,我希望应用程序能够完成三件事情——
-
使用日期和费用类别记录费用
-
将Streamlit与SQLite结合,您将得到一个交互式、用户友好的应用程序,它背后是一个强大的后端数据库系统。请跟随我在这篇文章中了解如何创建一个简单的费用管理应用程序来记录个人费用,并导出所有数据进行进一步分析。
-
导出所有数据
作为一项基本规则,我使用了规范化原则来设计表。我创建了单独的分类和费用表,并通过 SQL 连接将它们重新组合以创建完整的费用记录。以下是创建这两个表的代码:
import streamlit as st
import sqlite3
import pandas as pd
import datetime as dt
DATABASE_NAME = "expenses.db"
DEFAULT_CATEGORIES = ["Rent", "Utilities", "Groceries"]
cat_list = ""
for i in DEFAULT_CATEGORIES:
cat_list = cat_list + "("" + i + ""),"
def create_tables():
connection = sqlite3.connect(DATABASE_NAME)
cursor = connection.cursor()
# Add the categories table
cursor.execute('''
CREATE TABLE IF NOT EXISTS category (
id INTEGER PRIMARY KEY,
category TEXT
)
''')
# Insert some default category values
cat_query = 'SELECT category FROM category'
category_list = pd.read_sql_query(cat_query, connection).category.to_list()
if len(category_list) == 0:
cursor.execute('INSERT INTO category (category) VALUES ' + cat_list[:-1])
# Add the expenses table
cursor.execute('''
CREATE TABLE IF NOT EXISTS expenses (
id INTEGER PRIMARY KEY,
date INTEGER,
category_id INTEGER,
amount REAL,
FOREIGN KEY (category_id) REFERENCES category(id)
)
''')
connection.commit()
connection.close()
我添加了一些默认分类,以便用户可以从这些选项开始选择。我确保默认值仅在创建分类表时第一次插入。如果我们跳过这一步,那么在页面重新加载时可能会多次插入相同的值。
设计应用
添加自定义费用类别
def save_expense(date, category, amount):
connection = sqlite3.connect(DATABASE_NAME)
cursor = connection.cursor()
cursor.execute('SELECT id FROM category WHERE category = ?', (category,))
row = cursor.fetchone()
cat_id = row[0]
try:
cursor.execute('INSERT INTO expenses (date, category_id, amount) VALUES (?, ?, ?)', (date, cat_id, amount))
result='Expense record saved successfully!'
except:
result='Oops something is not right. Please check your inputs and try again!'
connection.commit()
connection.close()
return result
st.header("Record Expense")
with st.form("record_form", clear_on_submit=True):
st.write("Fill in your expense details here")
f_date = st.date_input("Enter the date", value=None)
f_category = st.selectbox('Pick a category', u.get_category_list())
f_amount = st.number_input("Enter amount in $")
f_submitted = st.form_submit_button('Submit Expense')
if f_submitted:
expense_result = save_expense(f_date,f_category,f_amount)
st.success(expense_result)
要添加自定义类别,我添加了逻辑来检查特定类别是否已经在数据库中存在,以避免重复。我还将类别存储为“标题”格式,以保持类别列表整洁(例如:“flight tickets”、“Flight tickets”、“FLIGHT TICKETS”等都被保存为“Flight Tickets”)。我显示了可供用户选择的类别列表,以便他们在创建新类别之前可以直观地检查。一旦添加了新类别,我还必须更新显示给用户的列表。为了在不重新运行整个脚本的情况下实现这一点,我将类别列表存储在一个 _sessionstate中,该状态在点击“添加新类别”按钮时也会更新。
def save_category(new_choice):
new_choice = new_choice.title()
connection = sqlite3.connect(DATABASE_NAME)
cursor = connection.cursor()
cat_query = 'SELECT category FROM category'
category_list = pd.read_sql_query(cat_query, connection).category.to_list()
if new_choice in category_list:
result = new_choice + " already exists in the list of categories"
else:
cursor.execute('INSERT INTO category (category) VALUES (?)', (new_choice,))
result = new_choice + " added successfully to the list of categories"
connection.commit()
connection.close()
return result
def update_value():
connection = sqlite3.connect(DATABASE_NAME)
query = '''
SELECT c.id, c.category
FROM category c
'''
df = pd.read_sql_query(query, connection)
connection.close()
cat_list = df.category.tolist()
st.session_state.category = ', '.join(cat_list)
st.header("Categories")
new_choice = st.text_input('Add your own new category:')
submitted = st.button("Add new category")
if submitted:
category_result = save_category(new_choice)
st.success(category_result)
update_value()
# ---------- Show Categories --------------------
st.write("Here are the categories.")
st.write(st.session_state.category)
导出数据也很简单。你需要一个函数,该函数将从所需的表中收集数据并将其导出到 Excel 文件。在这种情况下,我将支出和类别表连接起来,创建一个包含用户之前输入的所有数据的表。这个函数可以在点击按钮时调用。
def get_expenses():
connection = sqlite3.connect(DATABASE_NAME)
query = '''
SELECT e.date, c.category, e.amount
FROM expenses e
LEFT JOIN category c ON c.id = e.category_id
'''
df = pd.read_sql_query(query, connection)
connection.close()
return df
# -------------- Download Button------------------
df = get_expenses()
st.download_button(
label="Download data as CSV",
data=df.to_csv().encode("utf-8"),
file_name="expenses.csv",
mime="text/csv",
)
要运行文件(比如说文件名是 main.py),只需执行以下命令行:
streamlit run main.py
一旦我准备好了所有代码块,我将所有数据库函数分离到一个不同的文件中。我还使用“列”来安排页面布局,并添加了一个侧边栏来写入额外的信息。查看我 GitHub 仓库中的代码最终版本这里。
最后的想法
恭喜你使用 Python、Streamlit 和 SQLite 构建了一个交互式、数据库驱动的应用程序!我希望这个项目能作为一个愉快的学习体验,探索 Streamlit 的一些 UI 功能的世界。有很多可能性可以改进这个应用程序,比如添加数据可视化、用户认证、增强的数据管理选项以及将应用程序部署到云端,但所有这些留待另日再谈。我希望你喜欢在这个项目上的工作!继续实验,谁知道呢?
在离开之前…
_ 在 Medium 上关注我,以免错过我未来写的任何新帖子;你可以在我的个人资料页面上找到更多我的文章。你还可以在LinkedIn或Twitter上与我联系!_

被折叠的 条评论
为什么被折叠?



