[PYTHON] Relationship between Bound / Unbound and initial value of Django2 Form

Premise

It's for beginners to some extent who have used all the basic features of Django.

environment

Introduction

This is an explanation of the initial value settings that you feel addicted to when using the form. Note that the description may not clearly distinguish between Form and ModelForm.

Image using form

First, let's illustrate a rough image of the form. フォームイメージ1.png

Form input

Next, it represents the main input items of the form (items of the argument of the \ _ \ _ init \ _ \ _ () method). フォームイメージ2.png

The following shows what each input item corresponds to.

data
request.GET, request.POST
files
request.FILES
instance
model instance / dd>
initial
Dictionary with initial values

What is Bound / Unbound?

There is a description in the official document that "the distinction between Bound / Unbound is important", but it is difficult to understand the behavior of the form unless you hold down this. The definition of Bound / Unbound is as follows.

Bound form
Form with request.GET, request.POST, or request.FILES set
Unbound form
Form without request.GET, request.POST, request.FILES

This can also be seen from the source code of the BaseForm class.

#Part of the code for the BaseForm class
self.is_bound = data is not None or files is not None

Here are some examples of what arguments can be used to instantiate a form to make it Bound (or Unbound).

# Bound/Unbound form example
form_bound_1 = SomeForm(request.GET)
form_bound_2 = SomeForm(request.POST)
form_bound_3 = SomeForm(request.POST, request.FILES)

form_unbound_1 = SomeForm()
form_unbound_2 = SomeForm(initial={"some_field": "some_valule"})
form_unbound_3 = SomeModelForm(initial={"some_field": "some_valule"}, instance=some_instance)

form_bound_4 = SomeModelForm(request.POST, initial={"some_field": "some_valule"}, instance=some_instance)

Relationship between Bound / Unbound and initial value

The "main input items of the form" and "Bound / Unbound" that we have seen so far are related to the initial value setting. The form defines one or more input fields, or fields, but let's look at the code where each field gets a value to render the Html markup.


#BoundField class methods
def value(self):
    """
    Return the value for this BoundField, using the initial value if
    the form is not bound or the data otherwise.
    """
    data = self.initial
    if self.form.is_bound:
        data = self.field.bound_data(self.data, data)
    return self.field.prepare_value(data)

From the above, we can see that Bound form field values do not use (overwrite) initial </ b>. Also, note that even if request.GET etc. is empty (≠ None), it will be a Bound form </ b>.

Countermeasures

If you want to create a search form or use Ajax to do this on one screen, you can enable the initial value setting by branching as follows. (The code is just an example.)

if "some_field" in request.GET:
  form = SomeForm(request.GET)
else:
  form = SomeForm(initial={"some_field": "some_value"})

The problem does not occur while model creation and model update are executed in individual views, but it may become addictive once you start applying it.

Value priority

In addition, it should be noted that the values of the form fields are set in the following priority order. (To be precise, it's like a "priority image" that ignores Bound / Unbound differences, etc.)

  1. data (request.GET, request.POST)
  2. initial
  3. instance
  4. Form field initial value ⇒ A value to be set like "some_field = forms.CharField (initial =" init value ")" in the form field definition.

In ModelForm, the default value of Model seems to be the initial of the form field, but it doesn't seem to be.

in conclusion

The "Bound / Unbound" and "Initial value setting" parts are parts that you can't imagine, so it's a good idea to deepen your understanding.