未提供项目描述
项目描述
快速行动。攀登高峰。不要破坏事物。
Mountaineer 🏔️ 是一个用于在Python和React中轻松构建webapp的框架。如果您之前使用过这两种语言中的任何一种进行开发,我们认为您会感到宾至如归。
主要特性
每个框架都有自己的独特特性和权衡。登山者始终以开发者生产力为重,其次才是生产速度。
- 📝 整个栈的Typehints:前端、后端和数据库
- 🎙️ 简单的客户
- 🌎 优化的服务器渲染,以提高可访问性和SEO
- 🏹 对网页进行静态分析,以进行强验证:链接有效性、数据访问等。
- 🤩 跳过API或Node.js服务器来为前端客户端提供服务
我们构建Mountaineer是因为我们一次次地重复发明webapp的轮子。我们喜欢Python进行后端开发,以及React在前端UX中的互动性。但是,如果没有相当多的粘合剂,它们无法无缝结合。所以:我们创建了粘合剂。在这个过程中,我们嵌入了一个V8引擎以提供服务器端渲染,添加了应用程序配置的约定,构建了本机TypeScript集成,等等。我们的愿景是让您导入一个精简的依赖项,然后就可以开始竞赛了。
我们迫不及待地想让您尝试Mountaineer,如果您喜欢它,我们也同样致力于使您成功。如果您遇到任何意外情况或学习曲线比预期的更陡峭,请提交一个Issue。还有很多事情要做 - 我们很兴奋能一起完成。
~ Pierce
入门指南
新项目
为了尽可能快速地开始,我们捆绑了一个项目生成器,在快速问答后设置了一个简单的项目。请确保您已安装pipx (安装链接)。
$ pipx run create-mountaineer-app
? Project name [my-project]: my_webapp
? Author [Pierce Freeman <pierce@freeman.vc>] Default
? Use poetry for dependency management? [Yes] Yes
? Create stub MVC files? [Yes] Yes
? Use Tailwind CSS? [Yes] Yes
? Add editor configuration? [vscode] vscode
Mountaineer项目都遵循类似的结构。运行此CLI后,您应该会看到一个名为my_webapp
的新文件夹,其中包含以下文件夹
my_webapp
/controllers
/home.py
/models
/mymodel.py
/views
/app
/home
/page.tsx
/layout.tsx
/package.json
/tsconfig.json
/app.py
/cli.py
pyproject.toml
poetry.lock
每个服务文件都嵌套在my_webapp
根包下。视图定义在基于磁盘的层次结构中(views
),嵌套路由位于嵌套文件夹中。此文件夹作为您的React项目,您可以在其中定义需求并在package.json
和tsconfig.json
中设置构建参数。控制器定义在附近的平面文件夹(controllers
)中,每个路由都是一个单独的文件。其他所有内容都是您需要根据需要修改的标准Python代码。
开发
如果您从头开始创建新应用程序,通常需要创建新的数据库表。请确保Postgres正在运行。我们通过create-mountaineer-app
捆绑了方便的docker compose文件。
docker compose up -d
poetry run createdb
当然,您也可以使用现有的数据库实例,只需在项目根目录中的.env
文件中配置它即可。
Mountaineer依赖于监视项目更改并进行渐进式编译。我们提供了一些CLI命令来帮助您完成此操作。
在进行开发工作时,通常需要预览前端并自动构建依赖文件。您可以使用以下命令来完成此操作
$ poetry run runserver
INFO: Started server process [93111]
INFO: Waiting for application startup.
INFO: Application startup complete.
INFO: Uvicorn running on http://127.0.0.1:5006 (Press CTRL+C to quit)
导航到http://127.0.0.1:5006以查看您的新Web应用程序正在运行。
或者,如果您只想监视源树中的更改而不托管服务器。监视将允许您的前端从后端控制器获取API定义
$ poetry run watch
这两个CLI命令在您的项目cli.py
文件中指定。
教程
以下我们将介绍Mountaineer的一些独特方面。让我们创建一个简单的待办事项列表,我们可以添加新项目。
为了本教程的目的,我们假设您的项目是用create-mountaineer-app
生成的,并且您已跳过MVC占位符文件。如果不是这样,您将不得不删除一些现有的文件。
让我们从创建将应用程序状态持久化到数据库的数据模型开始。这些定义实际上是Pydantic模式,将通过SQLModel与数据库桥接。
# my_webapp/models/todo.py
from mountaineer.database import SQLModel, Field
from uuid import UUID, uuid4
class TodoItem(SQLModel, table=True):
id: UUID = Field(default_factory=uuid4, primary_key=True)
description: str
completed: bool = False
同时更新索引文件
# my_webapp/models/__init__.py
from .todo import TodoItem # noqa: F401
确保您有一个正在运行的Postgres数据库。我们通过create-mountaineer-app
捆绑了方便的docker compose文件。在后台启动它,并从这些代码定义中创建新的数据库表。
docker compose up -d
poetry run createdb
poetry run runserver
太好了!在这个阶段,我们已经创建了数据库表并运行了一个基本服务器。接下来,我们将创建一个新的控制器,因为它将定义您可以推送到和从前端拉取哪些数据。
# my_webapp/controllers/home.py
from mountaineer import sideeffect, ControllerBase, RenderBase
from mountaineer.database import DatabaseDependencies
from fastapi import Request, Depends
from mountaineer.database.session import AsyncSession
from sqlmodel import select
from my_webapp.models.todo import TodoItem
class HomeRender(RenderBase):
client_ip: str
todos: list[TodoItem]
class HomeController(ControllerBase):
url = "/"
view_path = "/app/home/page.tsx"
async def render(
self,
request: Request,
session: AsyncSession = Depends(DatabaseDependencies.get_db_session)
) -> HomeRender:
todos = (await session.exec(select(TodoItem))).all()
return HomeRender(
client_ip=(
request.client.host
if request.client
else "unknown"
),
todos=todos
)
控制器只有三个要求:设置
- URL
- 视图路径
- 初始数据有效负载
此render()
函数是Mountaineer的核心构建块。所有控制器都需要一个。它定义了前端需要解决其视图的所有数据。这个特定的控制器从数据库检索所有待办事项,以及用户的当前IP。
[!TIP] render()函数接受FastAPI端点所有的参数:路径、查询参数和依赖注入函数。目前我们只是获取
Request
对象以获取客户端IP。
注意,数据库会话是通过依赖注入提供的,它与FastAPI的 Depends语法兼容。标准库提供了两个主要的依赖提供程序
- mountaineer.CoreDependencies:配置和一般依赖注入的辅助函数
- mountaineer.database.DatabaseDependencies:数据库生命周期和管理的辅助函数
现在我们已经创建了这个控制器,我们将其连接到应用程序。这将使其在您加载主页时注册并显示。
# my_webapp/app.py
from mountaineer.app import AppController
from mountaineer.js_compiler.postcss import PostCSSBundler
from mountaineer.render import LinkAttribute, Metadata
from my_webapp.config import AppConfig
from my_webapp.controllers.home import HomeController
controller = AppController(
config=AppConfig(),
global_metadata=Metadata(
links=[LinkAttribute(rel="stylesheet", href="/static/app_main.css")]
),
custom_builders=[
PostCSSBundler(),
],
)
controller.register(HomeController())
让我们转到前端。
/* my_webapp/views/app/home/page.tsx */
import React from "react";
import { useServer, ServerState } from "./_server/useServer";
const CreateTodo = ({ serverState }: { serverState: ServerState }) => {
return (
<div className="flex gap-x-4">
<input
type="text"
className="grow rounded border-2 border-gray-200 px-4 py-2"
/>
<button className="rounded bg-blue-500 px-4 py-2 font-bold text-white hover:bg-blue-700">
Create
</button>
</div>
);
};
const Home = () => {
const serverState = useServer();
return (
<div className="mx-auto max-w-2xl space-y-8 p-8 text-2xl">
<p>
Hello {serverState.client_ip}, you have {serverState.todos.length} todo
items.
</p>
<CreateTodo serverState={serverState} />
{
/* Todo items are exposed as typehinted Typescript interfaces */
serverState.todos.map((todo) => (
<div key={todo.id} className="rounded border-2 border-gray-200 p-4">
<div>{todo.description}</div>
</div>
))
}
</div>
);
};
export default Home;
我们定义了一个简单的视图来显示从后端传来的数据。通常情况下,为了实现这一点,我们需要设置一个API层、Node服务器,或者使用Jinja模板来格式化页面。
在这里,我们使用自动生成的useServer()
钩子。这个钩子负载将提供所有HomeRender
字段作为serverState的属性。并且它可以在页面加载时立即使用,无需任何往返请求。另外,如果你的IDE支持语言服务器(现在大多数都支持),你应该会看到当你在输入时,serverState
的字段会自动提示。
如果你在浏览器中访问localhost:5006/
,我们可以看到我们的欢迎消息,但我们还不能真正对待办事项进行操作。让我们添加一些交互性。
[!TIP] 尝试在你的浏览器中禁用JavaScript。由于我们的服务器端渲染,页面将以所有变量保持完整的方式渲染。
没有变长的待办事项列表有什么用?我们定义了一个add_todo
函数,该函数接受一个pydantic模型NewTodoRequest
,该模型定义了新待办事项所需的所有参数。然后我们将它转换为数据库对象,并添加到PostgreSQL表中。
# my_webapp/controllers/home.py
from pydantic import BaseModel
class NewTodoRequest(BaseModel):
description: str
class HomeController(ControllerBase):
...
@sideeffect
async def add_todo(
self,
payload: NewTodoRequest,
session: AsyncSession = Depends(DatabaseDependencies.get_db_session)
) -> None:
new_todo = TodoItem(description=payload.description)
session.add(new_todo)
await session.commit()
这里的关键是@sideeffect
。一旦你创建了一个新的待办事项,前端上的先前状态就过时了。它只会显示在你创建新待办事项之前的待办事项。这不是交互式应用程序想要的效果。这个装饰器表示我们希望前端刷新其数据,因为我们更新了服务器上的待办事项列表后,客户端状态会变得过时。
Mountaineer检测到这个副作用函数的存在,并分析其签名。然后它将这个函数暴露给前端作为一个普通的异步函数。
/* my_webapp/views/app/home/page.tsx */
import React, { useState } from "react";
import { useServer } from "./_server/useServer";
/* Replace the existing CreateTodo component definition you have */
const CreateTodo = ({ serverState }: { serverState: ServerState }) => {
const [newTodo, setNewTodo] = useState("");
return (
<div className="flex gap-x-4">
<input
type="text"
className="grow rounded border-2 border-gray-200 px-4 py-2"
value={newTodo}
onChange={(e) => setNewTodo(e.target.value)}
/>
<button
className="rounded bg-blue-500 px-4 py-2 font-bold text-white hover:bg-blue-700"
onClick={
/* Here we call our sideeffect function */
async () => {
await serverState.add_todo({
requestBody: {
description: newTodo,
},
});
setNewTodo("");
}
}
>
Create
</button>
</div>
);
};
...
export default Home;
useServer()
将我们的add_todo
函数暴露出来,这样我们就可以直接从前端调用后端。注意,我们不需要读取或解析这个函数的输出值来渲染新待办事项到列表中。因为这个函数被标记为副作用,所以当这个函数被调用后,前端将自动刷新其数据。
在浏览器中加载它。如果你打开你的网络工具,你可以创建一个新的待办事项,看到POST请求将数据发送到后端并接收当前服务器状态。实际的数据更新和合并由Mountaineer内部完成。
你可以在任何需要使用动态React状态变量(useEffect、useCallback等)的地方使用这些serverState变量。但是,与React状态不同,当相关的副作用被触发时,这些变量会自动更新。
就是这样。我们刚刚构建了一个完全交互式的Web应用程序,无需担心显式的API。你指定服务器上的数据模型和操作,适当的客户端钩子会自动生成和更新。它给了你服务器渲染HTML和虚拟DOM交互性的能力,而无需在保持一切同步的复杂数据突变上妥协。
了解更多
我们还有其他文档,对Mountaineer的不同功能进行了更深入的技术探讨。查看mountaineer.sh。
项目详情
mountaineer-0.6.0.tar.gz 的哈希值
算法 | 哈希摘要 | |
---|---|---|
SHA256 | 9b76d72cff7d429ff98f156c856076e11179465edb1de960636860095c255a34 |
|
MD5 | e74bb72eed89ad385688e411884af818 |
|
BLAKE2b-256 | 4957a1b90a6a1ca8b08a1725209cf600b55a3d98de7054b02233588305d0399f |
mountaineer-0.6.0-cp312-none-win_amd64.whl 的哈希值
算法 | 哈希摘要 | |
---|---|---|
SHA256 | 829b95449362728c1cd6f87d24056a75801bf3e2063bae53521117f43d8e29d7 |
|
MD5 | 8e3c74537ff918125ae09d01b773eefe |
|
BLAKE2b-256 | 24e990e1a57078817fafa17cd32a4bba5456a0ef260a408b1e387669e5d32800 |
哈希值 用于 mountaineer-0.6.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
算法 | 哈希摘要 | |
---|---|---|
SHA256 | 74af233f3662b08cff2c635e6dd5ce9da806e5fe50cb8b176e6dfc87906bbb74 |
|
MD5 | f2d4fadc0f56a18745c4e68d8b6d32f4 |
|
BLAKE2b-256 | 659acc9004c83ff6d54ecf4bb255042253001428e45eb333f78e01139ce25c21 |
哈希值 用于 mountaineer-0.6.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
算法 | 哈希摘要 | |
---|---|---|
SHA256 | 6fab1f6d177791124ea0942dbbf25c5c1cdaf73c0d068585075a64fc286d5efa |
|
MD5 | 574849ca75db2097ffc3dc61c1e41932 |
|
BLAKE2b-256 | f81323a89b7eef1785a95aba16258586aa94ef8d672b01a585b189c435525772 |
哈希值 用于 mountaineer-0.6.0-cp312-cp312-macosx_11_0_arm64.whl
算法 | 哈希摘要 | |
---|---|---|
SHA256 | facf7b1401050e91e13a9c8d65d5204975aa84e6d7c6b9fb3deb1161c0a538aa |
|
MD5 | bfd516cf128195ca8f90c5c884096986 |
|
BLAKE2b-256 | 6ab1b61c25a5facc7d8fd6a278228892a3acb3f4e988f27fe35981e6ea0f7536 |
哈希值 用于 mountaineer-0.6.0-cp312-cp312-macosx_10_12_x86_64.whl
算法 | 哈希摘要 | |
---|---|---|
SHA256 | 3cb1f9c7435f59833359162beee44513c47a64b9e11c5cefbfcd8ab2e25f7118 |
|
MD5 | 39df262ea1bacd2953c5edc4fdf9f8b6 |
|
BLAKE2b-256 | 9733cd9158692365b86de1cdf613007b1667ef9262291942283150dd63cdf6c3 |
哈希值 用于 mountaineer-0.6.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
算法 | 哈希摘要 | |
---|---|---|
SHA256 | b20cac66575073cd88d6c0eca28b4847b3f85b65a7f4cfc3d12863ecab0695bf |
|
MD5 | c56960312ed1afafd8a2b1cfc692a89d |
|
BLAKE2b-256 | cc2c6867592d1a94925b7947abf4d9d92ce8fbe82bb85d0a02b6f0acad831c0f |
哈希值 用于 mountaineer-0.6.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
算法 | 哈希摘要 | |
---|---|---|
SHA256 | 5a23b05ae5a2af1f345a6223f79a6e436fdc2494c7718ab02ed008e81f001492 |
|
MD5 | 4d37ea66a1f74b973f50f71dd9c384d7 |
|
BLAKE2b-256 | ba78afebe75f1616b3b9be0e390db5d8208edf3a3abd96b35278372fcca8b653 |
哈希值 用于 mountaineer-0.6.0-cp311-cp311-macosx_11_0_arm64.whl
算法 | 哈希摘要 | |
---|---|---|
SHA256 | bd6946f13d6874a315d043862562f83d8abf02e030cb59da1a4fc77bbce16ac8 |
|
MD5 | a433c0faddaf374852e5bf542c90ef65 |
|
BLAKE2b-256 | e438346082e65f9f131f1da5e7fc2ed4b9d7151f1fd445495f2c357b6b7b3eae |
哈希值 用于 mountaineer-0.6.0-cp311-cp311-macosx_10_12_x86_64.whl
算法 | 哈希摘要 | |
---|---|---|
SHA256 | 0049fdab73ac26eed4dceff0bafb9affe2a1c8543a612754842bf6bc4334f916 |
|
MD5 | 3cb474fa2be6cc37e9bb03dfbaf330e0 |
|
BLAKE2b-256 | c433079bacbf0b40eb059a24639563fc00edf7fba27e76d209ce7d68dbd802e6 |
哈希值 用于 mountaineer-0.6.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
算法 | 哈希摘要 | |
---|---|---|
SHA256 | 930c684f68a5b81efcb1a90d5af9f942b57e85746a4769da59c7c2bc902c4ccb |
|
MD5 | ef2dc27ae1e222c40324f9e368a6e354 |
|
BLAKE2b-256 | 5c590528c9a405f028c9069d3af7520827dde04ebe04ddf079e4effc6a2fc3fb |
哈希值 用于 mountaineer-0.6.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
算法 | 哈希摘要 | |
---|---|---|
SHA256 | ae7f63e69ba148e18125724a79ab6e56e42fd3ab4cb1574e9eee7cb09f4ed4f0 |
|
MD5 | b88629765f7e8cceca9765928fa625db |
|
BLAKE2b-256 | a3b0910476c7dcd015b30c7b5561b63e2b24683f331b38a8f0cece27431b45b5 |
哈希值 用于 mountaineer-0.6.0-cp310-cp310-macosx_11_0_arm64.whl
算法 | 哈希摘要 | |
---|---|---|
SHA256 | 4a194a83475f456bfde3c27ed9e81e62ae706a3aa38d42245859aabd8e02ab34 |
|
MD5 | c365bef4c3073488a414583ea477085f |
|
BLAKE2b-256 | 48d666b56013673a86ca96820a6a17a1b4afab66a2d6c89c0c45e64fcb67801f |
哈希值 for mountaineer-0.6.0-cp310-cp310-macosx_10_12_x86_64.whl
算法 | 哈希摘要 | |
---|---|---|
SHA256 | a133a388a3ff7c4daff7ab64c7c9d027741cfe19a73ea427f4f2c21d14b36a5d |
|
MD5 | 9e13d6c5ee0360e5470bbd0557a3b341 |
|
BLAKE2b-256 | 16f0d342cba6f852b1fa612d14b80d80b5ce898feb83d1866c451c25a84eebee |