”工欲善其事,必先利其器。“—孔子《论语.录灵公》
首页 > 编程 > 使用 Django、HTMX、Alpine、Tailwind 和 Plaid 的个人财务管理应用程序

使用 Django、HTMX、Alpine、Tailwind 和 Plaid 的个人财务管理应用程序

发布于2024-11-15
浏览:267

我一直渴望深入研究 HTMX,特别是在观看了 DjangoCon Europe 2022 演讲之后 从真实 SaaS 项目上的 React 到 HTMX。我最近在日常工作中一直在使用 HTMX 和 Alpine,它们一起使开发交互式 Web 应用程序变得更加愉快,特别是对于不太喜欢前端开发的人来说。

我并不是 Django 模板的忠实粉丝,但 HTMX、Alpine.js 和 Django Cotton 已经开始改变我的观点。有了 Tailwind CSS 的帮助,它已经成为我迄今为止使用过的最好的堆栈之一。

这个项目是一个个人财务管理应用程序,集成了 Plaid 来链接银行帐户并提供财务见解。我使用 Plaid 存储库中的官方示例作为参考。

想要快速了解代码吗?在 GitHub 上查看

Personal Finance Management App with Django, HTMX, Alpine, Tailwind, and Plaid

特征

  • Plaid Integration 可链接银行账户、获取实时账户数据并跟踪交易。
  • 自动化 webhooks:
    • 每当有新交易可用时,自动同步和更新银行数据。
    • 当帐户进入不良状态时(例如,需要登录),轻松更新和重新验证帐户。
    • 自动检测新的银行账户并提示用户添加新检测到的账户。
  • 提供财务见解,例如净值、特定于账户的详细信息和基于类别的支出细目。
  • 使用身份验证器应用程序进行双因素身份验证 (2FA),以增加安全层。

用诗歌进行依赖管理

为了管理这个项目中的依赖关系,我使用了 Poetry。 Poetry 简化了包管理流程,并自动执行了与依赖项相关的大部分繁重工作。它依赖于 pyproject.toml 文件,该文件现在是定义现代 Python 项目中构建需求的标准。

为什么是诗歌?

  • 更轻松地解决依赖关系。
  • 它创建和管理虚拟环境,因此,如果您忘记创建/激活虚拟环境,Poetry 会为您提供支持。
  • 它锁定了软件包的确切版本,确保环境是可重现的。这非常有帮助,尤其是在更大的团队中工作时。

Ruff 和预提交

我一直在使用 Ruff,这是一个非常快的 Python linter 和代码格式化程序,用 Rust 构建。它可以同时处理一堆工具,例如 Black、isort 等等。

我喜欢 Ruff 的地方:

  • 您可以使用 pyproject.toml 来配置它,这可以使事情保持干净和集中。
  • 它是一体化的,因此无需管理 Black、isort 等单独的工具。

我还使用 djlint 来检查 Django 模板。它很不错,但我仍在寻找更好的东西。如果您知道任何好的替代方案,请随时分享!

有关在 Django 中设置用于代码格式化和 linting 的预提交挂钩的更多信息,请在此处查看我的文章。

项目结构

django_finance/
├── apps/
│   ├── accounts/
│   ├── common/
│   └── plaid/
├── config/
│   ├── app_template/
│   ├── settings/
│   ├── asgi.py
│   ├── celery.py
│   ├── urls.py
│   └── wsgi.py
├── static/
│   ├── css/
│   ├── images/
│   └── js/
├── templates/
│   ├── account/
│   ├── components/
│   ├── errors/
│   ├── layouts/
│   ├── mfa/
│   ├── plaid/
│   ├── _base_dashboard.html
│   ├── _base.html
│   └── index.html
├── tests/
│   ├── accounts/
│   ├── common/
│   ├── plaid/
│   └── conftest.py
├── theme/
├── .pre-commit-config.yaml
├── db.sqlite3
├── manage.py
├── poetry.lock
├── pyproject.toml
└── README.md

在通用应用程序中,我添加管理命令、验证、基础模型、模板标签等。BaseModel 如下所示:

class BaseModel(models.Model):
    """
    Base model for common fields and methods.
    """

    id = models.AutoField(primary_key=True)
    uuid = models.UUIDField(default=uuid.uuid4, unique=True, editable=False)
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)
    is_active = models.BooleanField(default=True, db_index=True, help_text="Used for soft deleting records.")

    objects = BaseModelManager()

    class Meta:
        abstract = True

    def soft_delete(self):
        self.is_active = False
        self.save()

    def restore(self):
        self.is_active = True
        self.save()

我还创建了一个自定义 startapp 命令来生成具有适合我的用例的预定义文件和内容的 Django 应用程序。

测试

为了编写测试,我将 pytest 与factory_boy一起使用,这样可以轻松声明特定于测试的字段并创建测试数据。

最初,我曾经在每个 Django 应用程序中放置一个测试文件夹。不过,我后来改在项目根目录下有一个测试文件夹,每个应用程序都有子文件夹。这种方法使导航更加顺畅,并使一切井井有条。另外,它使共享通用夹具变得更加容易,因为它们可以在更高级别上定义并在多个测试模块中重用。

tests/
├── accounts/
│   ├── __init__.py
│   ├── factories.py
│   ├── test_managers.py
│   ├── test_models.py
├── common/
│   ├── __init__.py
│   ├── test_validators.py
├── plaid/
│   ├── conftest.py
│   ├── dummy_data.py
│   ├── factories.py
│   ├── test_models.py
│   ├── test_services.py
│   ├── test_tasks.py
│   ├── test_views.py
│   ├── test_webhooks.py
├── __init__.py/
└── conftest.py

样本测试:

class TestCreatePlaidLinkToken:
    LINK_TOKEN = "link_token"

    class MockLinkTokenResponse:
        def to_dict(self):
            return {
                "link_token": TestCreatePlaidLinkToken.LINK_TOKEN,
                "expiration": "2020-03-27T12:56:34Z",
                "request_id": "request_id",
            }

    @pytest.fixture
    def setup_mocks(self, mocker):
        return {
            "mock_link_token": mocker.patch(
                "django_finance.apps.plaid.views.plaid_config.client.link_token_create",
                return_value=self.MockLinkTokenResponse(),
            ),
            "mock_logger": mocker.patch("django_finance.apps.plaid.views.logger"),
        }

    @pytest.fixture
    def create_link_token(self, client):
        def _create_link_token(data=None):
            return client.post(
                reverse("create_link_token"),
                json.dumps(data) if data else None,
                content_type="application/json",
            )

        return _create_link_token

    def test_create_plaid_link_token_success(self, login, setup_mocks, create_link_token):
        login()
        response = create_link_token()
        assert response.status_code == 201
        data = json.loads(response.content)
        assert "link_token" in data
        assert data["link_token"] == self.LINK_TOKEN
        setup_mocks["mock_link_token"].assert_called_once()

前端

该项目结合了以下前端工具:

HTMX 允许您向项目添加动态交互,而无需繁重的 JavaScript 框架。您可以使用小型、可重用的 HTMX 片段来组织 Django 模板。这允许您根据用户操作更新页面的特定部分,而不需要重新加载整个页面。这种模式非常适合 Django 的视图,因为每段内容都可以被视为自己的视图,然后动态注入到 DOM 中。

Alpine.js 是另一个用于添加交互性的轻量级 JavaScript 框架。它与 Django 的模板结构配合得很好,并为模态、下拉菜单或切换等内容提供快速的声明性行为。与 HTMX 结合,Alpine 可以为您提供足够的 JavaScript 来增强用户体验,而无需处理 React 或 Vue 之类的东西。例如,您可以使用 Alpine 管理前端状态,例如打开和关闭模式或处理客户端验证。

Tailwind CSS 处理此项目中的样式,这加快了干净且响应式 UI 的开发速度。该项目还使用 Flowbite 来构建预构建的 UI 组件,例如模式、工具提示和按钮。 Flowbite 与 Tailwind 完美集成,可帮助您避免为常见 UI 元素重新发明轮子。

Django Cotton 允许您创建可重用的 Django 模板。我在项目进行到一半时发现了这个很棒的库,同时尝试使模态可重用。最初,我尝试使用 Django 的 include 模板标签,但是当传递大量上下文数据时,它很快就变得混乱。 Django-Cotton 允许您创建高度可重用的组件,这些组件可以通过类似 HTML 标记的语法与 HTMX 和 Tailwind 很好地绑定。例如:

使用 AllAuth 进行双因素身份验证

为了在这个项目中实现身份验证,我使用了 AllAuth 库,它还提供了对多重身份验证 (MFA) 的支持。它提供了一个 MFA 模块,可与您现有的用户身份验证设置无缝集成。您可以通过身份验证器应用程序启用 2FA,从而增加额外的安全层。您还可以轻松自定义 AllAuth 提供的模板,以匹配您的应用程序的外观和风格。

Personal Finance Management App with Django, HTMX, Alpine, Tailwind, and Plaid Personal Finance Management App with Django, HTMX, Alpine, Tailwind, and Plaid

结论

总的来说,这个项目主要是教育性的,并且有足够的功能让您开始集成 Django、HTMX、Alpine.js、Tailwind 和 Plaid。

话虽这么说,请注意这里或那里可能存在一些错误,并且总有改进和进一步重用组件的空间。但总的来说,对于任何想要探索这些技术的人来说,它都是一个坚实的基础。

在 GitHub 上查看该项目

如果您希望我更详细地探讨此处提到的主题,请在评论中告诉我。

编码愉快! ?

版本声明 本文转载于:https://dev.to/earthcomfy/personal-finance-management-app-with-django-htmx-alpine-tailwind-and-plaid-2bl0?1如有侵犯,请联系[email protected]删除
最新教程 更多>
  • 如何在 PHP 中组合两个关联数组,同时保留唯一 ID 并处理重复名称?
    如何在 PHP 中组合两个关联数组,同时保留唯一 ID 并处理重复名称?
    在 PHP 中组合关联数组在 PHP 中,将两个关联数组组合成一个数组是一项常见任务。考虑以下请求:问题描述:提供的代码定义了两个关联数组,$array1和$array2。目标是创建一个新数组 $array3,它合并两个数组中的所有键值对。 此外,提供的数组具有唯一的 ID,而名称可能重合。要求是构...
    编程 发布于2024-11-15
  • 如何修复 macOS 上 Django 中的“配置不正确:加载 MySQLdb 模块时出错”?
    如何修复 macOS 上 Django 中的“配置不正确:加载 MySQLdb 模块时出错”?
    MySQL配置不正确:相对路径的问题在Django中运行python manage.py runserver时,可能会遇到以下错误:ImproperlyConfigured: Error loading MySQLdb module: dlopen(/Library/Python/2.7/site-...
    编程 发布于2024-11-15
  • Bootstrap 4 Beta 中的列偏移发生了什么?
    Bootstrap 4 Beta 中的列偏移发生了什么?
    Bootstrap 4 Beta:列偏移的删除和恢复Bootstrap 4 在其 Beta 1 版本中引入了重大更改柱子偏移了。然而,随着 Beta 2 的后续发布,这些变化已经逆转。从 offset-md-* 到 ml-auto在 Bootstrap 4 Beta 1 中, offset-md-*...
    编程 发布于2024-11-15
  • 如何使用 Twitter Bootstrap 对齐表格中的文本?
    如何使用 Twitter Bootstrap 对齐表格中的文本?
    Twitter Bootstrap 中的表格文本对齐在 Twitter 的 Bootstrap 框架中,您可以使用指定的文本对齐类来对齐表格内的文本。 Bootstrap 3text-left:左对齐文本text-center:居中对齐文本text-right:右对齐文本Bootstrap 4tex...
    编程 发布于2024-11-15
  • 除了“if”语句之外:还有什么地方可以在不进行强制转换的情况下使用具有显式“bool”转换的类型?
    除了“if”语句之外:还有什么地方可以在不进行强制转换的情况下使用具有显式“bool”转换的类型?
    无需强制转换即可上下文转换为 bool您的类定义了对 bool 的显式转换,使您能够在条件语句中直接使用其实例“t”。然而,这种显式转换提出了一个问题:“t”在哪里可以在不进行强制转换的情况下用作 bool?上下文转换场景C 标准指定了四种值可以根据上下文转换为 bool 的主要场景:语句:if、w...
    编程 发布于2024-11-15
  • 如何使 CSS 中的空表格单元格的边框可见?
    如何使 CSS 中的空表格单元格的边框可见?
    我可以在 CSS 中使空单元格的边框可见吗?在 Internet Explorer 7 中,默认情况下可能不会显示空单元格的边框。不过,有几种方法可以解决此问题。使用不间断空格如果可行,请插入不间断空格 ( )进入空单元格可以强制浏览器渲染带有边框的单元格。纯 CSS 解决方案对于纯 CS...
    编程 发布于2024-11-15
  • 如何将 Python 列表转换为 CSV 文件?
    如何将 Python 列表转换为 CSV 文件?
    将 Python 列表列表导出到 CSV 文件您的目标是将 Python 列表列表转换为 CSV 文件,确保每个子列表中都会保留不同类型(浮点型、整数型、字符串型)的数据。所需的 CSV 格式涉及使用逗号分隔每个子列表中的元素并垂直对齐子列表。要实现此目的,您可以利用 Python 的内置 csv ...
    编程 发布于2024-11-15
  • 测试限制:了解软件测试的边界
    测试限制:了解软件测试的边界
    软件测试是确保软件质量、稳定性和功能的开发过程的重要组成部分。然而,尽管测试很重要,但它也有其局限性。虽然它可以揭示缺陷,但它不能保证应用程序完全没有错误。了解这些限制有助于企业和开发人员设定切合实际的期望并优化他们的测试流程。在本文中,我们将探讨软件测试的主要局限性及其带来的挑战。 无法测试每个...
    编程 发布于2024-11-15
  • 如何有效地将文件加载到`std::vector`中?
    如何有效地将文件加载到`std::vector`中?
    高效地将文件加载到 std::vector高效地将文件加载到 std::vector,必须避免不必要的复制和内存重新分配。虽然利用 Reserve 和 read() 的原始方法可能看起来很直接,但单独的 Reserve() 并不会改变向量的容量。使用迭代器的规范方法:规范方法使用输入流迭代器来方便地...
    编程 发布于2024-11-15
  • 如何在 Go 中将数组元素直接解压为变量?
    如何在 Go 中将数组元素直接解压为变量?
    在 Go 中解包数组元素Go 缺乏将数组元素直接解包到 Python 中的变量的便捷语法。虽然提问者使用中间变量的初始方法有效,但它可能会导致代码混乱,尤其是在复杂的场景中。多个返回值为了解决这个问题,建议使用解决方案是创建一个返回多个值的函数。例如,要拆分字符串并将结果解压为两个变量,可以使用如下...
    编程 发布于2024-11-15
  • “n:m”和“1:n”关系如何塑造数据库设计?
    “n:m”和“1:n”关系如何塑造数据库设计?
    理解关系数据库设计:“n:m”和“1:n”的意义在数据库设计中,符号“ n:m”和“1:n”在表示表或实体之间的关系方面起着至关重要的作用。这些符号表示它们关联的基数。"n:m" 关系:多对多“n:m”关系表示多对多两个数据实体之间的对多关联。这意味着对于一个表中的每个实体,它可...
    编程 发布于2024-11-15
  • 如何在 Java 中查找重定向的 URL?
    如何在 Java 中查找重定向的 URL?
    在 Java 中查找重定向 URL在 Java 中访问网页时,处理 URL 重定向到备用位置的情况至关重要。要确定重定向的 URL,您可以使用 URL 和 URLConnection 类。使用 URLConnection.getUrl()使用 URLConnection 建立连接后,您可以检索连接通...
    编程 发布于2024-11-15
  • 如何使用 MySQL 查找今天生日的用户?
    如何使用 MySQL 查找今天生日的用户?
    如何使用 MySQL 识别今天生日的用户使用 MySQL 确定今天是否是用户的生日涉及查找生日匹配的所有行今天的日期。这可以通过一个简单的 MySQL 查询来实现,该查询将存储为 UNIX 时间戳的生日与今天的日期进行比较。以下 SQL 查询将获取今天有生日的所有用户: FROM USERS ...
    编程 发布于2024-11-15
  • 在 C++ 中将字符串转换为整数时如何处理转换错误?
    在 C++ 中将字符串转换为整数时如何处理转换错误?
    使用 C 中的错误处理将字符串转换为 int 将字符串转换为整数是编程中的常见任务。但是,在某些情况下,字符串值可能无法成功转换为整数。在这种情况下,优雅地处理转换失败至关重要。boost::lexical_cast将字符串转换为 int 时出现错误的最直接方法之一处理方法是使用 boost::le...
    编程 发布于2024-11-15
  • 如何在 JavaScript 中访问 PHP 变量?
    如何在 JavaScript 中访问 PHP 变量?
    在 JavaScript 中访问 PHP 变量直接在 JavaScript 中访问 PHP 变量是一个挑战。但是,有一些方法可以实现此目的:使用嵌入式 PHP 语句:在 JavaScript 块中嵌入 PHP 代码允许您将 PHP 变量分配给 JavaScript 变量:<script typ...
    编程 发布于2024-11-15

免责声明: 提供的所有资源部分来自互联网,如果有侵犯您的版权或其他权益,请说明详细缘由并提供版权或权益证明然后发到邮箱:[email protected] 我们会第一时间内为您处理。

Copyright© 2022 湘ICP备2022001581号-3