Coerce loose input to canonical types on the create paths#19
Merged
Conversation
create()/get_or_create()/update_or_create()/bulk_create() build instances
through Model.__init__, which normalises kwargs via to_python_value only for
fields that override it — previously just Datetime/Date/Time. A loose value
("12.34" for a decimal column, a hex string for a UUID column) therefore
stayed a raw string on the returned instance, while get() decoded the same
row to Decimal/UUID via to_python.
- Add to_python_value overrides to DecimalField, UUIDField, BooleanField
(via to_db, since bool("false") would be True), TimeDeltaField,
IntEnumField and CharEnumField. JSONField is left alone (a string is a
valid JSON value) and int/float/char stay identity to keep the plain
construction fast path.
- update_from_dict (the update_or_create update path) now applies the same
coercion through meta.coerced_fields.
- New tests/test_create_type_coercion.py runs on every configured backend
and checks all create-family entry points against get()'s types.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
create()returned instances carrying raw strings whereget()returns typed values:create(balance="12.34", token="<uuid string>").balancewas astr, while re-fetching the same row yieldedDecimal/UUID.Root cause: every create-family entry point (
create,get_or_create,update_or_create,bulk_create, and the QuerySet variants) builds instances throughModel.__init__, which normalises kwargs viato_python_valueonly for fields that override it — previously justDatetimeField/DateField/TimeField. Theupdate_or_createupdate path (update_from_dict) had no coercion at all.Fix
to_python_valueoverrides toDecimalField,UUIDField,BooleanField(delegating toto_db, sincebool("false")would beTrue),TimeDeltaField,IntEnumFieldandCharEnumField.JSONFieldis deliberately untouched — a string is a valid JSON value, so parsing it on assignment would change semantics. Int/float/char fields stay identity to preserve the all-plain-model construction fast path (no virtual call per column).update_from_dictnow applies the same coercion throughmeta.coerced_fields, soupdate_or_create(defaults={"balance": "99.50"})also leaves aDecimalon the instance.Tests
New
tests/test_create_type_coercion.py, parametrized across all six backends via thedbfixture: a unit test for each new coercion (includingNoneand canonical-value passthrough) plus DB round-trips forcreate,get_or_create(both paths),update_or_create(both paths),bulk_createand the QuerySet-levelget_or_create/update_or_create, each comparing the returned instance's types against whatget()yields.Verified locally: new test passes on sqlite + postgres (other backends skip without reachable servers); full suite green (1215 sqlite under xdist + serial postgres modules);
make lintclean; coverage unchanged vs main (identical missed-line set, all from backend-specific code needing live mysql/mariadb/oracle/mssql servers).