I have ProjectForm form in which one field is ModelChoiceField with foreign key to Company Model. Creating new record and saving is not a problem, but when I want to edit that record this field is empy (like if none company was selected, but in DB I can see it with correct selection), other fields are filled correctly.
I want to avoid changing template code as much as possible so its generated by universal form template.
----- models.py -----
class Company(models.Model):
name = models.CharField(max_length=250, unique=True)
...
class Project(models.Model):
company = models.ForeignKey(Company, on_delete=models.SET_NULL, null=True, blank=True)
name = models.CharField(max_length=250, blank=False)
description = models.TextField()
...
----- forms.py -----
@parsleyfy
class ProjectForm(forms.Form):
company = forms.ModelChoiceField(label='Společnost', queryset=Company.objects.all(), required=False)
name = forms.CharField(label='Název')
...
----- view.py -----
def edit(request, pk):
context = {'form_url': reverse('projects:project-edit', args=(pk,)),
'form_type':'edit'}
obj = get_object_or_404(Project, pk=pk)
if request.method == "GET":
form = ProjectForm(initial=obj.__dict__)
else:
form = ProjectForm(request.POST, request.FILES)
if form.is_valid():
try:
obj.update(**form.cleaned_data)
except IntegrityError:
errors = form._errors.setdefault("name", ErrorList())
errors.append(u"Toto jméno již existuje")
else:
return redirect(LIST_URL) # SUCCESSFULLY SAVED
context['form'] = form
return render(request, FORM_TEMPLATE, context)
----- template.html -----
<form role="form" action="{{ form_url }}" method="post" enctype="multipart/form-data"
data-parsley-validate novalidate>
{% include 'projects/reusable/form_template.html' %}
</form>
in which:
{% elif field|input_type == 'Select' %}
<div for="{{ field.label }}" class="col-sm-2 col-form-label">
{{ field.label_tag }}
</div>
<div class="col-sm-10">
{{ field|add_classes:'form-control'}}
{% if field.help_text %}
<small class="form-text text-muted">{{ field.help_text }}</small>
{% endif %}
</div>
----- DB screenshot -----
----- Form screenshot ----- (first input is the company)
initial=obj.__dict__
will return the project
dict with
{
...
company_id: value
}
in your form, you defined field name as company
though. Therefore, you need update your form initial
with value same as company_id
, thus the company
field will be properly populated
{
company: value
}
Use model_to_dict
can explicitly remove non-editable fields, check and get the ids of foreign keys, you can read more on its source since is not well documented.
def model_to_dict(instance, fields=None, exclude=None):
"""
Return a dict containing the data in ``instance`` suitable for passing as
a Form's ``initial`` keyword argument.
"""
pass
from django.forms.models import model_to_dict
# model_to_dict will handle 'special' fields properly
form = ProjectForm(initial=model_to_dict(obj))