Skip to content

Commit e6f68fb

Browse files
authored
feat: support OrmarConfig(comment=...) for table comments (#1700)
Forwarded to the underlying sqlalchemy.Table and emitted by metadata.create_all() on PostgreSQL and MySQL (SQLite ignores it). copy() inherits the parent comment by default; pass comment=None explicitly to clear it. Closes #1240
1 parent dc66c1d commit e6f68fb

5 files changed

Lines changed: 105 additions & 0 deletions

File tree

docs/models/index.md

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,27 @@ explicitly to opt out, or pass a different value to override.
300300
issue `CREATE SCHEMA <name>` (PostgreSQL) or `CREATE DATABASE <name>` (MySQL)
301301
before tables can be created in those schemas.
302302

303+
### Table Comment
304+
305+
You can attach a SQL `COMMENT ON TABLE` value to a model by passing `comment=` to
306+
`OrmarConfig` (or `OrmarConfig.copy()`). The value is forwarded to the underlying
307+
SQLAlchemy `Table` and emitted by `metadata.create_all()` on dialects that
308+
support table comments (PostgreSQL and MySQL; SQLite ignores it).
309+
310+
```python
311+
class Book(ormar.Model):
312+
ormar_config = base_ormar_config.copy(
313+
tablename="books",
314+
comment="Library catalogue entries.",
315+
)
316+
317+
id: int = ormar.Integer(primary_key=True)
318+
title: str = ormar.String(max_length=200)
319+
```
320+
321+
`copy()` inherits the parent config's comment by default; pass `comment=None`
322+
explicitly to opt out, or pass a different value to override.
323+
303324
### Constraints
304325

305326
On a model level you can also set model-wise constraints on sql columns.

docs/releases.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,13 @@
22

33
## Unreleased
44

5+
### ✨ Features
6+
7+
* Add `OrmarConfig(comment=...)` to attach a SQL `COMMENT ON TABLE` value to a
8+
model — forwarded to the underlying SQLAlchemy `Table` and emitted by
9+
`metadata.create_all()` on dialects that support table comments (PostgreSQL,
10+
MySQL; SQLite ignores it). [#1240](https://github.com/collerek/ormar/issues/1240)
11+
512
### 🐛 Fixes
613

714
* Fix race in `get_or_create` (and the m2m / reverse-fk variant) where

ormar/models/helpers/sqlalchemy.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -358,6 +358,7 @@ def populate_config_sqlalchemy_table_if_required(config: "OrmarConfig") -> None:
358358
*config.columns,
359359
*config.constraints,
360360
schema=config.schema,
361+
comment=config.comment,
361362
)
362363
config.table = table
363364

ormar/models/ormar_config.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ class OrmarConfig:
2222
engine: AsyncEngine
2323
tablename: str
2424
schema: Optional[str]
25+
comment: Optional[str]
2526
order_by: list[str]
2627
abstract: bool
2728
proxy: bool
@@ -36,6 +37,7 @@ def __init__(
3637
engine: Optional[AsyncEngine] = None,
3738
tablename: Optional[str] = None,
3839
schema: Optional[str] = None,
40+
comment: Optional[str] = None,
3941
order_by: Optional[list[str]] = None,
4042
abstract: bool = False,
4143
proxy: bool = False,
@@ -50,6 +52,7 @@ def __init__(
5052
self.engine = engine # type: ignore
5153
self.tablename = tablename # type: ignore
5254
self.schema = schema
55+
self.comment = comment
5356
self.orders_by = order_by or []
5457
self.columns: list[sqlalchemy.Column] = []
5558
self.constraints = constraints or []
@@ -74,6 +77,7 @@ def copy(
7477
engine: Optional[AsyncEngine] = None,
7578
tablename: Optional[str] = None,
7679
schema: Union[str, None, PydanticUndefinedType] = PydanticUndefined,
80+
comment: Union[str, None, PydanticUndefinedType] = PydanticUndefined,
7781
order_by: Optional[list[str]] = None,
7882
abstract: Optional[bool] = None,
7983
proxy: Optional[bool] = None,
@@ -87,12 +91,16 @@ def copy(
8791
resolved_schema = (
8892
self.schema if isinstance(schema, PydanticUndefinedType) else schema
8993
)
94+
resolved_comment = (
95+
self.comment if isinstance(comment, PydanticUndefinedType) else comment
96+
)
9097
return OrmarConfig(
9198
metadata=metadata or self.metadata,
9299
database=database or self.database,
93100
engine=engine or self.engine,
94101
tablename=tablename,
95102
schema=resolved_schema,
103+
comment=resolved_comment,
96104
order_by=order_by,
97105
abstract=abstract or self.abstract,
98106
proxy=proxy if proxy is not None else self.proxy,
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
"""Tests for ``OrmarConfig.comment`` forwarding to ``sqlalchemy.Table``."""
2+
3+
import ormar
4+
from tests.lifespan import init_tests
5+
from tests.settings import create_config
6+
7+
base_ormar_config = create_config()
8+
9+
10+
class CommentedModel(ormar.Model):
11+
ormar_config = base_ormar_config.copy(
12+
tablename="commented_models",
13+
comment="Stores commented things.",
14+
)
15+
16+
id: int = ormar.Integer(primary_key=True)
17+
name: str = ormar.String(max_length=100)
18+
19+
20+
class UncommentedModel(ormar.Model):
21+
ormar_config = base_ormar_config.copy(tablename="uncommented_models")
22+
23+
id: int = ormar.Integer(primary_key=True)
24+
25+
26+
create_test_database = init_tests(base_ormar_config)
27+
28+
29+
def test_comment_forwarded_to_sqlalchemy_table():
30+
assert CommentedModel.ormar_config.table.comment == "Stores commented things."
31+
32+
33+
def test_comment_defaults_to_none():
34+
assert UncommentedModel.ormar_config.comment is None
35+
assert UncommentedModel.ormar_config.table.comment is None
36+
37+
38+
def test_copy_inherits_parent_comment_when_omitted():
39+
parent = ormar.OrmarConfig(
40+
metadata=base_ormar_config.metadata,
41+
database=base_ormar_config.database,
42+
engine=base_ormar_config.engine,
43+
comment="parent comment",
44+
)
45+
child = parent.copy(tablename="child")
46+
assert child.comment == "parent comment"
47+
48+
49+
def test_copy_can_explicitly_clear_parent_comment():
50+
parent = ormar.OrmarConfig(
51+
metadata=base_ormar_config.metadata,
52+
database=base_ormar_config.database,
53+
engine=base_ormar_config.engine,
54+
comment="parent comment",
55+
)
56+
child = parent.copy(tablename="child", comment=None)
57+
assert child.comment is None
58+
59+
60+
def test_copy_can_override_parent_comment():
61+
parent = ormar.OrmarConfig(
62+
metadata=base_ormar_config.metadata,
63+
database=base_ormar_config.database,
64+
engine=base_ormar_config.engine,
65+
comment="parent comment",
66+
)
67+
child = parent.copy(tablename="child", comment="child comment")
68+
assert child.comment == "child comment"

0 commit comments

Comments
 (0)