Skip to content

Propagate model field defaults to ModelSerializer fields#9997

Open
M2004GV wants to merge 7 commits into
encode:mainfrom
M2004GV:propagate-model-field-defaults
Open

Propagate model field defaults to ModelSerializer fields#9997
M2004GV wants to merge 7 commits into
encode:mainfrom
M2004GV:propagate-model-field-defaults

Conversation

@M2004GV

@M2004GV M2004GV commented Jul 3, 2026

Copy link
Copy Markdown

Description

Refreshed resubmission of #8130, which was closed without being merged.

ModelSerializer already propagates a model field's default to the generated serializer field when that field participates in a uniqueness constraint (get_uniqueness_extra_kwargs), but not otherwise. This means a plain IntegerField(default=0) (with no uniqueness constraint) is generated as required=False but with no default, so omitting it from the input simply drops the attribute instead of falling back to the model's default — inconsistent with how Django's ModelForm behaves, and surprising given DRF already does the equivalent for unique-constrained fields.

This PR generalizes that existing behavior: whenever model_field.has_default(), the value is passed through as the serializer field's default in get_field_kwargs.

Closes #7469. Related to #2683 / #7489.

Addressing prior review feedback

On the original PR, @auvipy asked for two things before it could be accepted:

  1. Rebase with a fresh approach — done here as a clean two-commit PR.
  2. Update OPTIONS responses to include the default, consistent with how default is already surfaced in schemas/openapi.py. Done in the second commit: SimpleMetadata.get_field_info now includes a default key for any non-callable field default.

Behavior change

The only user-visible behavior change is for non-partial updates/creates: a field with a model-level default that is omitted from the input will now have that default appear in validated_data, matching what already happens for fields covered by uniqueness constraints. Partial updates (partial=True) are unaffected — defaults are still skipped in that case, per existing Field.get_default() behavior.

Test plan

  • Added test_default_value_used_when_field_omitted and test_default_value_skipped_on_partial_update in tests/test_model_serializer.py, and updated the repr() regexes in test_regular_fields / test_field_options to reflect the new default=... kwarg.
  • Added test_default_value_field_info and test_callable_default_field_info in tests/test_metadata.py.
  • Added a note to docs/api-guide/fields.md documenting the propagation.
  • Full test suite passes against both Django 5.2 and Django main (6.2 dev): 1562 passed, 0 failed.
  • flake8 clean on changed files.

@Shrikantgiri25 Shrikantgiri25 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great work! Quick suggestion: Add tests for:

  1. Callable defaults (uuid.uuid4) - ensure they execute correctly
  2. Choice fields with default - validates the original #7469 fix end-to-end
  3. OpenAPI schema (optional) - verify defaults appear in docs

These cover the exact scenarios from the linked issues.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR updates DRF’s ModelSerializer field generation to propagate Django model field default values into the generated serializer fields, aligning validation behavior (and metadata/schema output) more closely with model defaults.

Changes:

  • Propagate model_field.default into serializer field kwargs when model_field.has_default() in get_field_kwargs.
  • Surface non-callable field defaults in SimpleMetadata.get_field_info (for OPTIONS metadata), consistent with OpenAPI schema behavior.
  • Update tests and documentation to reflect and validate default propagation behavior (including callable defaults and choices fields).

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
rest_framework/utils/field_mapping.py Adds default propagation for non-relational model fields via get_field_kwargs.
rest_framework/metadata.py Includes non-callable field defaults in OPTIONS metadata output.
tests/test_model_serializer.py Updates repr expectations and adds tests covering default propagation, callable defaults, and choices-with-default behavior.
tests/test_metadata.py Adds coverage ensuring metadata includes non-callable defaults and excludes callable defaults.
tests/schemas/test_openapi.py Adds schema coverage verifying model defaults appear in OpenAPI output (excluding callable defaults).
docs/api-guide/fields.md Documents that ModelSerializer will use model field defaults as serializer defaults.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 133 to +138
if model_field.has_default() or model_field.blank or model_field.null:
kwargs['required'] = False

if model_field.has_default():
kwargs['default'] = model_field.default

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@M2004GV please cross check this suggestion and others relevant ones

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@auvipy, cross-checked both suggestions. Relational defaults are intentionally not propagated, a FK model default is a PK value, while a serializer field default goes into validated_data as is (no to_internal_value), so propagating it would break .create(). Documented this scope in the docs and locked it in with a test. The full-update test from your autofix is kept as is (plus a missing blank line fix for flake8).

Comment thread tests/test_model_serializer.py
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

ModelSerializer generate ChoicesField will discard default parameter

4 participants