Reputation: 131
I have a ChoiceField in a form and want to save its value in a model. The problem is that the form is never valid if the field is required. When I set required to False, the form is valid but does not contain any data - it's just blank.
models.py:
class CVSS(models.Model):
av = models.CharField(
max_length=10,
null=True,
)
forms.py:
class CVSSForm(forms.ModelForm):
AV = [
(u'N', u'Network'),
(u'A', u'Adjacent'),
(u'L', u'Local'),
(u'P', u'Physical'),
]
av = forms.ChoiceField(
# required=False,
widget=forms.RadioSelect(),
choices=AV,
)
class Meta:
model = CVSS
fields = ['av',]
views.py:
def edit(request, fid):
cvss = get_object_or_404(CVSS, finding_id=fid)
cvss_form = CVSSForm(instance=cvss)
if request.method == 'POST':
cvss_form = CVSSForm(request.POST, instance=cvss)
if cvss_form.is_valid():
cvss_form.save()
edit.html:
<div class="table-responsive">
{% include "dojo/form_fields.html" with form=cvss_form %}
</div>
The form is rendered from form_fields.html:
{% elif field|is_radio %}
{% if field.auto_id %}
<label class="col-sm-4 control-label {% if field.field.required %}{{ form.required_css_class }}{% endif %}">
{{ field.label }}{% if field.field.required%}<sup>*</sup>{% endif %}
</label>
{% endif %}
<div class="col-sm-8 {{ classes.value }}">
{% for choice in field %}
<div class="radio">
<label>
{{ choice.tag }}
{{ choice.choice_label }}
</label>
</div>
{% endfor %}
{% for error in field.errors %}
<span class="help-block {{ form.error_css_class }}">{{ error }}</span>
{% endfor %}
{% if field.help_text %}
<i class="fa fa-question-circle has-popover" data-trigger="hover" data-content="{{ field.help_text }}" data-placement="right" data-container="body">
</i>
{% endif %}
</div>
{% else %}
Upvotes: 0
Views: 2854
Reputation: 46
I came across the same issue. When I print out request.POST, I can verify that the data of the MultipleChoiceField are transmitted. But when I manually select it (request.POST["fieldname"]) it only prints out the last element.
I'm pretty sure that there is a serialization issue, the information gets out correctly but is not probably received. Somewhere the forms part from Django does not like the output and will not verify correctly (probably it expects a list but gets an Integer?!). From my point of view, the main issue lies before the forms part. Personally, I have no a fix yet, but will try the Charfield Choise option from above. Still, I think it is an issue, which should be fixed!
Edit: Charfield does not work for me, because i need multiple answer options.
I now have build a minimal test, which works completly fine.
Also i found the reason, why I only get the last element of the list:
print(request.body)
output: long bytestring ... + "&fieldname=2&fieldname=3"
Django only uses the last one. I still do not understand why it works in a simplyfied version and why it is formatted in the way above in the more complex one. But, for now I have a workaround.
Upvotes: 0
Reputation: 11665
You can change your model and form as below
models.py
class CVSS(models.Model):
AV_CHOICES = AV = [
(u'N', u'Network'),
(u'A', u'Adjacent'),
(u'L', u'Local'),
(u'P', u'Physical'),
]
av = models.CharField(
choices=AV_CHOICES
max_length=1,
null=True,
)
forms.py
class CVSSForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
super(CVSSForm, self).__init__(*args, **kwargs)
self.fields['av'].widget = forms.RadioSelect()
class Meta:
model = CVSS
fields = ['av',]
views.py
def edit(request, fid):
cvss = get_object_or_404(CVSS, finding_id=fid)
cvss_form = CVSSForm(instance=cvss)
if request.method == 'POST':
cvss_form = CVSSForm(instance=cvss, data=request.POST)
if cvss_form.is_valid():
cvss_form.save()
template.html
{{ form }}
Upvotes: 2
Reputation: 22588
You said
the form is never valid if the field is required.
The term required is ambigious here. Django has 2 different options to configure a required or optional field.
null
If True, Django will store empty values as NULL in the database. Default is False.
Null attribute is used to control how the filed is declared at database level. Django doc also explicitely state:
Avoid using null on string-based fields such as CharField and TextField. If a string-based field has null=True, that means it has two possible values for « no data »: NULL, and the empty string. In most cases, it’s redundant to have two possible values for « no data; » the Django convention is to use the empty string, not NULL. One exception is when a CharField has both unique=True and blank=True set. In this situation, null=True is required to avoid unique constraint violations when saving multiple objects with blank values.
In addition, another field control how Django itself will perform validation on a form field that is linked to this model field:
blank
If True, the field is allowed to be blank. Default is False.
Once again, Django doc brings some additional information:
Note that this is different than null. null is purely database-related, whereas blank is validation-related. If a field has blank=True, form validation will allow entry of an empty value. If a field has blank=False, the field will be required.
Upvotes: 0