大家好,我是木木。
今天给大家分享一个轻巧的 Python 库,lektor。
lektor
如果你想做博客、文档站或长期维护的小型内容站,但又不想一上来就背完整数据库和后台系统的重量,Lektor 这类“文件即内容、构建即发布”的路线会很顺。它既有静态站生成器的轻量感,也保留了一套内建管理界面和内容模型,让内容团队和开发者都能在同一份项目里协作。
项目地址:https://github.com/lektor/lektor
官方文档:https://www.getlektor.com/docs/
三大特点
文件即内容
页面、附件和目录结构天然对应,博客、文档站和作品集都很容易落地。
多语言自然
alt、slug 和 URL 可以一起走,做双语站时不用自己再拼一层路由逻辑。
构建链路完整
Markdown、附件和图片会在生成阶段统一改写成最终站点里的可发布链接。
最佳实践
安装方式:python -m pip install Lektor
Lektor 很适合那种“内容文件想放在 Git 里,但编辑体验也不能太原始”的项目。这篇我不去起完整本地站,而是直接拿仓库自带的 demo-project 做最小实验,看它怎么把多语言内容、分页列表和构建输出串成一条真正能交付的静态站链路。
功能一:同一条内容记录可以映射出不同语言下的 slug 和 URL
这段代码解决什么问题:很多静态站工具能做多语言,但一旦你希望德语页面和英语页面不仅文案不同、连 slug 也不同,路由就开始变得别扭。Lektor 的记录路径和语言 URL 是分开的,所以同一条内容既能共用内容主键,又能各自长出合适的前台地址。
importsysfrompathlibimportPathsys.path.insert(0,str(Path.cwd()/"repo"))fromlektor.projectimportProject# noqa: E402fromlektor.environmentimportEnvironment# noqa: E402fromlektor.dbimportDatabase,get_alts# noqa: E402project=Project.from_path(Path.cwd()/"repo"/"tests"/"demo-project")env=Environment(project,load_plugins=False)pad=Database(env).new_pad()slave_en=pad.resolve_url_path("/projects/slave/")slave_de=pad.resolve_url_path("/de/projects/sklave/")wolf_de=pad.get("/projects/wolf",alt="de")print("EN :",slave_en.alt,slave_en.path,slave_en["name"],slave_en.url_path)print("DE :",slave_de.alt,slave_de.path,slave_de["name"],slave_de.url_path)print("alts :",get_alts(slave_en))print("fallback:",wolf_de.alt,wolf_de["_source_alt"],wolf_de["name"],get_alts(wolf_de,fallback=True),)print("cross :",slave_en.url_to(slave_de))
这类设计很适合长期内容站。内容主键还是同一条记录,但前台 URL 可以按语言独立演进,缺失翻译时又能回落到主语言,不至于让整条内容链断掉。
功能二:分页不是简单切片,而是和内容筛选、URL 规则绑在一起
这段代码解决什么问题:很多人做静态站时,真正麻烦的不是“生成 HTML”,而是栏目列表怎么分页、怎么排除不该上列表的内容、第二页 URL 长什么样。Lektor 把这些规则放进内容模型里,读出来就是站点行为。
importsysfrompathlibimportPathsys.path.insert(0,str(Path.cwd()/"repo"))fromlektor.projectimportProject# noqa: E402fromlektor.environmentimportEnvironment# noqa: E402fromlektor.dbimportDatabase# noqa: E402project=Project.from_path(Path.cwd()/"repo"/"tests"/"demo-project")env=Environment(project,load_plugins=False)pad=Database(env).new_pad()forpage_numin[1,2]:page=pad.get("/projects",page_num=page_num)names=[item["name"]foriteminpage.pagination.items.all()]print(f"PAGE {page_num} -> {page.url_path}")print("items :",names)all_names=[item["name"]foriteminpad.get("/projects").pagination.items.all()]print("ALL :",all_names)
这里最有意思的是,被过滤掉的内容根本不会混进分页结果里,第二页 URL 也跟内容模型保持一致。对博客、案例集、作品列表这种页面来说,这种“模型即规则”的味道非常对。
环境与版本信息
- 本文 demo 环境:Windows 11,Python 3.11,直接基于
lektor/lektor 仓库源码运行 - 为了跑通仓库核心模块,本地补齐了
python-slugify、inifile、watchfiles、marshmallow、mistune 2.x 等依赖 - 仓库当前活跃状态:GitHub
pushed_at 为 2026-04-05T20:29:32Z - 官方包要求:
pyproject.toml 当前声明 Python >=3.10
高级功能
这段代码解决什么问题:静态站真正省心的地方,不只是能把 Markdown 转成 HTML,而是构建阶段会把相对附件、文章内图片这些内容引用自动改写成最终站点里的可访问路径。Lektor 在构建页面时把这件事一起做了,编辑写内容时就不用满脑子算发布后路径。
importsysimporttempfilefrompathlibimportPathsys.path.insert(0,str(Path.cwd()/"repo"))fromlektor.projectimportProject# noqa: E402fromlektor.environmentimportEnvironment# noqa: E402fromlektor.dbimportDatabase# noqa: E402fromlektor.builderimportBuilder# noqa: E402project=Project.from_path(Path.cwd()/"repo"/"tests"/"demo-project")env=Environment(project,load_plugins=False)pad=Database(env).new_pad()blog_index=pad.get("/blog",page_num=1)blog_post=pad.get("/blog/post1")withtempfile.TemporaryDirectory()astmp:builder=Builder(pad,tmp)prog1,_=builder.build(blog_index)withprog1.artifacts[0].open("rb")asf:html1=f.read().decode("utf-8")prog2,_=builder.build(blog_post)withprog2.artifacts[0].open("rb")asf:html2=f.read().decode("utf-8")print("INDEX_LINK :","2015/12/post1/hello.txt"inhtml1)print("INDEX_IMAGE:","2015/12/post1/logo.png"inhtml1)print("POST_LINK :",'href=\"hello.txt\"'inhtml2)print("POST_IMAGE :",'src=\"logo.png\"'inhtml2)forlineinhtml2.splitlines():if"attachment"inlineor"logo.png"inline:print(line.strip())

这个能力对内容团队很友好。写文章的人只管在内容目录里放附件、写相对链接,真正发布时由构建器改写成最终站点路径。站点结构一旦调整,维护成本也比手工拼 URL 小得多。
适用场景
- 你要做博客、文档站、作品集或品牌官网,并且希望内容文件能直接进 Git
- 团队更习惯“目录 + Markdown + 模板”的工作方式,但又想要一个现成后台辅助编辑
- 站点需要双语或多语言,并且前台 slug、分页、附件路径都想保持可控
不适用场景
- 你需要高度实时的协作编辑、复杂权限审批或强依赖数据库事务的后台系统
- 内容模型会频繁依赖跨表查询、在线搜索和用户写入,不只是静态发布
- 团队完全不想接触模板、目录结构和构建流程,只想要托管式可视化 CMS
上线检查
- 先确认 Python 版本和
mistune、watchfiles 这些核心依赖与当前仓库要求一致 - 用一篇真实文章检查分页、附件链接和图片路径,确认构建后 URL 没有跑偏
- 如果要上多语言站点,先把各语言 slug 和 fallback 规则做一轮 smoke test
总结
如果你想要一个轻量、内容结构清楚、又不至于把编辑体验完全交给命令行的静态 CMS,Lektor 很值得认真试一遍。