Opened 13 days ago

Closed 13 days ago

#29407 closed Bug (invalid)

django.forms field in edit dialog don't display default values

Reported by: Alex Volkov Owned by:
Component: Uncategorized Version: 2.0
Severity: Normal Keywords: forms, django.forms, fields
Cc: Triage Stage: Unreviewed
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: yes UI/UX: no

Description

I created a project with Django 2.0.4 running on Python 3.6.5 with cookiecutter django template.

I created an app (survey) then created a form using a model with model form interface view and template. Everything works except when I added a form for editing existing model values, the form fields with default values don't get populated. Here's a snippet from my implementation:

models.py

  from django.db import models

class Survey(models.Model):

       class Meta:
           """Set composite key for file_number/location fields"""
           unique_together = (('file_number', 'court_location', ))

    file_number = models.CharField(max_length=127)
    location = models.ForeignKey(Location, on_delete=models.PROTECT)

forms.py

from django import forms

class SurveyForm(forms.ModelForm):
    """Survey Form along with customizations"""

    def __init__(self, *args, **kwargs):
        self.user = kwargs.pop('user', None)
        super().__init__(*args, **kwargs)
        # Only show locations available to the user
        locations = Location.objects.filter(contract__user_id=self.user.id)
        self.fields['location'].queryset = locations

    class Meta:
        model = Survey
        fields = '__all__'

views.py

class SurveyEdit(View):
    """Edit form for SurveyForm class"""

    def get(self, request, survey_id):
        survey_obj = Survey.objects.get(id=survey_id)
        survey_form = SurveyForm(
            request.GET, user=request.user, instance=survey_obj)
        return render(
            request,
            'survey_edit_form.html',
            {'survey_form': survey_form, 'survey_id': survey_id}
        )

    def post(self, request, survey_id):
        sf = SurveyForm(
            request.POST,
            user=request.user,
            instance=Survey.objects.get(id=survey_id))
        if sf.is_valid():
            sf.save()
            messages.add_message(
                request,
                messages.SUCCESS,
                "Survey {} was updated".format(sf.cleaned_data['file_number'])
            )
            return HttpResponseRedirect('/survey/list')
        error_message(sf, request)
        return render(
            request,
            'survey_edit_form.html',
            {'survey_form': sf, 'survey_id': survey_id}
        )

survey_edit_form.html

{% extends "base.html" %}

{% block title %}
  {% block head_title %}
  Edit Survey
  {% endblock head_title %}
{% endblock title %}

{% block content %}
<div class="row">
  <div class="col-md-6 offset-md-3">
    <form action="{% url "survey:edit" survey_id=survey_id %}" method=POST>
      {% csrf_token %}
      {% for field in survey_form %}
        <div class='form-group'>
          <div class='label'>{{ field.label }}</div>
          {{ field }}
        </div>
      {% endfor %}
      <input type="submit" value="Submit">
    </form>
  </div>
</div>
{% endblock %}

url.py

path('edit/<int:survey_id>', login_required(SurveyEdit.as_view()), name='edit'),

I also have the following test case, which verifiees that the data is loaded into the form

tests.py

def test_006_edit_data_is_loaded(self):
    """When editing a survey through SurveyForm, verify Survey data is loaded"""
    client = Client()
    client.force_login(self.user)
    # create survey object from generated data
    edit_survey_data = copy(gen_survey_data(self))
    edit_survey = Survey(**edit_survey_data)
    edit_survey.save()
    # go to edit page
    edit_url = '/survey/edit/{}'.format(edit_survey.id)
    resp = client.get(edit_url)
    # verify that field values were loaded
    content = str(resp.content)
    self.assertIn(edit_survey_data['file_number'], content)"

The problem seems to be somewhere either in django.forms.boundfield

https://github.com/django/django/blob/c591bc3ccece1514d6b419826c7fa36ada9d9213/django/forms/boundfield.py#L126

def value(self):
    data = self.initial
    if self.form.is_bound:
        data = self.field.bound_data(self.data, data)
    return self.field.prepare_value(data)

Where data is correctly assigned from self.initial value (which is taken from instance param passed to SurveyForm). However, self.field.bound_data method seems to return wrong value,

https://github.com/django/django/blob/c591bc3ccece1514d6b419826c7fa36ada9d9213/django/forms/fields.py#L161

In this code snippet:

if self.disabled:
    return initial
return data

The initial value returned only when the field is disabled, which should not be the case, I want default data to be displayed when request.GET is passed to render ModelForm, in my case the check should be, if there's no updated data, return initial data i.e.

Code highlighting:

if data:
    return data
return initial

This seems to fix the issue I have and when I make the change the default values are displayed in edit field, however I looked at history of these two files (git blame) and didn't find anything that's been changed recently (all the changes are from 2-3 years ago), so I'm not sure if this is something I'm doing wrong or there was a bug introduced in django.forms in some other way?

Change History (1)

comment:1 Changed 13 days ago by Claude Paroz

Resolution: invalid
Status: newclosed

Thanks for the report, but I would suggest you first search help in Django support channels and ensure that Django is at fault before opening a ticket.

I'm sure reading https://docs.djangoproject.com/en/2.0/topics/class-based-views/generic-editing/ could already help you.

Note: See TracTickets for help on using tickets.
Back to Top