This field timeout = int(request.POST.get('timeout')) throws an error saying
invalid literal for int() with base 10: ''
this is my model field: timeout = models.IntegerField(default=10)
The forms submits just fine if I submit number because the form interprets it as a string but my form handler will convert it into integer. But it fails if I leave the field blank. Seems like it can't process an empty string.
What can I do ?
forms.py:
class TestCaseSuiteForm(forms.ModelForm):
name = forms.CharField(widget=forms.TextInput(attrs={
'class': 'form-control',
'placeholder': 'Enter Name'}), label='')
documentation = forms.CharField(widget=forms.TextInput(attrs={
'class': 'form-control',
'placeholder': 'Enter Documentation'}), label='')
setup = forms.CharField(widget=forms.TextInput(attrs={
'class': 'form-control',
'placeholder': 'Enter Setup'}), label='')
teardown = forms.CharField(widget=forms.TextInput(attrs={
'class': 'form-control',
'placeholder': 'Enter teardown'}), label='')
force_tags = forms.CharField(widget=forms.TextInput(attrs={
'class': 'form-control',
'placeholder': 'Enter Force Tags'}), label='')
timeout = forms.IntegerField(widget=forms.TextInput(attrs={
'class': 'form-control',
'placeholder': 'Enter Timeout (optional)'}),
required=False, label='')
class Meta:
model = TestCase
fields = [
'name',
'documentation',
'force_tags',
'setup',
'teardown',
'timeout',
]
my view:
def index(request):
if request.method == 'POST':
form_tc = TestCaseForm(request.POST)
form_ts = TestCaseSuiteForm(request.POST)
if form_tc.is_valid() or form_ts.is_valid():
form_tc.save()
form_ts.save()
return redirect('/list')
In case you're wondering ... I've got two forms using one submit button.
Having gone to the trouble of defining a form and validating it, you are supposed to use that validated data, rather than resorting to the raw post data. Not only will the validated data use defaults as defined in the form where necessary, it will also convert types etc.
if form_tc.is_valid() and form_ts.is_valid():
TestCase.objects.create(
name=form.cleaned_data['name'],
documentation=cleaned_data['documentation'],
...
)
Note, you need to use the unprefixed field names as the keys here.
But this still isn't really getting you what you want. You haven't defined all your model fields as form fields, so you won't get defaults for the fields you haven't defined. Instead you should be using a model form.
class TestCaseSuiteForm(forms.ModelForm):
class Meta:
model = TestCase
fields = ['name', 'documentation', ...]
and now in your view you simply save the form to create the objects:
if form_tc.is_valid() and form_ts.is_valid():
form_tc.save()
form_ts.save()
Now your model defaults will be used appropriately.
Set a default using:
timeout = int(request.POST.get('timeout', 0))
Related
My edit view in my application won't update the court details whenever I want to edit it. I keep getting the error below. Adding, Viewing & Deleting functions are working okay. I cant find a solution on the other S/O answers. Some help on this would be appreciated. Thanks
Exception Value: 'str' object has no attribute 'court_number'
model.py
# Create your models here.
class Court(models.Model):
court_number = models.CharField(max_length=255, verbose_name='Court Number.')
accused_person = models.ForeignKey(AccusedPerson, on_delete=models.CASCADE, verbose_name='Accused Person')
court = models.CharField(choices=COURTS, max_length=255, verbose_name='Court')
court_verdict = models.CharField(choices=VERDICT, max_length=50, verbose_name='Court Status')
scheduled_on = models.DateField(verbose_name='Scheduled On')
created_by = models.ForeignKey(Profile, on_delete=models.CASCADE, verbose_name='Police Officer')
date_created = models.DateTimeField(auto_now_add=True, verbose_name='Date Created')
date_updated = models.DateTimeField(auto_now=True, verbose_name='Date Updated')
def __str__(self):
return str(self.court_number)
class Meta:
verbose_name_plural = 'Court'
forms.py
class EditCourtInfoForm(forms.Form):
court_number = forms.CharField(max_length=50, required=True, widget=forms.TextInput(attrs={'id': 'court_number', 'class': 'form-control mb-4', 'name': 'court_number', 'placeholder': 'Court Number'}))
accused_person = forms.ChoiceField(required=True, widget=forms.Select(attrs={'id': 'accused_person', 'class': 'form-control mb-4', 'name': 'accused_person', 'placeholder': 'Accused Person'}))
court = forms.ChoiceField(choices=COURTS, required=True, widget=forms.Select(attrs={'id': 'court', 'class': 'form-control mb-4', 'name': 'court', 'placeholder': 'Court'}))
court_verdict = forms.ChoiceField(choices=VERDICT, required=True, widget=forms.Select(attrs={'id': 'court_verdict', 'class': 'form-control mb-4', 'name': 'court_verdict', 'placeholder': 'Verdict'}))
scheduled_on = forms.DateField(required=True, widget=forms.DateInput(attrs={'type': 'date', 'id': 'scheduled_on', 'class': 'form-control mb-4', 'name': 'scheduled_on', 'placeholder': 'Scheduled On'}))
def __init__(self, *args, **kwargs):
super(EditCourtInfoForm, self).__init__(*args, **kwargs)
self.fields['accused_person'].choices = [(e.pk, f"{e.first_name}" + ' ' + f"{e.middle_name}" + ' ' + f"{e.last_name}") for e in AccusedPerson.objects.all()]
class Meta:
model = Court
fields = ['court_number', 'accused_person', 'court', 'court_verdict', 'scheduled_on']
views.py
def EditCourtInfo(request, id):
court = Court.objects.get(id=id)
if request.method == 'POST':
form = EditCourtInfoForm(request.POST)
if form.is_valid():
context = {'has_error': False}
court_number = form.cleaned_data['court_number']
accused_person = form.cleaned_data['accused_person']
court = form.cleaned_data['court']
court_verdict = form.cleaned_data['court_verdict']
scheduled_on = form.cleaned_data['scheduled_on']
print(scheduled_on)
court.court_number = court_number # The problem
court.accused_person = AccusedPerson.objects.get(pk=int(accused_person))
court.court = court
court.court_verdict = court_verdict
court.scheduled_on = scheduled_on
court.created_by = request.user.profile
if not context['has_error']:
court.save()
messages.success(request, '✅ Court Record Successfully Updated!')
return redirect('OfficerCourtInfo')
else:
messages.error(request, '⚠️ Court Record Was Not Updated!')
return redirect('EditCourtInfo', id=id)
else:
form = EditCourtInfoForm()
return render(request, 'Officer Edit Court.html', {'form':form, 'court':court})
The immediate problem is that you have
court = Court.objects.get(id=id)
but then later
court = form.cleaned_data['court']
So you are re-using a variable for a different purpose. You could fix this problem by using a different variable for one of these. However, you are making this much more complicated than you need to. The form will already take care of editing the Court object for you:
def EditCourtInfo(request, id):
court = Court.objects.get(id=id)
if request.method == 'POST':
form = EditCourtInfoForm(request.POST, instance=court). # pass the court object to the form
if form.is_valid():
form.save() # just save the form
messages.success(request, '✅ Court Record Successfully Updated!')
return redirect('OfficerCourtInfo')
else:
messages.error(request, '⚠️ Court Record Was Not Updated!')
return redirect('EditCourtInfo', id=id)
else:
form = EditCourtInfoForm()
return render(request, 'Officer Edit Court.html', {'form':form, 'court': court})
You will need to change your form to extend ModelForm instead of Form:
class EditCourtInfoForm(forms.ModelForm):
This code is untested, so I may have missed something. I suggest checking out the Django documentation to fill in any gaps in your understanding. You might even consider using a class-based view instead.
I have a form, ApplyJobForm and a Formset, ApplyJobFormset. GET method works when I pass the form and the formset to a view, but for the post request the form and the formset is_valid() isn't working, after clicking submit it returns me to a view without saving. I am unable to save the form with the formset, I don't know what I'm doing wrong here.
Here are my codes.
models.py
class Applicants(models.Model):
user = models.ForeignKey(User, on_delete=models.CASCADE)
job = models.ForeignKey(Job, on_delete=models.CASCADE, related_name='applicants')
experience = models.IntegerField(blank=True, null=True)
cv = models.FileField(upload_to=user_directory_path)
degree = models.CharField(choices=DEGREE_TYPE, blank=True, max_length=10)
created_at = models.DateTimeField(default=timezone.now)
def __str__(self):
return f'{self.user.get_full_name()} Applied'
class Certification(models.Model):
applicant = models.ForeignKey(Applicants, on_delete=models.CASCADE, related_name='applicant_certifications')
name = models.CharField(max_length=50)
certification = models.FileField(upload_to=user_directory_path, blank=True)
def __str__(self):
return f'{self.user.get_full_name()} certificate'
forms.py
class ApplyJobForm(forms.ModelForm):
class Meta:
model = Applicants
fields = ('job', 'degree', 'experience', 'cv')
exclude = ('job',)
labels = {
'degree': 'Degree',
'experience': 'Experience',
'cv': 'CV',
}
widgets = {
'degree': forms.Select(attrs={
'class': 'form-control',
}
),
'experience': forms.NumberInput(
attrs={
'class': 'form-control',
}
),
'cv': forms.FileInput(
attrs={
'class': 'form-control',
}
),
}
ApplyFormset = modelformset_factory(
Certification,
fields=('name', 'certification'),
extra=1,
widgets={
'name': forms.TextInput(
attrs={
'class': 'form-control',
'placeholder': 'Certification name'
}
),
'certification': forms.FileInput(
attrs={
'class': 'form-control',
'placeholder': 'Upload certification'
}
)
}
)
views.py
def job_apply(request, job_id=None):
template_name = 'apply_form.html'
applyform = ApplyJobForm(request.GET or None)
job = get_object_or_404(Job, id=job_id)
formset = ApplyFormset(queryset=Certification.objects.none())
if request.method == 'GET':
context = {'applyform': applyform, 'formset': formset}
return render(request, template_name, context)
elif request.method == 'POST':
applyform = ApplyJobForm(request.POST)
formset = ApplyFormset(request.POST)
if applyform.is_valid() and formset.is_valid():
apply = applyform.save(commit=False)
applyform.job = job
apply.save()
for form in formset:
# so that `apply` instance can be attached.
certification = form.save(commit=False)
certification.apply = apply
certification.save()
return redirect('successful-apply')
else:
return redirect('job-detail', id=job.id)
return render(request, template_name, {'applyform': applyform, 'formset': formset})
Here an applicant can add as many certification field when applying for a job, although the certification field is not a required field. Certification model is bound to the Applicants model.
.html
<form class="form" method="POST" action="" enctype="multipart/form-data" role="form" autocomplete="off">
.................
</form>
First of all, never redirect if your forms are not valid. You want to render your template with the invalid form so that you can display the errors to the user. This also helps debugging since you'll see the errors.
So in your view, remove these two lines:
else:
return redirect('job-detail', id=job.id)
so that the invalid case renders the forms in your template.
Next, since you have files to upload, you need to initialise forms that require files with request.FILES:
formset = ApplyFormset(request.POST, request.FILES)
(and the same for applyform).
Finally make sure that in your template you are also displaying all the errors, either on each field ({{ form.<field>.errors }}) or globally ({{ form.errors }}).
How do I remove the ----- in my django ModelForm widgts?
documentation say to use empty_label but it is for SelectDateWidget
my form
class ProjectForm(forms.ModelForm):
class Meta:
model = Project
exclude = ('copy', 'created', 'researcher', 'keywords', 'application_area', 'predominant_area')
widgets = {
'title':forms.TextInput(attrs={
'class':'form-control',
'placeholder': 'Titulo da oportunidade'
}),
'conclusion_date':forms.TextInput(attrs={
'class': 'form-control',
'type':'text',
'placeholder':'Data de conclusão'
}),
'category': forms.RadioSelect(attrs={
'class':'form-control'
}),
'result':forms.Select(attrs={
'class':'form-control'
}),
}
You can add default value for your select input then it will show default value initially.
For further reference:
https://docs.djangoproject.com/en/2.0/topics/forms/
https://docs.djangoproject.com/en/2.0/ref/forms/fields/
I have two fields in a form:
names_field = CharField(
label='Names',
widget=Textarea(attrs={'rows': '10', 'placeholder': 'input names here ...'}))
file_field = FileField(label='Upload from file')
Both are not required, but I can pass form.is_valid() only if I fill the both fields. It fails when I submit only one field names_field or file_field.
My view part:
form = AddNamessForm(request.POST, request.FILES)
if form.is_valid():
...
I thought that required=False is a default value but it's not true.
names_field = CharField(
label='Names',
reuired=False,
widget=Textarea(attrs={'rows': '10', 'placeholder': 'input names here ...'}))
file_field = FileField(label='Upload from file', reuired=False)
I'm struggling to get my tests to throw a form validation error in Django. This is using standard/default input types.
# forms.py
class NewUserForm(forms.Form):
first_name = floppyforms.CharField(widget=floppyforms.TextInput(attrs={'class': 'form-control input-lg', 'placeholder': 'First Name'})),
last_name = floppyforms.CharField(widget=floppyforms.TextInput(attrs={'class': 'form-control input-lg', 'placeholder': 'Last Name'})),
email = forms.EmailField(),
mobile = floppyforms.CharField(
required=False,
widget=floppyforms.TextInput(attrs={'class': 'form-control input-lg', 'placeholder': 'Mobile number', 'autocomplete': 'false'})),
postcode = floppyforms.CharField(widget=floppyforms.TextInput(attrs={'class': 'form-control input-lg', 'placeholder': 'Postcode'})),
super_balance = floppyforms.CharField(widget=floppyforms.RangeInput(attrs={'class': 'bar', 'type': 'range', 'id': 'rangeinput',
'value': '492500', 'min': '75000', 'max': '1000000',
'step': '5000', }))
# tests.py
class NewUserFormTest(TestCase):
def setUp(self):
self.valid_data = {
'first_name': 'herp',
'last_name': 'derp',
'email': 'herp#derp.com',
'mobile': '0412345678',
'postcode': '00000',
'relationship_status': 'S',
'super_balance': '100000',
'current_super_provider': '49'
}
...
def test_invalid_fields(self):
form = NewUserForm({})
self.assertFalse(form.is_valid()) # correct
data = self.valid_data
data['email'] = 24234 # this field should fail
form = NewUserForm(data)
form.is_valid() # returns True
When I pass a blank dictionary to the initial form. form.errors displays {'super_balance': ['This field is required.']}. This is more confusing because the documentation states that unless explicitly declared then all fields are assumed to be required.
I'm using 1.8.5
Cheers in advance
You need to remove the trailing commas from all the fields in your form.
Instead of
class NewUserForm(forms.Form):
...
email = forms.EmailField(),
...
it should be
class NewUserForm(forms.Form):
...
email = forms.EmailField()
...
At the moment, NewUserForm.email is a tuple, not a field, so any values for that field in the data dictionary are ignored. The only field without the trailing comma is super_balance, which is why it is the only error that appears when you pass a blank dictionary to the form.