#29407: django.forms field in edit dialog don't display default values
-------------------------------------+-------------------------------------
               Reporter:  avolkov    |          Owner:  nobody
                   Type:  Bug        |         Status:  new
              Component:             |        Version:  2.0
  Uncategorized                      |       Keywords:  forms,
               Severity:  Normal     |  django.forms, fields
           Triage Stage:             |      Has patch:  0
  Unreviewed                         |
    Needs documentation:  0          |    Needs tests:  0
Patch needs improvement:  0          |  Easy pickings:  1
                  UI/UX:  0          |
-------------------------------------+-------------------------------------
 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:


 {{{
 #!div style="font-size: 80%"
 models.py
   {{{#!python
   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)
   }}}
 }}}


 {{{
 #!div style="font-size: 80%"
 forms.py
   {{{#!python
 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__'
   }}}
 }}}

 {{{
 #!div style="font-size: 80%"
 views.py
   {{{#!python
 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}
         )
   }}}
 }}}


 {{{
 #!div style="font-size: 80%"
 survey_edit_form.html
   {{{#!jinja
 {% 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 %}
   }}}
 }}}

 {{{
 #!div style="font-size: 80%"
 url.py
   {{{#!python
   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

 {{{
 #!div style="font-size: 80%"
 tests.py
   {{{#!python
 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

 {{{
 #!div style="font-size: 80%"
   {{{#!python
 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

 {{{
 #!div style="font-size: 80%"
 In this code snippet:
   {{{#!python
 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.

 {{{
 #!div style="font-size: 80%"
 Code highlighting:
   {{{#!python
 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?

-- 
Ticket URL: <https://code.djangoproject.com/ticket/29407>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.

-- 
You received this message because you are subscribed to the Google Groups 
"Django updates" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To post to this group, send email to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/django-updates/050.de053d5b2f22d1edb88373e2b9652de1%40djangoproject.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to