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

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

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

我一直渴望深入研究 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]删除
最新教程 更多>
  • 哪种方法更有效地用于点 - 填点检测:射线跟踪或matplotlib \的路径contains_points?
    哪种方法更有效地用于点 - 填点检测:射线跟踪或matplotlib \的路径contains_points?
    在Python 射线tracing方法 matplotlib路径对象表示多边形。它检查给定点是否位于定义路径内。 This function is often faster than the ray tracing approach, as seen in the code snippet pr...
    编程 发布于2025-02-19
  • 如何在JavaScript对象中动态设置键?
    如何在JavaScript对象中动态设置键?
    如何为JavaScript对象变量创建动态键,尝试为JavaScript对象创建动态键,使用此Syntax jsObj['key' i] = 'example' 1;将不起作用。正确的方法采用方括号:他们维持一个长度属性,该属性反映了数字属性(索引)和一个数字属性的数量。标准对象没有模仿这...
    编程 发布于2025-02-19
  • 如何可靠地检查MySQL表中的列存在?
    如何可靠地检查MySQL表中的列存在?
    在mySQL中确定列中的列存在,验证表中的列存在与与之相比有点困惑其他数据库系统。常用的方法:如果存在(从信息_schema.columns select * * where table_name ='prefix_topic'和column_name =&...
    编程 发布于2025-02-19
  • 如何为PostgreSQL中的每个唯一标识符有效地检索最后一行?
    如何为PostgreSQL中的每个唯一标识符有效地检索最后一行?
    [2最后一行与数据集中的每个不同标识符关联。考虑以下数据: 1 2014-02-01 kjkj 1 2014-03-11 ajskj 3 2014-02-01 sfdg 3 2014-06-12 fdsa 为了检索数据集中每个唯一ID的最后一行信息,您可以在操作员上使用Postgres的有效效...
    编程 发布于2025-02-19
  • 如何使用char_length()在mySQL中按字符串长度对数据进行排序?
    如何使用char_length()在mySQL中按字符串长度对数据进行排序?
    [2使用内置的char_length()函数。:返回字符串中的字符数,考虑多BYTE字符encoding(例如UTF-8)。 ] :返回字符串占用的字节数,该字符的数量可能无法准确反映字符计数多字节编码。 [&& && && && && && &&华从指定的表格中的所有行,并根据指定列的字符长度按升...
    编程 发布于2025-02-19
  • 如何检查对象是否具有Python中的特定属性?
    如何检查对象是否具有Python中的特定属性?
    方法来确定对象属性存在寻求一种方法来验证对象中特定属性的存在。考虑以下示例,其中尝试访问不确定属性会引起错误: >>> a = someClass() >>> A.property Trackback(最近的最新电话): 文件“ ”,第1行, AttributeError:SomeClass实...
    编程 发布于2025-02-19
  • 在映射到MySQL枚举列时,如何确保冬眠保留值?
    在映射到MySQL枚举列时,如何确保冬眠保留值?
    在hibernate中保存枚举值:故障排除错误的列type ,他们各自的映射至关重要。在Java中使用枚举类型时,至关重要的是,建立冬眠的方式如何映射到基础数据库。在您的情况下,您已将MySQL列定义为枚举,并在Java中创建了相应的枚举代码。但是,您遇到以下错误:“ MyApp中的错误列类型。...
    编程 发布于2025-02-19
  • 如何使用替换指令在GO MOD中解析模块路径差异?
    如何使用替换指令在GO MOD中解析模块路径差异?
    克服go mod中的模块路径差异 github.com/coreos/etcd/integration imports :解析GO.mod:模块将其路径声明为: go.etcd.io/bbolt [&&&&&&&&&&&&&&&&&&&&&&&&&&&& github.com/coreos/b...
    编程 发布于2025-02-19
  • 如何使用PHP从XML文件中有效地检索属性值?
    如何使用PHP从XML文件中有效地检索属性值?
    从php 您的目标可能是检索“ varnum”属性值,其中提取数据的传统方法可能会使您感到困惑。 - > attributes()为$ attributeName => $ attributeValue){ echo $ attributeName,'=“',$ at...
    编程 发布于2025-02-19
  • Java是否允许多种返回类型:仔细研究通用方法?
    Java是否允许多种返回类型:仔细研究通用方法?
    在java中的多个返回类型:一个误解介绍,其中foo是自定义类。该方法声明似乎拥有两种返回类型:列表和E。但是,情况确实如此吗?通用方法:拆开神秘 [方法仅具有单一的返回类型。相反,它采用机制,如钻石符号“ ”。分解方法签名: :本节定义了一个通用类型参数,E。它表示该方法接受扩展FOO类的任何...
    编程 发布于2025-02-19
  • 大批
    大批
    [2 数组是对象,因此它们在JS中也具有方法。 切片(开始):在新数组中提取部分数组,而无需突变原始数组。 令ARR = ['a','b','c','d','e']; // USECASE:提取直到索引作...
    编程 发布于2025-02-19
  • 在没有密码提示的情况下,如何在Ubuntu上安装MySQL?
    在没有密码提示的情况下,如何在Ubuntu上安装MySQL?
    在ubuntu 使用debconf-set-selections 在安装过程中避免密码提示mysql root用户。这需要以下步骤: sudo debconf-set-selections
    编程 发布于2025-02-19
  • 如何以不同的频率控制Android设备振动?
    如何以不同的频率控制Android设备振动?
    控制使用频率变化的Android设备振动是否想为您的Android应用程序添加触觉元素?了解如何触发设备的振动器至关重要。您可以做到这一点:生成基本振动以生成简单的振动,使用振动器对象:这将导致设备在指定的持续时间内振动。许可要求通过上述技术,您可以创建在您的Android应用程序中自定义振动,以增...
    编程 发布于2025-02-19
  • 如何克服PHP的功能重新定义限制?
    如何克服PHP的功能重新定义限制?
    克服PHP的函数重新定义限制在PHP中,多次定义一个相同名称的函数是一个no-no。尝试这样做,如提供的代码段所示,将导致可怕的“不能重新列出”错误。 //错误:“ cance redeclare foo()” 但是,PHP工具腰带中有一个隐藏的宝石:runkit扩展。它使您能够灵活地重新定义...
    编程 发布于2025-02-19

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

Copyright© 2022 湘ICP备2022001581号-3