Files
app-store-optimization/skills/loki-mode/benchmarks/results/2026-01-05-01-24-17/swebench-patches/django__django-16229.patch

142 lines
7.4 KiB
Diff

Based on my knowledge of Django's codebase, I can analyze and fix this issue. The problem is in `django/forms/models.py` in the `apply_limit_choices_to_to_formfield` function or more specifically in how the form field's `show_hidden_initial` is determined.
The issue is:
1. When a model field has a callable default, Django's form field gets `show_hidden_initial=True`
2. This is because the callable is evaluated, making it appear the field has an initial value that could change
3. However, for fields with callable defaults that return empty/default values (like `list`), this causes issues with `has_changed()` detection
Looking at Django's code, the fix should be in `django/forms/models.py` where `show_hidden_initial` is set based on whether the field has a callable default. The specific issue is that when the default is callable, the form field shouldn't automatically get `show_hidden_initial=True` just because the initial value was derived from a callable.
The fix should be in the function that constructs form fields from model fields. The `show_hidden_initial` should not be set to `True` when the initial value comes from a callable default on the model field.
Here's the patch:
--- a/django/forms/models.py
+++ b/django/forms/models.py
@@ -92,7 +92,10 @@ def construct_instance(form, instance, fields=None, exclude=None):
def apply_limit_choices_to_to_formfield(formfield):
"""Apply limit_choices_to to the formfield's queryset if needed."""
from django.db.models import Exists, OuterRef, Q
- if hasattr(formfield, 'queryset') and hasattr(formfield, 'get_limit_choices_to'):
+ if hasattr(formfield, "queryset") and hasattr(formfield, "get_limit_choices_to"):
limit_choices_to = formfield.get_limit_choices_to()
if limit_choices_to:
complex_filter = limit_choices_to
@@ -188,6 +191,10 @@ class ModelForm(BaseForm, metaclass=ModelFormMetaclass):
def modelform_factory(model, form=ModelForm, fields=None, exclude=None,
formfield_callback=None, widgets=None, localized_fields=None,
labels=None, help_texts=None, error_messages=None):
+ """
+ Return a ModelForm containing form fields for the given model. You can
+ optionally pass a `form` argument to use as a starting point for
+ constructing the ModelForm.
--- a/django/forms/fields.py
+++ b/django/forms/fields.py
@@ -136,8 +136,10 @@ class Field:
def has_changed(self, initial, data):
"""Return True if data differs from initial."""
# Always return False if the field is disabled since self.bound_data
- # always uses the initial value in this case.
+ # always uses the initial value in this case.
if self.disabled:
return False
try:
data = self.to_python(data)
Let me reconsider the actual fix based on the issue description more carefully. The issue is specifically about:
1. A field with `default=list` (callable)
2. On first submit with error, the hidden initial value gets the user's input
3. On second submit, validation is bypassed
The root cause is that `show_hidden_initial` is being set to `True` for fields with callable defaults. The fix should ensure that when a form field's initial value is derived from a callable default on a model field, `show_hidden_initial` should be set based on whether the callable actually produces a value that could change between requests.
Looking at the Django code flow:
1. `modelform_defines_fields` checks fields
2. `fields_for_model` creates form fields from model fields
3. For each field, `formfield()` is called which sets `show_hidden_initial`
The fix should be in how `show_hidden_initial` is determined. When the initial value comes from evaluating a callable default, the resulting value should be used for the hidden initial comparison, not the fact that it came from a callable.
--- a/django/forms/models.py
+++ b/django/forms/models.py
@@ -43,6 +43,9 @@ def construct_instance(form, instance, fields=None, exclude=None):
def model_to_dict(instance, fields=None, exclude=None):
"""
Return a dict containing the data in ``instance`` suitable for passing as
@@ -86,7 +89,14 @@ def fields_for_model(model, fields=None, exclude=None, widgets=None,
formfield_callback=None, localized_fields=None,
labels=None, help_texts=None, error_messages=None,
field_classes=None, *, apply_limit_choices_to=True):
- """
+ """Return a dictionary containing form fields for the given model.
+
+ ``fields`` is an optional list of field names. If provided, return only the
+ named fields.
+
+ ``exclude`` is an optional list of field names. If provided, exclude the
+ named fields from the returned fields, even if they are listed in the
+ ``fields`` argument.
field_dict = {}
ignored = []
opts = model._meta
Based on the issue, the actual fix needs to be in how the initial value is propagated when a callable default is involved. The problem is in `django/forms/models.py` in the function that creates form fields.
--- a/django/forms/models.py
+++ b/django/forms/models.py
@@ -92,6 +92,9 @@ def fields_for_model(
continue
kwargs = {}
+ if form_field is not None and f.has_default() and callable(f.default):
+ kwargs["show_hidden_initial"] = False
if widgets and f.name in widgets:
kwargs["widget"] = widgets[f.name]
if localized_fields == ALL_FIELDS or (
Wait, I need to be more precise about the location of the fix. Let me provide the correct patch based on Django's actual codebase structure:
--- a/django/forms/models.py
+++ b/django/forms/models.py
@@ -47,6 +47,12 @@ __all__ = (
ALL_FIELDS = "__all__"
+def _get_default_value(field):
+ """Return the default value for a model field, calling it if callable."""
+ if callable(field.default):
+ return field.default()
+ return field.default
+
+
def construct_instance(form, instance, fields=None, exclude=None):
"""
Construct and return a model instance from the bound ``form``'s
@@ -91,6 +97,10 @@ def fields_for_model(
kwargs = {}
if widgets and f.name in widgets:
kwargs["widget"] = widgets[f.name]
+ # Don't show hidden initial for fields with callable defaults,
+ # as the value has already been evaluated.
+ if f.has_default() and callable(f.default):
+ kwargs["show_hidden_initial"] = False
if localized_fields == ALL_FIELDS or (
localized_fields and f.name in localized_fields
):
Actually, looking at this more carefully, the real issue is about how form fields determine `show_hidden_initial`. The issue is that when a model field has a callable default, the form field's `show_hidden_initial` becomes True, which causes issues with the `has_changed()` detection.
Let me provide the correct minimal patch:
--- a/django/forms/models.py
+++ b/django/forms/models.py
@@ -93,6 +93,9 @@ def fields_for_model(
kwargs = {}
if widgets and f.name in widgets:
kwargs["widget"] = widgets[f.name]
+ # Fields with callable defaults should not show hidden initial.
+ if f.has_default() and callable(f.default):
+ kwargs["show_hidden_initial"] = False
if localized_fields == ALL_FIELDS or (
localized_fields and f.name in localized_fields
):