diff --git a/python/typing-models/flask-sqlalchemy-typing/.envrc b/python/typing-models/flask-sqlalchemy-typing/.envrc
new file mode 100644
index 0000000..5bf8fc1
--- /dev/null
+++ b/python/typing-models/flask-sqlalchemy-typing/.envrc
@@ -0,0 +1,3 @@
+source_url "https://raw.githubusercontent.com/cachix/devenv/95f329d49a8a5289d31e0982652f7058a189bfca/direnvrc" "sha256-d+8cBpDfDBj41inrADaJt+bDWhOktwslgoP5YiGJ1v0="
+
+use devenv
\ No newline at end of file
diff --git a/python/typing-models/flask-sqlalchemy-typing/.gitignore b/python/typing-models/flask-sqlalchemy-typing/.gitignore
new file mode 100644
index 0000000..4d058db
--- /dev/null
+++ b/python/typing-models/flask-sqlalchemy-typing/.gitignore
@@ -0,0 +1,9 @@
+# Devenv
+.devenv*
+devenv.local.nix
+
+# direnv
+.direnv
+
+# pre-commit
+.pre-commit-config.yaml
diff --git a/python/typing-models/flask-sqlalchemy-typing/README.md b/python/typing-models/flask-sqlalchemy-typing/README.md
new file mode 100644
index 0000000..e723dcf
--- /dev/null
+++ b/python/typing-models/flask-sqlalchemy-typing/README.md
@@ -0,0 +1,11 @@
+# Flask-SQLAlchemy models
+
+## Requirements
+
+* `devenv`
+
+## Start
+
+```
+devenv shell
+```
diff --git a/python/typing-models/flask-sqlalchemy-typing/app/main.py b/python/typing-models/flask-sqlalchemy-typing/app/main.py
new file mode 100644
index 0000000..2e59a14
--- /dev/null
+++ b/python/typing-models/flask-sqlalchemy-typing/app/main.py
@@ -0,0 +1,94 @@
+from datetime import datetime
+import sys
+from typing import Optional, TypedDict
+
+from flask import Flask, Response, jsonify, render_template, request
+
+from flask_sqlalchemy import SQLAlchemy
+
+from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column
+
+# Web Application
+
+app = Flask(__name__)
+
+# Database
+
+class Base(DeclarativeBase):
+ pass
+
+db = SQLAlchemy(model_class=Base)
+
+app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///project.db"
+
+db.init_app(app)
+
+
+class NotificationModel(TypedDict):
+ id: int
+ description: Optional[str]
+ email: str
+ date: datetime
+ url: Optional[str]
+ read: bool
+
+
+class Notification(db.Model):
+ __tablename__ = "notifications"
+
+ id: Mapped[int] = mapped_column(primary_key=True, index=True)
+ description: Mapped[str]
+ email: Mapped[str] = mapped_column(nullable=False)
+ date: Mapped[datetime] = mapped_column(nullable=False)
+ url: Mapped[str]
+ read: Mapped[bool] = mapped_column(default=False)
+
+ def to_dict(self) -> NotificationModel:
+ return {
+ "id": self.id,
+ "description": self.description,
+ "email": self.email,
+ "date": self.date,
+ "url": self.url,
+ "read": self.read,
+ }
+
+
+def get_all():
+ return db.session.query(Notification).all()
+
+
+def get_unread():
+ return db.session.query(Notification).filter(Notification.read.is_(False)).all()
+
+reveal_type(get_all)
+
+reveal_type(get_unread)
+
+
+@app.route("/", methods=["GET"])
+def root():
+ return render_template("root.html")
+
+
+@app.route("/notifications", methods=["GET", "POST"])
+def notifications() -> Response:
+ if request.method == "POST":
+ new_notification = Notification(
+ **dict(request.form, date=datetime.fromisoformat(request.form["date"]))
+ )
+ db.session.add(new_notification)
+ db.session.commit()
+
+ notifications = get_all()
+ return jsonify([notification.to_dict() for notification in notifications])
+
+
+if __name__ == "__main__":
+ if len(sys.argv) > 1 and sys.argv[1] == 'db':
+ print("Creating db...")
+ with app.app_context():
+ db.create_all()
+ sys.exit(0)
+
+ app.run(debug=True)
diff --git a/python/typing-models/flask-sqlalchemy-typing/app/templates/root.html b/python/typing-models/flask-sqlalchemy-typing/app/templates/root.html
new file mode 100644
index 0000000..787016c
--- /dev/null
+++ b/python/typing-models/flask-sqlalchemy-typing/app/templates/root.html
@@ -0,0 +1,34 @@
+
+
+
+
+
+ Notification Form
+
+
+ Notification Form
+
+
+
diff --git a/python/typing-models/flask-sqlalchemy-typing/devenv.lock b/python/typing-models/flask-sqlalchemy-typing/devenv.lock
new file mode 100644
index 0000000..e5233a2
--- /dev/null
+++ b/python/typing-models/flask-sqlalchemy-typing/devenv.lock
@@ -0,0 +1,160 @@
+{
+ "nodes": {
+ "devenv": {
+ "locked": {
+ "dir": "src/modules",
+ "lastModified": 1727098005,
+ "owner": "cachix",
+ "repo": "devenv",
+ "rev": "f318d27a4637aff765a378106d82dfded124c3b3",
+ "treeHash": "c77efa71afb25615542aed9d7e805a3b6216b6a2",
+ "type": "github"
+ },
+ "original": {
+ "dir": "src/modules",
+ "owner": "cachix",
+ "repo": "devenv",
+ "type": "github"
+ }
+ },
+ "flake-compat": {
+ "flake": false,
+ "locked": {
+ "lastModified": 1696426674,
+ "owner": "edolstra",
+ "repo": "flake-compat",
+ "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33",
+ "treeHash": "2addb7b71a20a25ea74feeaf5c2f6a6b30898ecb",
+ "type": "github"
+ },
+ "original": {
+ "owner": "edolstra",
+ "repo": "flake-compat",
+ "type": "github"
+ }
+ },
+ "flake-compat_2": {
+ "flake": false,
+ "locked": {
+ "lastModified": 1696426674,
+ "owner": "edolstra",
+ "repo": "flake-compat",
+ "rev": "0f9255e01c2351cc7d116c072cb317785dd33b33",
+ "treeHash": "2addb7b71a20a25ea74feeaf5c2f6a6b30898ecb",
+ "type": "github"
+ },
+ "original": {
+ "owner": "edolstra",
+ "repo": "flake-compat",
+ "type": "github"
+ }
+ },
+ "gitignore": {
+ "inputs": {
+ "nixpkgs": [
+ "pre-commit-hooks",
+ "nixpkgs"
+ ]
+ },
+ "locked": {
+ "lastModified": 1709087332,
+ "owner": "hercules-ci",
+ "repo": "gitignore.nix",
+ "rev": "637db329424fd7e46cf4185293b9cc8c88c95394",
+ "treeHash": "ca14199cabdfe1a06a7b1654c76ed49100a689f9",
+ "type": "github"
+ },
+ "original": {
+ "owner": "hercules-ci",
+ "repo": "gitignore.nix",
+ "type": "github"
+ }
+ },
+ "nixpkgs": {
+ "locked": {
+ "lastModified": 1716977621,
+ "owner": "cachix",
+ "repo": "devenv-nixpkgs",
+ "rev": "4267e705586473d3e5c8d50299e71503f16a6fb6",
+ "treeHash": "6d9f1f7ca0faf1bc2eeb397c78a49623260d3412",
+ "type": "github"
+ },
+ "original": {
+ "owner": "cachix",
+ "ref": "rolling",
+ "repo": "devenv-nixpkgs",
+ "type": "github"
+ }
+ },
+ "nixpkgs-python": {
+ "inputs": {
+ "flake-compat": "flake-compat",
+ "nixpkgs": [
+ "nixpkgs"
+ ]
+ },
+ "locked": {
+ "lastModified": 1722978926,
+ "owner": "cachix",
+ "repo": "nixpkgs-python",
+ "rev": "7c550bca7e6cf95898e32eb2173efe7ebb447460",
+ "treeHash": "d9d38ef1b6fc92be18170b74e9889a7ab9174f6e",
+ "type": "github"
+ },
+ "original": {
+ "owner": "cachix",
+ "repo": "nixpkgs-python",
+ "type": "github"
+ }
+ },
+ "nixpkgs-stable": {
+ "locked": {
+ "lastModified": 1726969270,
+ "owner": "NixOS",
+ "repo": "nixpkgs",
+ "rev": "23cbb250f3bf4f516a2d0bf03c51a30900848075",
+ "treeHash": "f150876866adcc3af432c103db116c2f516f49b1",
+ "type": "github"
+ },
+ "original": {
+ "owner": "NixOS",
+ "ref": "nixos-24.05",
+ "repo": "nixpkgs",
+ "type": "github"
+ }
+ },
+ "pre-commit-hooks": {
+ "inputs": {
+ "flake-compat": "flake-compat_2",
+ "gitignore": "gitignore",
+ "nixpkgs": [
+ "nixpkgs"
+ ],
+ "nixpkgs-stable": "nixpkgs-stable"
+ },
+ "locked": {
+ "lastModified": 1726745158,
+ "owner": "cachix",
+ "repo": "pre-commit-hooks.nix",
+ "rev": "4e743a6920eab45e8ba0fbe49dc459f1423a4b74",
+ "treeHash": "56fbe2a9610b3ad9163a74011131e7624f6b3b81",
+ "type": "github"
+ },
+ "original": {
+ "owner": "cachix",
+ "repo": "pre-commit-hooks.nix",
+ "type": "github"
+ }
+ },
+ "root": {
+ "inputs": {
+ "devenv": "devenv",
+ "nixpkgs": "nixpkgs",
+ "nixpkgs-python": "nixpkgs-python",
+ "pre-commit-hooks": "pre-commit-hooks"
+ }
+ }
+ },
+ "root": "root",
+ "version": 7
+}
diff --git a/python/typing-models/flask-sqlalchemy-typing/devenv.nix b/python/typing-models/flask-sqlalchemy-typing/devenv.nix
new file mode 100644
index 0000000..83c02ab
--- /dev/null
+++ b/python/typing-models/flask-sqlalchemy-typing/devenv.nix
@@ -0,0 +1,16 @@
+{ pkgs, lib, config, inputs, ... }:
+
+{
+ packages = [
+ pkgs.sqlite
+ ];
+
+ languages.python = {
+ enable = true;
+ version = "3.12.0";
+ venv = {
+ enable = true;
+ requirements = "requirements.txt";
+ };
+ };
+}
diff --git a/python/typing-models/flask-sqlalchemy-typing/devenv.yaml b/python/typing-models/flask-sqlalchemy-typing/devenv.yaml
new file mode 100644
index 0000000..184b866
--- /dev/null
+++ b/python/typing-models/flask-sqlalchemy-typing/devenv.yaml
@@ -0,0 +1,8 @@
+inputs:
+ nixpkgs:
+ url: github:cachix/devenv-nixpkgs/rolling
+ nixpkgs-python:
+ url: github:cachix/nixpkgs-python
+ inputs:
+ nixpkgs:
+ follows: nixpkgs
diff --git a/python/typing-models/flask-sqlalchemy-typing/instance/project.db b/python/typing-models/flask-sqlalchemy-typing/instance/project.db
new file mode 100644
index 0000000..310aa4d
Binary files /dev/null and b/python/typing-models/flask-sqlalchemy-typing/instance/project.db differ
diff --git a/python/typing-models/flask-sqlalchemy-typing/requirements.txt b/python/typing-models/flask-sqlalchemy-typing/requirements.txt
new file mode 100644
index 0000000..f37be4b
--- /dev/null
+++ b/python/typing-models/flask-sqlalchemy-typing/requirements.txt
@@ -0,0 +1,12 @@
+blinker==1.8.2
+click==8.1.7
+Flask==3.0.3
+Flask-SQLAlchemy==3.1.1
+itsdangerous==2.2.0
+Jinja2==3.1.4
+MarkupSafe==2.1.5
+nodeenv==1.9.1
+pyright==1.1.383
+SQLAlchemy==2.0.35
+typing_extensions==4.12.2
+Werkzeug==3.0.4