diskdantic
Instead of having an ORM on top of a database, why not have a collection on top of a folder?
Disk-backed collections powered by Pydantic models. This is pretty much the whole API:
from datetime import date
from pydantic import BaseModel
from diskdantic import Collection
class BlogPost(BaseModel):
title: str
date: date
tags: list[str]
draft: bool = False
content: str
posts = Collection(
BlogPost,
path="./blog/posts",
format="markdown", # required when the folder is empty
body_field="content", # required when format is markdown (for the body)
)
recent = posts.filter(lambda post: not post.draft).order_by("-date").head(3)
for post in recent:
print(post.title)
new_post = BlogPost(
title="Hello World",
date=date.today(),
tags=["intro"],
content="# Hello\n\nIt works!",
)
posts.add(new_post)
There are also loads of utility functions.
# Get all published posts
published = posts.filter(lambda post: not post.draft)
# Get posts with specific tag
intro_posts = posts.filter(lambda post: "intro" in post.tags)
# Chain filters
recent_published = posts.filter(lambda p: not p.draft).filter(lambda p: p.date.year == 2025)
It's meant to work with markdown files, but it should also work with yaml/json.
API Reference
Query Methods
filter(predicate)- Filter items by a predicate functionorder_by(field)- Sort by field (prefix with-for descending)head(n=5)- Get first n itemstail(n=5)- Get last n itemsto_list()- Materialize query to listcount()- Count matching itemsfirst()- Get first item or Nonelast()- Get last item or Noneexists(predicate=None)- Check if any items matchget(filename)- Load specific file by name
Lifecycle Methods
add(model, path=None)- Add new model to collection (returns Path)update(model)- Update existing model on disk (returns Path)upsert(model)- Add if new, update if exists (returns Path)delete(target)- Delete by model, filename, or Pathrefresh(model)- Reload model from diskpath_for(model)- Get disk path for a model
Iteration
Collections are iterable and return model instances:
for post in posts:
print(post.title)
Why?
It makes it easier to write a custom CMS on top of your disk, which is nice. But it also feels like a fun thing that should exist. It's mainly a fun brainfart for now, but I can see some areas where I might make it better too.
- Figure out a nice API for a nested collection. The library has one now, but undocumented for a reason.
- Maybe make it more performant by seeing how far I can push the lazy loading. Though I doubt this will be worth it.
Metadata
Release files for diskdantic 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| diskdantic-0.1.0.tar.gz | 27.7 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| diskdantic-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 37.4 kB
Release files / diskdantic-0.1.0.tar.gz
| Download URL | diskdantic-0.1.0.tar.gz |
|---|---|
| Size | 27.7 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
6436eac963211b7b43258d60a0c072629cda735233ae37b125f25401ef9bf5aa
|
|
BLAKE2b-256 checksum How to use checksums |
d9b2b1d7273d13d1862d5b70d51645ee01a7fd1b0214cb887a9eed534c0d3614
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.7.13
|
Release files / diskdantic-0.1.0-py3-none-any.whl
| Download URL | diskdantic-0.1.0-py3-none-any.whl |
|---|---|
| Size | 9.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
a890c981b9bce8e34aa8c7ae78fb163c4df783d80efaf76787f03b96de359106
|
|
BLAKE2b-256 checksum How to use checksums |
41da856c8e042e198f7af90c5c66cae11d1e9e95dff2de801d328b93b3dc94bd
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.7.13
|