text: 10 January 2020 6 comments Python, Web development, Django
  Django's Form framework is excellent. It's intuitive and versatile and, best of all, easy to use. However, one little thing that is not so intuitive is how do you render a bound form with default/initial values when the form is never rendered unbound.
  If you do this in Django:
  class MyForm(forms.Form): name = forms.CharField(required=False) def view(request): form = MyForm(initial={'name': 'Peter'}) return render(request, 'page.html', form=form) # Imagine, in 'page.html' that it does this: # <label>Name:</label> # {{ form.name }}
  ...it will render out this:
  <label>Name:</label> <input type="text" name="name" value="Peter">
  The whole
  initial trick is something you can set on the whole form or individual fields. But it's only used in UN-bound forms when rendered.
  If you change your view function to this:
  def view(request): form = MyForm(request.GET, initial={'name': 'Peter'}) # data passed! if form.is_valid(): # makes it bound! print(form.cleaned_data['name']) return render(request, 'page.html', form=form)
  Now, the
  form is bound and the
  initial stuff is essentially ignored.
  Because
  name is not present in
  request.GET. And if it was present, but an empty string, it wouldn't be able to benefit for the default value.
  I tried many suggestions and tricks (based on rapid Stackoverflow searching) and nothing worked.
  I knew one thing: Only the view should know the actual initial values.
  Here's what works:
  import copy class MyForm(forms.Form): name = forms.CharField(required=False) def __init__(self, data, **kwargs): initial = kwargs.get('initial', {}) data = {**initial, **data} super().__init__(data, **kwargs)
  Now, suppose you don't have
  ?name=something in
  request.GET the line
  print(form.cleaned_data['name']) will print
  Peter and the rendered form will look like this:
  <label>Name:</label> <input type="text" name="name" value="Peter">
  And, as expected, if you have
  ?name=Ashley in
  request.GET it will print
  Ashley and produce this rendered HTML too:
  <label>Name:</label> <input type="text" name="name" value="Ashley">
  UPDATE June 2020
  If
  data is a
  QueryDict object (e.g.
  <QueryDict: {'days': ['90']}>), and
  initial is a plain dict (e.g.
  {'days': 30}),
  then you can merge these with
  {**data, **initial} because it produces a plain dict of value
  {'days': [90]} which Django's form stuff doesn't know is supposed to be "flattened".
  The solution is to use:
  from django.utils.datastructures import MultiValueDict ... def __init__(self, data, **kwargs): initial = kwargs.get("initial", {}) data = MultiValueDict({**{k: [v] for k, v in initial.items()}, **data}) super().__init__(data, **kwargs)
  (To be honest; this might work in the app I'm currently working on but I don't feel confident that this is covering all cases)
  Follow @peterbe on Twitter
  Hey Peter, the behavior you implemented looks more like default than initial. You want to 1. Suggest a value for the user and 2. Select a value if the user did not provide it.
  How do you implement `default`?
  I don't know why I thought there was a `default` attribute. You're right.. ;)
  The solution comes down to merging the `data` and `initial` dicts, with a preference for the content of `data`. Using an idiomatic merge would outline this behaviour. has a bunch of those.
  ```python
  def __init__(self, data, **kwargs):
  initial = kwargs.get('initial', {})
  data = {**initial, **data}
  super().__init__(data, **kwargs)
  ```
  That's neat! It's ultimately sugar syntax to my otherwise clunky loop where I merge `data` and `kwargs.get('initial')`. Thanks!
  For a full solution, you need to query the value of each field. You can do this with .value() on an instantiated form. So you can instantiate a dummy form, and then go through the bound fields, calling .value() on them and then using that as data, something this:
  dummy_form = ...
  default_values = {}
  for bound_field in dummy_form:
  v = bound_field.value()
  if v is not None:
  default_values[bound_field.name] = v
  Then
  SomeForm(default_values)
  I think this works because the fields check the type of their input before cleaning. It's not exactly elegant, though, forms in Django are a bit rough around the edges.