Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

revamp of adding type hints for SQLAlchemy models #9

Draft
wants to merge 5 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions python/typing-models/flask-sqlalchemy-typing/.envrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
source_url "https://raw.githubusercontent.com/cachix/devenv/95f329d49a8a5289d31e0982652f7058a189bfca/direnvrc" "sha256-d+8cBpDfDBj41inrADaJt+bDWhOktwslgoP5YiGJ1v0="

use devenv
9 changes: 9 additions & 0 deletions python/typing-models/flask-sqlalchemy-typing/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Devenv
.devenv*
devenv.local.nix

# direnv
.direnv

# pre-commit
.pre-commit-config.yaml
11 changes: 11 additions & 0 deletions python/typing-models/flask-sqlalchemy-typing/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Flask-SQLAlchemy models

## Requirements

* `devenv`

## Start

```
devenv shell
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We might want to add a classical requirements.txt flow for non-Nix users. WDYT?

```
94 changes: 94 additions & 0 deletions python/typing-models/flask-sqlalchemy-typing/app/main.py
Original file line number Diff line number Diff line change
@@ -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)
Comment on lines +64 to +66
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We might want to wrap these reveal_type calls in an if TYPE_CHECKING: conditional to avoid runtime errors. WDYT?

Suggested change
reveal_type(get_all)
reveal_type(get_unread)
if TYPE_CHECKING:
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':
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we add instructions about this in the README?

print("Creating db...")
with app.app_context():
db.create_all()
sys.exit(0)

app.run(debug=True)
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Notification Form</title>
</head>
<body>
<h1>Notification Form</h1>
<form action="{{ url_for('notifications') }}" method="POST">
<label for="description">Description:</label>
<textarea id="description" name="description" placeholder="Enter description (optional)"></textarea>
<br><br>

<label for="email">Email:</label>
<input type="email" id="email" name="email" required>
<br><br>

<label for="date">Date:</label>
<input type="datetime-local" id="date" name="date" required>
<br><br>

<label for="url">URL:</label>
<input type="url" id="url" name="url" placeholder="Enter URL (optional)">
<br><br>

<label for="read">Read:</label>
<input type="checkbox" id="read" name="read">
<br><br>

<input type="submit" value="Submit Notification">
</form>
</body>
</html>
160 changes: 160 additions & 0 deletions python/typing-models/flask-sqlalchemy-typing/devenv.lock
Original file line number Diff line number Diff line change
@@ -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
}
16 changes: 16 additions & 0 deletions python/typing-models/flask-sqlalchemy-typing/devenv.nix
Original file line number Diff line number Diff line change
@@ -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";
};
};
}
8 changes: 8 additions & 0 deletions python/typing-models/flask-sqlalchemy-typing/devenv.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
inputs:
nixpkgs:
url: github:cachix/devenv-nixpkgs/rolling
nixpkgs-python:
url: github:cachix/nixpkgs-python
inputs:
nixpkgs:
follows: nixpkgs
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need this file in the repository?

Binary file not shown.
12 changes: 12 additions & 0 deletions python/typing-models/flask-sqlalchemy-typing/requirements.txt
Original file line number Diff line number Diff line change
@@ -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