Connecting to data

大多数Streamlit应用程序需要某种数据或API访问才能发挥作用——无论是检索数据以供查看还是保存某些用户操作的结果。这些数据或API通常是某些远程服务、数据库或其他数据源的一部分。

你可以用Python做的任何事情,包括数据连接,通常都可以在Streamlit中实现。Streamlit的教程是许多数据源的绝佳起点。然而:

  • 在Python应用程序中连接数据通常既繁琐又令人烦恼。
  • 连接来自Streamlit应用程序的数据时,有一些特定的考虑因素,例如缓存和秘密管理。

Streamlit 提供了 st.connection(),只需几行代码即可更轻松地将您的 Streamlit 应用程序连接到数据和 API。本页提供了使用该功能的基本示例,然后重点介绍高级用法。

要全面了解此功能,请查看Streamlit开发者体验产品经理Joshua Carroll的视频教程。您将通过实际示例了解该功能在创建和管理应用程序内数据连接中的实用性。

有关基本启动和使用示例,请阅读相关的数据源教程。Streamlit 内置了对 SQL 方言和 Snowflake 的连接。我们还维护了可安装的连接,用于云文件存储Google Sheets

如果你刚开始学习,最好的方法是选择一个你可以访问的数据源,并从上面的页面中获取一个最小示例进行实践 👆。在这里,我们将提供一个使用SQLite数据库的超简单示例。之后,本页的其余部分将专注于高级用法。

一个本地SQLite数据库可能对你的应用程序的半持久数据存储非常有用。

push_pin

注意

社区云应用程序不保证本地文件存储的持久性,因此平台可能随时删除使用此技术存储的数据。

要查看下面示例的实时运行情况,请查看下面的交互式演示:

步骤 1: 安装必备库 - SQLAlchemy

Streamlit中的所有SQLConnections都使用SQLAlchemy。对于大多数其他SQL方言,您还需要安装驱动程序。但SQLite驱动程序随python3一起提供,因此不需要安装。

pip install SQLAlchemy==1.4.0

步骤2:在您的Streamlit secrets.toml文件中设置数据库URL

创建一个目录和文件 .streamlit/secrets.toml,该文件应位于应用程序运行的同一目录中。将以下内容添加到文件中。

# .streamlit/secrets.toml [connections.pets_db] url = "sqlite:///pets.db"

步骤3:在您的应用程序中使用连接

以下应用程序创建与数据库的连接,使用它创建表并插入一些数据,然后查询数据并将其显示在数据框中。

# streamlit_app.py import streamlit as st # Create the SQL connection to pets_db as specified in your secrets file. conn = st.connection('pets_db', type='sql') # Insert some data with conn.session. with conn.session as s: s.execute('CREATE TABLE IF NOT EXISTS pet_owners (person TEXT, pet TEXT);') s.execute('DELETE FROM pet_owners;') pet_owners = {'jerry': 'fish', 'barbara': 'cat', 'alex': 'puppy'} for k in pet_owners: s.execute( 'INSERT INTO pet_owners (person, pet) VALUES (:owner, :pet);', params=dict(owner=k, pet=pet_owners[k]) ) s.commit() # Query and display the data you inserted pet_owners = conn.query('select * from pet_owners') st.dataframe(pet_owners)

在这个例子中,我们没有在调用conn.query()时设置ttl=值,这意味着只要应用程序服务器运行,Streamlit就会无限期地缓存结果。

现在,进入更高级的主题!🚀

Streamlit 支持在用户的主目录中指定的全局密钥文件,例如 ~/.streamlit/secrets.toml。如果您构建或管理多个应用程序,我们建议在跨应用程序的本地开发中使用全局凭据或密钥文件。通过这种方法,您只需在一个地方设置和管理您的凭据,并且将新应用程序连接到现有数据源实际上只需一行代码。它还减少了意外将凭据提交到 git 的风险,因为它们不需要存在于项目存储库中。

对于在本地开发期间连接到多个类似数据源的情况(例如本地与暂存数据库),您可以在您的密钥或凭据文件中为不同的环境定义不同的连接部分,然后在运行时决定使用哪一个。st.connection 支持使用 name=env: 语法来实现这一点。

例如,假设我有一个本地和一个暂存的MySQL数据库,并希望在不同的时间将我的应用程序连接到其中任何一个。我可以创建一个像这样的全局密钥文件:

# ~/.streamlit/secrets.toml [connections.local] url = "mysql://me:****@localhost:3306/local_db" [connections.staging] url = "mysql://jdoe:******@staging.acmecorp.com:3306/staging_db"

然后我可以配置我的应用程序连接,使其名称来自指定的环境变量

# streamlit_app.py import streamlit as st conn = st.connection("env:DB_CONN", "sql") df = conn.query("select * from mytable") # ...

现在我可以通过设置DB_CONN环境变量来指定在运行时连接到本地还是暂存环境。

# connect to local DB_CONN=local streamlit run streamlit_app.py # connect to staging DB_CONN=staging streamlit run streamlit_app.py

SQLConnection 配置使用 SQLAlchemy 的 create_engine() 函数。它将接受一个单一的 URL 参数,或尝试使用 SQLAlchemy.engine.URL.create() 从多个部分(用户名、数据库、主机等)构造一个 URL。

几个流行的SQLAlchemy方言,如Snowflake和Google BigQuery,除了URL之外,还可以使用额外的参数来配置create_engine()。这些参数可以直接作为**kwargs传递给st.connection调用,或者在名为create_engine_kwargs的额外秘密部分中指定。

例如,snowflake-sqlalchemy 接受一个额外的 connect_args 参数作为字典,用于配置 URL 中不支持的选项。这些可以按如下方式指定:

# .streamlit/secrets.toml [connections.snowflake] url = "snowflake://<user_login_name>@<account_identifier>/" [connections.snowflake.create_engine_kwargs.connect_args] authenticator = "externalbrowser" warehouse = "xxx" role = "xxx"
# streamlit_app.py import streamlit as st # url and connect_args from secrets.toml above are picked up and used here conn = st.connection("snowflake", "sql") # ...

或者,这可以完全在**kwargs中指定。

# streamlit_app.py import streamlit as st # secrets.toml is not needed conn = st.connection( "snowflake", "sql", url = "snowflake://<user_login_name>@<account_identifier>/", connect_args = dict( authenticator = "externalbrowser", warehouse = "xxx", role = "xxx", ) ) # ...

你也可以同时提供kwargs和secrets.toml的值,它们将会被合并(通常,kwargs会优先)。

默认情况下,连接对象使用st.cache_resource进行缓存,且不会过期。在大多数情况下,这是期望的行为。如果你希望连接对象在一段时间后过期,可以执行st.connection('myconn', type=MyConnection, ttl=)

许多连接类型预计是长时间运行或完全无状态的,因此不需要过期。假设连接变得陈旧(例如缓存的令牌过期或服务器端连接关闭)。在这种情况下,每个连接都有一个reset()方法,该方法将使缓存的版本无效,并导致Streamlit在下次检索时重新创建连接。

query()read()这样的便捷方法通常会默认使用st.cache_data缓存结果,且没有过期时间。当应用程序可以运行许多不同的读取操作并产生大量结果时,随着时间的推移,可能会导致高内存使用率,并且在长时间运行的应用程序中结果会变得陈旧,这与任何其他使用st.cache_data的情况相同。对于生产用例,我们建议在这些读取操作上设置适当的ttl,例如conn.read('path/to/file', ttl="1d")。有关更多信息,请参阅Caching

对于可能获得大量并发使用的应用程序,请确保您了解连接的任何线程安全影响,特别是在使用由第三方构建的连接时。由Streamlit构建的连接默认应提供线程安全的操作。

在大多数情况下,使用现有的驱动程序或SDK构建自己的基本连接实现是相当简单的。然而,通过进一步的努力,您可以添加更复杂的功能。这种自定义实现可以是扩展对新数据源支持并为Streamlit生态系统做出贡献的好方法。

对于经常使用特定访问模式和数据源的组织来说,维护一个定制的内部Connection实现可能是一种强大的实践。

查看下面的构建你自己的连接页面在st.experimental连接演示应用程序中,以获取快速教程和工作实现。此演示在DuckDB之上构建了一个最小但非常实用的连接。

典型的步骤是:

  1. 声明 Connection 类,扩展 ExperimentalBaseConnection,并将类型参数绑定到底层连接对象:

    from streamlit.connections import ExperimentalBaseConnection import duckdb class DuckDBConnection(ExperimentalBaseConnection[duckdb.DuckDBPyConnection])
  2. 实现_connect方法,该方法读取任何kwargs、外部配置/凭据位置以及Streamlit密钥,以初始化底层连接:

    def _connect(self, **kwargs) -> duckdb.DuckDBPyConnection: if 'database' in kwargs: db = kwargs.pop('database') else: db = self._secrets['database'] return duckdb.connect(database=db, **kwargs)
  3. 添加对您的连接有意义的实用辅助方法(在需要缓存的地方将它们包装在st.cache_data中)

我们建议应用以下最佳实践,以使您的连接与Streamlit内置的连接及更广泛的Streamlit生态系统保持一致。这些实践对于您打算公开分发的连接尤为重要。

  1. 扩展现有的驱动程序或SDK,并默认使用对其现有用户有意义的语义。

    在构建连接时,您很少需要从头开始实现复杂的数据访问逻辑。尽可能使用现有的流行Python驱动程序和客户端。这样做可以使您的连接更易于维护、更安全,并让用户能够获得最新功能。例如,SQLConnection扩展了SQLAlchemy,FileConnection扩展了fsspecGsheetsConnection扩展了gspread等。

    考虑使用与底层包一致的访问模式、方法/参数命名和返回值,这些对于该包的现有用户来说是熟悉的。

  2. 直观、易用的读取方法。

    st.connection 的强大之处在于提供了直观、易用的读取方法,使应用开发者能够快速上手。大多数连接应至少暴露一个读取方法,该方法应具备以下特点:

    • 使用简单的动词命名,如 read()query()get()
    • 默认情况下由 st.cache_data 包装,至少支持 ttl= 参数
    • 如果结果是表格格式,则返回一个 pandas DataFrame
    • 提供常用关键字参数(如分页或格式化),并具有合理的默认值——理想情况下,常见情况只需 1-2 个参数。
  3. _connect方法中的配置、密钥和优先级。

    每个连接都应支持通过Streamlit密钥和关键字参数提供的常用连接参数。名称应与初始化或配置底层包时使用的名称匹配。

    此外,在相关情况下,连接应支持通过现有的标准环境变量或配置/凭证文件进行数据源特定配置。在许多情况下,底层包提供了构造函数或工厂函数,这些函数已经轻松处理了这一点。

    当您可以在多个地方指定相同的连接参数时,我们建议尽可能使用以下优先级顺序(从高到低):

    • 代码中指定的关键字参数
    • Streamlit密钥
    • 数据源特定配置(如果相关)
  4. 处理线程安全性和过时连接。

    连接在实际操作中应提供线程安全的操作(这应该是大多数情况),并清楚地记录与此相关的任何注意事项。大多数底层驱动程序或SDK应提供线程安全的对象或方法——尽可能使用这些。

    如果底层驱动程序或SDK存在状态连接对象变得过时或无效的风险,请考虑在访问方法中构建一个低影响的健康检查或重置/重试模式。Streamlit内置的SQLConnection使用tenacity和内置的Connection.reset()方法提供了一个很好的示例。另一种方法是鼓励开发人员在st.connection()调用中设置适当的TTL,以确保它定期重新初始化连接对象。

forum

还有问题吗?

我们的 论坛 充满了有用的信息和Streamlit专家。