{"id":23734033,"url":"https://github.com/trialandsuccess/typedal","last_synced_at":"2026-04-20T19:01:50.474Z","repository":{"id":63958369,"uuid":"572112516","full_name":"trialandsuccess/TypeDAL","owner":"trialandsuccess","description":"Typing-friendly database abstraction based on pydal","archived":false,"fork":false,"pushed_at":"2025-08-29T15:02:22.000Z","size":746,"stargazers_count":10,"open_issues_count":1,"forks_count":1,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-08-29T17:19:37.240Z","etag":null,"topics":["orm","py4web","pydal","python","web2py"],"latest_commit_sha":null,"homepage":"https://typedal.readthedocs.io/en/stable/","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/trialandsuccess.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2022-11-29T15:23:12.000Z","updated_at":"2025-08-29T15:02:24.000Z","dependencies_parsed_at":"2023-10-23T10:40:06.590Z","dependency_job_id":"c15f76dd-1bb5-4ee8-a51c-a733ef4b5377","html_url":"https://github.com/trialandsuccess/TypeDAL","commit_stats":{"total_commits":18,"total_committers":1,"mean_commits":18.0,"dds":0.0,"last_synced_commit":"308ea14f38d4611b626c0edaf737ee53467b25b2"},"previous_names":[],"tags_count":95,"template":false,"template_full_name":null,"purl":"pkg:github/trialandsuccess/TypeDAL","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/trialandsuccess%2FTypeDAL","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/trialandsuccess%2FTypeDAL/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/trialandsuccess%2FTypeDAL/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/trialandsuccess%2FTypeDAL/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/trialandsuccess","download_url":"https://codeload.github.com/trialandsuccess/TypeDAL/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/trialandsuccess%2FTypeDAL/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":273583404,"owners_count":25131815,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-09-04T02:00:08.968Z","response_time":61,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["orm","py4web","pydal","python","web2py"],"created_at":"2024-12-31T05:33:28.828Z","updated_at":"2026-03-13T21:03:05.149Z","avatar_url":"https://github.com/trialandsuccess.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# TypeDAL\n\n[![PyPI - Version](https://img.shields.io/pypi/v/TypeDAL.svg)](https://pypi.org/project/TypeDAL)\n[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/TypeDAL.svg)](https://pypi.org/project/TypeDAL)  \n[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)  \n[![su6 checks](https://github.com/trialandsuccess/TypeDAL/actions/workflows/su6.yml/badge.svg?branch=development)](https://github.com/trialandsuccess/TypeDAL/actions)\n![coverage.svg](coverage.svg)\n\nTyping support for [PyDAL](http://web2py.com/books/default/chapter/29/6).\nThis package aims to improve the typing support for PyDAL. By using classes instead of the define_table method,\ntype hinting the result of queries can improve the experience while developing. In the background, the queries are still\ngenerated and executed by pydal itself, this package only provides some logic to properly pass calls from class methods to\nthe underlying `db.define_table` pydal Tables.\n\n- `TypeDAL` is the replacement class for DAL that manages the code on top of DAL.\n- `TypedTable` must be the parent class of any custom Tables you define (e.g. `class SomeTable(TypedTable)`)\n- `TypedField` can be used instead of Python native types when extra settings (such as `default`) are required (\n  e.g. `name = TypedField(str, default=\"John Doe\")`). It can also be used in an annotation (`name: TypedField[str]`) to\n  improve\n  editor support over only annotating with `str`.\n- `TypedRows`: can be used as the return type annotation of pydal's `.select()` and subscribed with the actual table\n  class, so\n  e.g. `rows: TypedRows[SomeTable] = db(...).select()`. When using the QueryBuilder, a `TypedRows` instance is returned\n  by `.collect()`.\n\nVersion 2.0 also introduces more ORM-like functionality.\nMost notably, a Typed Query Builder that sees your table classes as models with relationships to each other.\nSee [3. Building Queries](https://typedal.readthedocs.io/en/stable/3_building_queries/) for more\ndetails.\n\n## Quickstart\n\n```bash\nuv pip install typedal\n# alternative:\npip install typedal\n```\n\n```python\nfrom typedal import TypeDAL, TypedTable\n\ndb = TypeDAL(\"sqlite:memory\")\n# Alternatives:\n# db = TypeDAL(\"sqlite://storage.sqlite\")\n# db = TypeDAL(\"postgres://user:password@localhost:5432/mydb\")\n# db = TypeDAL(\"mysql://user:password@localhost:3306/mydb\")\n# ...\n\n@db.define()\nclass User(TypedTable):\n    name: str\n    age: int | None\n\n\nUser.insert(name=\"Alice\", age=30)\nadults = User.where(User.age \u003e= 18).collect()\nprint(adults.column(\"name\"))  # ['Alice']\n```\n\nIf you are new to TypeDAL, start with:\n\n1. [Getting Started](https://typedal.readthedocs.io/en/stable/1_getting_started/)\n2. [Defining Tables](https://typedal.readthedocs.io/en/stable/2_defining_tables/)\n3. [Building Queries](https://typedal.readthedocs.io/en/stable/3_building_queries/)\n4. [Relationships](https://typedal.readthedocs.io/en/stable/4_relationships/)\n\n## CLI\nThe TypeDAL CLI provides a convenient interface for generating SQL migrations for [edwh-migrate](https://github.com/educationwarehouse/migrate#readme)\nfrom PyDAL or TypeDAL configurations using [pydal2sql](https://github.com/robinvandernoord/pydal2sql). \nIt offers various commands to streamline database management tasks.\n\n### Usage\n\n```bash\ntypedal --help\n```\n\n## Options\n\n- `--show-config`: Toggle to show configuration details. Default is `no-show-config`.\n- `--version`: Toggle to display version information. Default is `no-version`.\n- `--install-completion`: Install completion for the current shell.\n- `--show-completion`: Show completion for the current shell, for copying or customization.\n- `--help`: Display help message and exit.\n\n## Commands\n\n- `cache.clear`: Clear expired items from the cache.\n- `cache.stats`: Show caching statistics.\n- `migrations.fake`: Mark one or more migrations as completed in the database without executing the SQL code.\n- `migrations.generate`: Run `pydal2sql` based on the TypeDAL configuration.\n- `migrations.run`: Run `edwh-migrate` based on the TypeDAL configuration.\n- `setup`: Interactively setup a `[tool.typedal]` entry in the local `pyproject.toml`.\n\n### Configuration\n\nTypeDAL and its CLI can be configured via `pyproject.toml`.  \nSee [6. Migrations](https://typedal.readthedocs.io/en/stable/6_migrations/) for more information about configuration.\n\n\n## TypeDAL for PyDAL users - Quick Overview\n\nBelow you'll find a quick overview of translation from pydal to TypeDAL.  \nFor more info, see **[the docs](https://typedal.readthedocs.io/en/latest/)**.\n\n---\n\n### Translations from pydal to typedal\n\n\u003ctable\u003e\n\u003ctr\u003e\n\u003ctd\u003eDescription\u003c/td\u003e\n\u003ctd\u003e pydal \u003c/td\u003e \u003ctd\u003e typedal \u003c/td\u003e \u003ctd\u003e typedal alternative(s) \u003c/td\u003e \u003ctd\u003e ... \u003c/td\u003e\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctr\u003e\n\u003ctd\u003eSetup\u003c/td\u003e\n\u003ctd\u003e\n\n```python\nfrom pydal import DAL, Field\n\ndb = DAL(...)\n```\n\n\u003c/td\u003e\n\n\u003ctd\u003e\n\n```python\nfrom typedal import TypeDAL, TypedTable, TypedField\n\ndb = TypeDAL(...)\n```\n\n\u003c/td\u003e\n\n\u003c/tr\u003e\n\u003ctr\u003e\n\u003ctd\u003eTable Definitions\u003c/td\u003e\n\u003ctd\u003e\n\n```python\ndb.define_table(\"table_name\",\n                Field(\"fieldname\", \"string\", required=True),\n                Field(\"otherfield\", \"float\"),\n                Field(\"yet_another\", \"text\", default=\"Something\")\n                )\n```\n\n\u003c/td\u003e\n\n\u003ctd\u003e\n\n```python\n@db.define\nclass TableName(TypedTable):\n    fieldname: str\n    otherfield: float | None\n    yet_another = TypedField(str, type=\"text\", default=\"something\", required=False)\n```\n\n\u003c/td\u003e\n\n\u003ctd\u003e\n\n```python\nimport typing\n\n\nclass TableName(TypedTable):\n    fieldname: TypedField[str]\n    otherfield: TypedField[typing.Optional[float]]\n    yet_another = TextField(default=\"something\", required=False)\n\n\ndb.define(TableName)\n```\n\n\u003c/td\u003e\n\u003c/tr\u003e\n\n\u003ctr\u003e\n\u003ctd\u003eInsert\u003c/td\u003e\n\n\u003ctd\u003e\n\n```python\ndb.table_name.insert(fieldname=\"value\")\n```\n\n\u003c/td\u003e\n\n\u003ctd\u003e\n\n```python\nTableName.insert(fieldname=\"value\")\n```\n\n\u003ctd\u003e\n\n```python\n# the old syntax is also still supported:\ndb.table_name.insert(fieldname=\"value\")\n```\n\n\u003c/td\u003e\n\u003c/tr\u003e\n\n\u003ctr\u003e\n\u003ctd\u003e(quick) Select\u003c/td\u003e\n\n\n\u003ctd\u003e\n\n```python\n# all:\nall_rows = db(db.table_name).select()  # -\u003e Any (Rows)\n# some:\nrows = db((db.table_name.id \u003e 5) \u0026 (db.table_name.id \u003c 50)).select(db.table_name.id)\n# one:\nrow = db.table_name(id=1)  # -\u003e Any (Row)\n```\n\n\u003c/td\u003e\n\n\u003ctd\u003e\n\n```python\n# all:\nall_rows = TableName.collect()  # or .all()\n# some:\n# order of select and where is interchangeable here\nrows = TableName.select(Tablename.id).where(TableName.id \u003e 5).where(TableName.id \u003c 50).collect()\n# one:\nrow = TableName(id=1)  # or .where(...).first()\n\n```\n\n\u003ctd\u003e\n\n```python\n# you can also still use the old syntax and type hint on top of it;\n# all:\nall_rows: TypedRows[TableName] = db(db.table_name).select()\n# some:\nrows: TypedRows[TableName] = db((db.table_name.id \u003e 5) \u0026 (db.table_name.id \u003c 50)).select(db.table_name.id)\n# one:\nrow: TableName = db.table_name(id=1)\n```\n\n\u003c/td\u003e\n\n\n\u003c/tr\u003e\n\n\u003c/table\u003e\n\n\n\u003c!-- \n\u003ctd\u003e\n\n```python\n\n```\n\n\u003c/td\u003e\n\n\u003ctd\u003e\n\n\u003ctd\u003e\n\n```python\n\n```\n\n\u003c/td\u003e\n\u003c/tr\u003e\n--\u003e\n\n### All Types\n\nSee [2. Defining Tables](https://typedal.readthedocs.io/en/stable/2_defining_tables/)\n\n### Helpers\n\nTypeDAL provides some utility functions to interact with the underlying pyDAL objects:\n\n- **`get_db(TableName)`**:  \n  Retrieve the DAL instance associated with a given TypedTable or pyDAL Table.\n\n- **`get_table(TableName)`**:  \n  Access the original PyDAL Table from a TypedTable instance (`db.table_name`).\n\n- **`get_field(TableName.fieldname)`**:  \n  Get the pyDAL Field from a TypedField. This ensures compatibility when interacting directly with PyDAL.\n\nThese helpers are useful for scenarios where direct access to the PyDAL objects is needed while still using TypeDAL.\nAn example of this is when you need to do a `db.commit()` but you can't import `db` directly:\n\n```python\nfrom typedal.helpers import get_db #, get_table, get_field\n\nMyTable.insert(...)\ndb = get_db(MyTable)\ndb.commit() # this is usually done automatically but sometimes you want to manually commit.\n```\n\n## Caveats\n\n- Some editors (notably PyCharm) cannot always distinguish class-level and instance-level access on the same symbol.\n  For example, `Model.somefield` is a field descriptor (query operations like `.belongs()`), while\n  `model.somefield` is the runtime value (for example `list[str]`).\n- `TypedField` limitations; Since pydal implements some magic methods to perform queries, some features of typing will\n  not work on a typed field: `typing.Optional` or a union (`Field() | None`) will result in errors. The only way to make\n  a typedfield optional right now, would be to set `required=False` as an argument yourself. This is also a reason\n  why `typing.get_type_hints` is not a complete solution.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftrialandsuccess%2Ftypedal","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftrialandsuccess%2Ftypedal","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftrialandsuccess%2Ftypedal/lists"}