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

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

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

我一直渴望深入研究 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]删除
最新教程 更多>
  • SQL Server 2016中如何从JSON对象数组中提取值数组?
    SQL Server 2016中如何从JSON对象数组中提取值数组?
    [2 SQL Server 2016:将JSON对象数组转换为值数组 SQL Server 2016的JSON功能很强,但是提取特定数据结构有时需要创造性的方法。 此示例演示了将json对象数组转换为更简单的值。 考虑一个生成json对象数组的查询: 这会产生这样的JSON: [2 ...
    编程 发布于2025-04-18
  • 如何简化PHP中的JSON解析以获取多维阵列?
    如何简化PHP中的JSON解析以获取多维阵列?
    php 试图在PHP中解析JSON数据的JSON可能具有挑战性,尤其是在处理多维数组时。 To simplify the process, it's recommended to parse the JSON as an array rather than an object.To do...
    编程 发布于2025-04-18
  • 如何在其容器中为DIV创建平滑的左右CSS动画?
    如何在其容器中为DIV创建平滑的左右CSS动画?
    通用CSS动画,用于左右运动 ,我们将探索创建一个通用的CSS动画,以向左和右移动DIV,从而到达其容器的边缘。该动画可以应用于具有绝对定位的任何div,无论其未知长度如何。问题:使用左直接导致瞬时消失 更加流畅的解决方案:混合转换和左 [并实现平稳的,线性的运动,我们介绍了线性的转换。这...
    编程 发布于2025-04-18
  • 如何在无序集合中为元组实现通用哈希功能?
    如何在无序集合中为元组实现通用哈希功能?
    在未订购的集合中的元素要纠正此问题,一种方法是手动为特定元组类型定义哈希函数,例如: template template template 。 struct std :: hash { size_t operator()(std :: tuple const&tuple)const {...
    编程 发布于2025-04-18
  • JavaScript中如何动态访问全局变量?
    JavaScript中如何动态访问全局变量?
    在JavaScript 一种方法是使用窗口对象存储和检索变量。通过引用全局范围,可以使用其名称动态访问变量。 //一个脚本 var somevarname_10 = 20; //另一个脚本 window.all_vars = {}; window.all_vars ['somevarnam...
    编程 发布于2025-04-18
  • 如何高效统计字符串中字符频率?
    如何高效统计字符串中字符频率?
    在字符串中确定字符频率问题:如何有效地计数字符串中的字符频率?答案:确定字符频率,创建一个映射到整数字符的Java映射。通过字符串的字符迭代并检查它们是否存在于地图中。如果是这样,请增加其价值;否则,将其值初始化为1。此方法将导致字符作为键及其各自频率作为值的映射。另外,您可以利用Bozho的建议...
    编程 发布于2025-04-18
  • SQL按日期筛选现金值并求和
    SQL按日期筛选现金值并求和
    Totaling Cash Values with Date Filtering in SQLAs you mentioned, you have a SQL statement that calculates the total cash for each unique transaction I...
    编程 发布于2025-04-18
  • PHP阵列键值异常:了解07和08的好奇情况
    PHP阵列键值异常:了解07和08的好奇情况
    PHP数组键值问题,使用07&08 在给定数月的数组中,键值07和08呈现令人困惑的行为时,就会出现一个不寻常的问题。运行print_r($月)返回意外结果:键“ 07”丢失,而键“ 08”分配给了9月的值。此问题源于PHP对领先零的解释。当一个数字带有0(例如07或08)的前缀时,PHP将其...
    编程 发布于2025-04-18
  • 如何处理PHP文件系统功能中的UTF-8文件名?
    如何处理PHP文件系统功能中的UTF-8文件名?
    在PHP的Filesystem functions中处理UTF-8 FileNames 在使用PHP的MKDIR函数中含有UTF-8字符的文件很多flusf-8字符时,您可能会在Windows Explorer中遇到comploreer grounder grounder grounder gro...
    编程 发布于2025-04-18
  • 如何在Java字符串中有效替换多个子字符串?
    如何在Java字符串中有效替换多个子字符串?
    在java 中有效地替换多个substring,需要在需要替换一个字符串中的多个substring的情况下,很容易求助于重复应用字符串的刺激力量。 However, this can be inefficient for large strings or when working with nu...
    编程 发布于2025-04-18
  • Go web应用何时关闭数据库连接?
    Go web应用何时关闭数据库连接?
    在GO Web Applications中管理数据库连接很少,考虑以下简化的web应用程序代码:出现的问题:何时应在DB连接上调用Close()方法?,该特定方案将自动关闭程序时,该程序将在EXITS EXITS EXITS出现时自动关闭。但是,其他考虑因素可能保证手动处理。选项1:隐式关闭终止数...
    编程 发布于2025-04-18
  • 为你的所见即所得编辑器添加图像编辑功能
    为你的所见即所得编辑器添加图像编辑功能
    图像编辑是每个Wysiwyg编辑器的强大功能。它极大地增强了应用程序的功能,并使想要使用上传图像做更多更多事情的用户感到高兴。 如今,用户期望应用程序具有图像编辑功能,但是实现此类功能并不像听起来那么琐碎。 上传图像和其他媒体可能是编辑器具有的正常功能,但是图像编辑通常不是。这就是为什么在本教程中,...
    编程 发布于2025-04-18
  • 为什么HTML无法打印页码及解决方案
    为什么HTML无法打印页码及解决方案
    无法在html页面上打印页码? @page规则在@Media内部和外部都无济于事。 HTML:Customization:@page { margin: 10%; @top-center { font-family: sans-serif; font-weight: bo...
    编程 发布于2025-04-18
  • 如何避免Go语言切片时的内存泄漏?
    如何避免Go语言切片时的内存泄漏?
    ,a [j:] ...虽然通常有效,但如果使用指针,可能会导致内存泄漏。这是因为原始的备份阵列保持完整,这意味着新切片外部指针引用的任何对象仍然可能占据内存。 copy(a [i:] 对于k,n:= len(a)-j i,len(a); k
    编程 发布于2025-04-18
  • 如何正确使用与PDO参数的查询一样?
    如何正确使用与PDO参数的查询一样?
    在pdo 中使用类似QUERIES在PDO中的Queries时,您可能会遇到类似疑问中描述的问题:此查询也可能不会返回结果,即使$ var1和$ var2包含有效的搜索词。错误在于不正确包含%符号。通过将变量包含在$ params数组中的%符号中,您确保将%字符正确替换到查询中。没有此修改,PDO...
    编程 发布于2025-04-18

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

Copyright© 2022 湘ICP备2022001581号-3