Skip to content

Commit 660698f

Browse files
tmgbeduclaude
andcommitted
fix: populate __fillable__ from full MRO, not just own annotations
__init_subclass__ previously built __fillable__ from cls.__annotations__ which only contains annotations declared directly in the class body. Fields inherited from an intermediate base model were silently absent, causing fill() / update() to skip them with no error. Fix: walk the MRO with klass.__dict__.get('__annotations__', {}), collecting annotations from every user-defined ancestor while stopping at Model and its own bases (so framework internals like db_manager, created_at, updated_at are excluded). Also reverts the earlier incorrect update() change — fill(attributes).save() is the right implementation; the problem was always in __fillable__ not being populated correctly. Closes #67 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 8243951 commit 660698f

7 files changed

Lines changed: 75 additions & 392 deletions

File tree

‎fastapi_startkit/src/fastapi_startkit/masoniteorm.backup/tests/factories/test_factories.py‎

Lines changed: 0 additions & 66 deletions
This file was deleted.

‎fastapi_startkit/src/fastapi_startkit/masoniteorm.backup/tests/integrations/config/database.py‎

Lines changed: 0 additions & 119 deletions
This file was deleted.

‎fastapi_startkit/src/fastapi_startkit/masoniteorm.backup/tests/integrations/databases/migrations/2026_01_01_000000_create_users_table.py‎

Lines changed: 0 additions & 29 deletions
This file was deleted.

‎fastapi_startkit/src/fastapi_startkit/masoniteorm.backup/tests/integrations/model.py‎

Lines changed: 0 additions & 57 deletions
This file was deleted.

‎fastapi_startkit/src/fastapi_startkit/masoniteorm.backup/tests/integrations/test_model.py‎

Lines changed: 0 additions & 70 deletions
This file was deleted.

‎fastapi_startkit/src/fastapi_startkit/masoniteorm/models/model.py‎

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,24 @@ def __init_subclass__(cls, **kwargs):
3232
super().__init_subclass__(**kwargs)
3333
Registry.register(cls)
3434

35+
from fastapi_startkit.masoniteorm.relationships.BaseRelationship import (
36+
BaseRelationship,
37+
)
38+
39+
# Collect annotations from every user-defined class in the MRO, walking
40+
# from the most-base class downward so subclass annotations win on conflict.
41+
# Stop at Model itself — its own fields (db_manager, created_at, etc.) are
42+
# framework internals, not user-defined data columns.
43+
_model_bases = {Model, *Model.__mro__}
44+
all_annotations: dict = {}
45+
for klass in reversed(cls.__mro__):
46+
if klass in _model_bases:
47+
continue
48+
all_annotations.update(klass.__dict__.get("__annotations__", {}))
49+
3550
fillable = []
36-
for name, _typ in cls.__annotations__.items():
51+
for name in all_annotations:
3752
attr = getattr(cls, name, None)
38-
from fastapi_startkit.masoniteorm.relationships.BaseRelationship import (
39-
BaseRelationship,
40-
)
41-
4253
if isinstance(attr, BaseRelationship):
4354
continue
4455
if callable(attr):
@@ -200,10 +211,7 @@ async def update(self, attributes: dict) -> bool:
200211
if not self._exists:
201212
return False
202213

203-
for key, value in attributes.items():
204-
self.set_attribute(key, value)
205-
206-
return await self.save()
214+
return await self.fill(attributes).save()
207215

208216
def fill(self, attributes: dict) -> "Model":
209217
for key, value in attributes.items():

0 commit comments

Comments
 (0)