Repository navigation
Description
What happens
On an extra="allow" model, pydantic reaches extra values through instance __getattr__, which fires only when normal attribute lookup fails. A stored key that collides with a model method therefore loads without complaint but is unreachable as an attribute — lookup finds the method first, and nothing warns. The value is still reachable — doc.model_extra["count"] for access, tinydb.where("count") for queries — but nothing points the user there.
No definition-time check can catch this: the collision depends on what keys are in the database at run time, not on what the class declares. It is the runtime blind spot of ShadowedFieldError, and it is inherent to Python attribute lookup — the ask here is documentation, not a behavior change.
Why it matters
extra="allow" is precisely the configuration used over legacy or foreign TinyDB data — docs/usage/schema-evolution.md shows it for gradual migration, and docs/usage/queries.md points raw where() queries at "extra="allow" documents, legacy keys" — which is the data most likely to contain keys like count, search, or json. And doc.count returning a bound method can even look plausible in passing. The docs never mention the collision or model_extra at all: grep -rin "model_extra" docs/ README.md returns nothing (run 2026-08-14 at 389c6d9).
What to do
Wherever the reserved-name material planned in #121 lands, add: on extra="allow" models the reserved names apply to document keys as well; a collided extra key is read via doc.model_extra[...] and queried via tinydb.where(...). The pages that discuss extra="allow" (models, queries, schema-evolution, security) can share one cross-reference to it.
A code-level courtesy — from_tinydb_document() warning when an extra key shadows a class attribute — would be an enhancement with a per-read cost, worth weighing separately if this bites in practice; it is not part of this ask.
Reproduction
from pydantic import ConfigDict
from tinydb import TinyDB
from tinydb.storages import MemoryStorage
from tinydantic import TinydanticModel
db = TinyDB(storage=MemoryStorage)
class Doc(TinydanticModel, database=db):
model_config = ConfigDict(extra="allow")
name: str
Doc.get_table().insert({"name": "a", "count": 42})
doc = Doc.all()[0]
print("doc.count:", doc.count)
print("doc.model_extra:", doc.model_extra)Run with python repro.py (project venv).
Actual output:
doc.count: <bound method TinydanticModel.count of <class '__main__.Doc'>>
doc.model_extra: {'count': 42}
Expected: the same output — the behavior is inherent and not itself the defect. What is missing is documentation stating it; the docs-absence evidence is the grep above.
Environment: tinydantic 0.4.0 at 389c6d9, Python 3.10.20, TinyDB 4.9.0, pydantic 2.13.4, macOS. Run 2026-08-14.
The reproduction was run as shown. No docs change was drafted. Surfaced while working through the conflict-handling story for #121.
Assisted-by: Claude:claude-fable-5
Activity