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

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

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

我一直渴望深入研究 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]删除
最新教程 更多>
  • 如何检查对象是否具有Python中的特定属性?
    如何检查对象是否具有Python中的特定属性?
    方法来确定对象属性存在寻求一种方法来验证对象中特定属性的存在。考虑以下示例,其中尝试访问不确定属性会引起错误: >>> a = someClass() >>> A.property Trackback(最近的最新电话): 文件“ ”,第1行, AttributeError: SomeClass...
    编程 发布于2025-07-21
  • 在程序退出之前,我需要在C ++中明确删除堆的堆分配吗?
    在程序退出之前,我需要在C ++中明确删除堆的堆分配吗?
    在C中的显式删除 在C中的动态内存分配时,开发人员通常会想知道是否有必要在heap-procal extrable exit exit上进行手动调用“ delete”操作员,但开发人员通常会想知道是否需要手动调用“ delete”操作员。本文深入研究了这个主题。 在C主函数中,使用了动态分配变量(H...
    编程 发布于2025-07-21
  • 如何干净地删除匿名JavaScript事件处理程序?
    如何干净地删除匿名JavaScript事件处理程序?
    删除匿名事件侦听器将匿名事件侦听器添加到元素中会提供灵活性和简单性,但是当需要删除它们时,可以构成挑战,而无需替换元素本身就可以替换一个问题。 element? element.addeventlistener(event,function(){/在这里工作/},false); 要解决此问题,请考...
    编程 发布于2025-07-21
  • 如何从Python中的字符串中删除表情符号:固定常见错误的初学者指南?
    如何从Python中的字符串中删除表情符号:固定常见错误的初学者指南?
    从python import codecs import codecs import codecs 导入 text = codecs.decode('这狗\ u0001f602'.encode('utf-8'),'utf-8') 印刷(文字)#带有...
    编程 发布于2025-07-21
  • 在Python中如何创建动态变量?
    在Python中如何创建动态变量?
    在Python 中,动态创建变量的功能可以是一种强大的工具,尤其是在使用复杂的数据结构或算法时,Dynamic Variable Creation的动态变量创建。 Python提供了几种创造性的方法来实现这一目标。利用dictionaries 一种有效的方法是利用字典。字典允许您动态创建密钥并分...
    编程 发布于2025-07-21
  • 为什么使用固定定位时,为什么具有100%网格板柱的网格超越身体?
    为什么使用固定定位时,为什么具有100%网格板柱的网格超越身体?
    网格超过身体,用100%grid-template-columns 为什么在grid-template-colms中具有100%的显示器,当位置设置为设置的位置时,grid-template-colly修复了?问题: 考虑以下CSS和html: class =“ snippet-code”> g...
    编程 发布于2025-07-21
  • 如何在其容器中为DIV创建平滑的左右CSS动画?
    如何在其容器中为DIV创建平滑的左右CSS动画?
    通用CSS动画,用于左右运动 ,我们将探索创建一个通用的CSS动画,以向左和右移动DIV,从而到达其容器的边缘。该动画可以应用于具有绝对定位的任何div,无论其未知长度如何。问题:使用左直接导致瞬时消失 更加流畅的解决方案:混合转换和左 [并实现平稳的,线性的运动,我们介绍了线性的转换。这...
    编程 发布于2025-07-21
  • Java数组中元素位置查找技巧
    Java数组中元素位置查找技巧
    在Java数组中检索元素的位置 利用Java的反射API将数组转换为列表中,允许您使用indexof方法。 (primitives)(链接到Mishax的解决方案) 用于排序阵列的数组此方法此方法返回元素的索引,如果发现了元素的索引,或一个负值,指示应放置元素的插入点。
    编程 发布于2025-07-21
  • CSS可以根据任何属性值来定位HTML元素吗?
    CSS可以根据任何属性值来定位HTML元素吗?
    靶向html元素,在CSS 中使用任何属性值,在CSS中,可以基于特定属性(如下所示)基于特定属性的基于特定属性的emants目标元素: 字体家庭:康斯拉斯(Consolas); } 但是,出现一个常见的问题:元素可以根据任何属性值而定位吗?本文探讨了此主题。的目标元素有任何任何属性值,属...
    编程 发布于2025-07-21
  • 将图片浮动到底部右侧并环绕文字的技巧
    将图片浮动到底部右侧并环绕文字的技巧
    在Web设计中围绕在Web设计中,有时可以将图像浮动到页面右下角,从而使文本围绕它缠绕。这可以在有效地展示图像的同时创建一个吸引人的视觉效果。 css位置在右下角,使用css float and clear properties: img { 浮点:对; ...
    编程 发布于2025-07-21
  • 如何使用不同数量列的联合数据库表?
    如何使用不同数量列的联合数据库表?
    合并列数不同的表 当尝试合并列数不同的数据库表时,可能会遇到挑战。一种直接的方法是在列数较少的表中,为缺失的列追加空值。 例如,考虑两个表,表 A 和表 B,其中表 A 的列数多于表 B。为了合并这些表,同时处理表 B 中缺失的列,请按照以下步骤操作: 确定表 B 中缺失的列,并将它们添加到表的末...
    编程 发布于2025-07-21
  • input: Why Does "Warning: mysqli_query() expects parameter 1 to be mysqli, resource given" Error Occur and How to Fix It?

output: 解决“Warning: mysqli_query() 参数应为 mysqli 而非 resource”错误的解析与修复方法
    input: Why Does "Warning: mysqli_query() expects parameter 1 to be mysqli, resource given" Error Occur and How to Fix It? output: 解决“Warning: mysqli_query() 参数应为 mysqli 而非 resource”错误的解析与修复方法
    mysqli_query()期望参数1是mysqli,resource给定的,尝试使用mysql Query进行执行MySQLI_QUERY_QUERY formation,be be yessqli:sqli:sqli:sqli:sqli:sqli:sqli: mysqli,给定的资源“可能发...
    编程 发布于2025-07-21
  • C++20 Consteval函数中模板参数能否依赖于函数参数?
    C++20 Consteval函数中模板参数能否依赖于函数参数?
    [ consteval函数和模板参数依赖于函数参数在C 17中,模板参数不能依赖一个函数参数,因为编译器仍然需要对非contexexpr futcoriations contim at contexpr function进行评估。 compile time。 C 20引入恒定函数,必须在编译时进行...
    编程 发布于2025-07-21
  • 如何同步迭代并从PHP中的两个等级阵列打印值?
    如何同步迭代并从PHP中的两个等级阵列打印值?
    同步的迭代和打印值来自相同大小的两个数组使用两个数组相等大小的selectbox时,一个包含country代码的数组,另一个包含乡村代码,另一个包含其相应名称的数组,可能会因不当提供了exply for for for the uncore for the forsion for for ytry...
    编程 发布于2025-07-21
  • 为什么我会收到MySQL错误#1089:错误的前缀密钥?
    为什么我会收到MySQL错误#1089:错误的前缀密钥?
    mySQL错误#1089:错误的前缀键错误descript [#1089-不正确的前缀键在尝试在表中创建一个prefix键时会出现。前缀键旨在索引字符串列的特定前缀长度长度,以便更快地搜索这些前缀。理解prefix keys `这将在整个Movie_ID列上创建标准主键。主密钥对于唯一识别...
    编程 发布于2025-07-21

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

Copyright© 2022 湘ICP备2022001581号-3