There are many related questions I checked on so before posting my own, but I still can't figure this out. Using Django 2.2.x.
I want the owner
field of my html form to initially display only 1 username and this username has to be the current event
instance owner.
Being a ModelChoiceField
, the field on the html form is rendered as a dropdown. This dropdown for the time being contains all the app users, since the ModelChoiceField
has queryset=User.objects.all()
I tried to override the get_initial()
method of my UpdateView
but, afaict, this has no effect on the ModelChoiceField
. I'm able to set all forms.CharField() initial values though.
I tried also to override the __init__
method of my class EventForm(forms.ModelForm)
without luck.
forms.py
class EventForm(forms.ModelForm):
class Meta:
model = Event
fields = ["customer", "owner", ...]
# the helper block comes from crispy_forms extension.
# It rules the form layout, allowing to specify css classes, fields order
# and even adding custom buttons
helper = FormHelper()
helper.layout = Layout(
Field('customer'),
Field('owner'),
...
Submit('submit', 'Update')
)
customer = forms.CharField(disabled=True)
owner = forms.ModelChoiceField(queryset=User.objects.all())
...
views.py
class EventUpdateView(LoginRequiredMixin, UpdateView):
""" this view renders the update form that allows service desk operators to update event status,
ownership and event notes
"""
model = Event
form_class = EventForm
template_name = "metamonitor/event_update_form.html"
def get_initial(self):
""" returns the initial data to populate form field on this view.
We override this method to customize the `owner` initial value to the
default event owner.
"""
initial = super().get_initial()
# the event instance being updated
event = self.get_object()
# this expression returns a queryset containing only 1 item,
# like <QuerySet [<User: operator2>]>
initial['owner'] = User.objects.filter(pk=event.owner.pk)
return initial
# without this, after a successfull event update we would get an error
# because django needs a valid url to redirect to.
def get_success_url(self):
# app_name:url_name, as defined in the corresponding path() `name` argument in urls.py
return reverse("metamonitor:event_update", kwargs={"pk": self.object.pk})
How can I properly set the initial value?
Here is an approach to solve your problem. It requires two steps:
Override the get_form_kwargs method of your view, so it retrieves the 'owner' object and sends it to the form, stored in the kwargs argument
Override the init of the form, and then assign the owner object (taken from kwargs), to the .queryset of the field you want to change:
Override get_form_kwargs in your view:
def get_form_kwargs(self):
kwargs = super().get_form_kwargs()
event = self.object # no need to call get_object() as you did in your post
owner = User.objects.filter(pk=event.owner.pk)
kwargs.update({'owner': owner})
return kwargs
Override init of your Form class in forms.py:
def __init__(self, *args, **kwargs):
owner = kwargs.pop('owner') # get the owner object from kwargs
super().__init__(*args, **kwargs)
# assign owner to the field; note the use of .queryset on the field
self.fields['owner'].queryset = owner
Another, different from above, approach is to simply modify the get_form method of your view:
def get_form(self, form_class=None):
form = super().get_form(form_class)
event = self.object
owner = User.objects.filter(pk=event.owner.pk)
form.fields['owner'].queryset = owner
return form
This solution is notably quicker to implement than the first, it's true; I only mention it last because in my experience, people seem to prefer to modify the Form class itself when the desired goal is changing how the Form is displayed. Use whichever works best for your needs.