Django forms validation fail - django

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)

Related

Django - Keep specific fields on form after submit

I have a view that has a simple "save and add another" functionality, that redirects the user to the same page after submit the form.
View:
def new_planning(request):
form = PlanningForm(request.POST)
if form.is_valid():
form.save()
if 'another' in request.POST:
messages.success(request, ('Success!'))
return redirect('new_planning')
else:
return redirect('list_planning')
return render(request, 'pages/planning/new_planning.html', context={
'form': form,
})
Form:
class PlanningForm(forms.ModelForm):
accountplan = ModelChoiceField(
queryset=AccountsPlan.objects.filter(active=True).order_by('code'),
)
month = forms.DateField(
required=True,
error_messages={'required': '', },
)
amount = forms.DecimalField(
max_digits=9,
decimal_places=2,
required=True,
validators=[
error_messages={'required': '', },
)
class Meta:
model = Planning
fields = '__all__'
The function works as expected and after the submit, the same page is rendered with a blank form. What I want is to keep just the "amount" field blank and keep the data typed in the "accountplan" and "month" fields. Is there a way to do this?
I read about instance in the docs, but it doesn't seem to be what I looking for, since I don't want to get the data from the database (if that's possible), but simply keep the last inputs typed in both fields.
If you rewrite the "ModelForm" to a "Model" class, you can get the values of the posted datas, and can be rendered to the page.
For example:
# views.py
def ContactPageView(request):
if request.method == "POST":
email = request.POST.get('email')
message = request.POST.get('message')
message_element = ContactFormObject(email=email, message=message)
message_element.save()
else:
name, message = '', ''
form_data = name, message
return render(request, 'contact.html', {'form_data': form_data})
# models.py
class ContactFormObject(models.Model):
email = models.CharField(max_length=100) #....
ModelForm is more comfortable, but I don't recommend it if you have extra 10 minutes to code some HTML in order to the possibilities of more customization.

how to save custom ModelForm fields in django

i have custom fields in ModelForm and there is no any values on save. im just confuse what to use in view.py to save with data
form.py
class AddCityForm(forms.ModelForm):
duration = forms.ChoiceField(widget=forms.RadioSelect(attrs={
'style': 'background-color: #FAF9F9', 'class': 'mb-3, form-check-inline'}), choices=DURATION_CHOICES)
country = forms.ChoiceField(widget=forms.RadioSelect(attrs={
'style': 'background-color: #FAF9F9', 'class': 'mb-3, form-check-inline'}), choices=CITY_CHOICE)
something = forms.CharField(widget=forms.TextInput(attrs={
'style': 'background-color: #FAF9F9', 'class': 'mb-3'}))
class Meta:
model = Cities
exclude = ['city', 'duration', 'something']
view.py
def add_city(request):
data = dict()
if request.method == 'POST':
form = AddCityForm(request.POST)
if form.is_valid():
form = form.save(commit=False)
form.city = request.POST.get('country')
form.duration = request.POST.get('dur')
form.something = request.POST.get('something')
form = form.save()
messages.success(request, f'Test for Added Successfully')
data['form_is_valid'] = True
else:
data['form_is_valid'] = False
else:
form = AddCityForm()
context = dict(form=form)
data['html_form'] = render_to_string('cites/modal_1.html', context, request=request)
return JsonResponse(data)
can any one help with this ?
Looks like the code is working, i have no idea why did not before i asked this question but i will keep this if any one look for similar question

optional int(request.POST.get('timeout') throws error when empty

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))

How to save Foreign Key text input in Django model form

My view passes an id to my form. This id is a foreign key from another table. I am not able to save the id in the database table.
(id : voucher_id, table in which i am saving the form : TmpPlInvoicedet)
What i want to do
Send voucher_id from (View) to ---> TmpFormDetForm (Form) ---> TmpPlInvoicedet (DB)
Trying to get instance from the table 'TmpPlInvoice' (which has voucher_id as PK) and save it in the form gives me
DoesNotExist at /new/ TmpPlInvoice matching query does not exist
What am i doing wrong?
Views.py
def new_invoic(request):
# Create a voucher id according to my criteria
temp_vid = TmpPlInvoice.objects.order_by().values_list("voucher_id", flat=True).distinct()
if not temp_vid:
voucher_id = str(1).zfill(4)
else:
voucher_id = str(int(max(temp_vid)) + 1).zfill(4)
# POST METHOD TRying to show the voucher_id in the form in readonly format
if request.method == 'POST':
form_pk = TmpForm(request.POST or None, voucher_id=voucher_id,initial={'voucher_id': voucher_id})
if form.is_valid():
form_pk.save()
form = TmpFormDetForm(request.POST or None, voucher=voucher_id, initial={'voucher': voucher_id})
# My assumption is that since i have save the voucher_id in the TmpInvoice table so i can get the PK voucher_id value and save it in the TmpInvoiceDetForm
form.save()
return HttpResponseRedirect('/new/')
else:
return render_to_response('test.html',{'form': form, 'form_pk': form_pk},context_instance=RequestContext(request))
else:
form_pk = TmpForm(voucher_id=voucher_id,initial={'voucher_id': voucher_id})
form = TmpFormDetForm(voucher=voucher_id, initial={'voucher': voucher_id})
return render_to_response('test.html',{'form': form, 'form_pk': form_pk},context_instance=RequestContext(request))
Forms.py
# This form contains the FK. This one is giving errors while saving.
class TmpFormDetForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
voucher = kwargs.pop('voucher', None)
super(TmpFormDetForm, self).__init__(*args, **kwargs)
self.fields['voucher'].initial = TmpPlInvoice.objects.get(voucher_id=voucher)
voucher = forms.CharField(widget=forms.TextInput(attrs={'size':'40'}))
class Meta:
model = TmpPlInvoicedet
exclude = ['emp_id','particulars','qty', 'rate' , 'itemtot', 'stock_code' ]
widgets = {
'voucher': forms.TextInput(attrs={'class': 'form-control', 'placeholder': '', 'required': 'False', 'name': 'voucher','readonly': 'readonly'}),
'lineitem': forms.TextInput(attrs={'class': 'form-control', 'placeholder': 'Add Total', 'required': 'False', 'blank': 'True'})}
# This form takes the PK. I save the PK here first.
class TmpForm(forms.ModelForm):
def __init__(self, *args, **kwargs):
voucher_id = kwargs.pop('voucher_id', None)
super(TmpFor, self).__init__(*args, **kwargs)
self.fields['voucher_id'].initial = voucher_id
pos_code = MyModelChoiceField(queryset=Positions.objects.all(), widget=forms.Select(attrs={'class': 'select2_single form-control', 'blank': 'True'}))
cust = MyModelChoiceField(queryset=Custodian.objects.all(), to_field_name='acct_id',widget=forms.Select(attrs={'class': 'select2_single form-control', 'blank': 'True'}))
acct = MyModelChoiceField(queryset=Item.objects.all(), to_field_name='stock_code',widget=forms.Select(attrs={'class':'select2_single form-control', 'blank': 'True'}))
voucher_date = forms.DateField(widget=forms.TextInput(attrs={'tabindex': '-1', 'class': 'form-control has-feedback-left', 'id': 'single_cal1','aria-describedby': 'inputSuccess2Status'}))
class Meta:
model = TmpPlInvoice
exclude = ['net_amt', 'post_date', 'address', 'posted']
widgets = {
'voucher_id': forms.TextInput(attrs={'class': 'form-control', 'placeholder': '', 'required':'False', 'name': 'voucher_id', 'readonly': 'readonly'}),
'voucher_date': forms.TextInput(attrs={'tabindex': '-1', 'class': 'form-control has-feedback-left', 'id': 'single_cal1','aria-describedby': 'inputSuccess2Status'}),
'particulars': forms.TextInput(attrs={'class': 'form-control', 'placeholder': 'Add Particulars', 'required':'False'}),
}
Models.py
class TmpPlInvoicedet(models.Model):
stock_code = models.CharField(max_length=13, blank=True, null=True)
voucher = models.ForeignKey(TmpPlInvoice, db_column='voucher_id')
lineitem = models.CharField(max_length=6)
particulars = models.CharField(max_length=200, blank=True, null=True)
qty = models.FloatField(blank=True, null=True)
rate = models.FloatField(blank=True, null=True)
itemtot = models.FloatField(blank=True, null=True)
emp_id = models.CharField(max_length=8, blank=True, null=True)
class Meta:
managed = False
db_table = 'tmp_pl_invoicedet'
unique_together = (('voucher', 'lineitem'),)
Easy peesy.
def master_detail(request):
def get_new_voucher_id():
temp_vid = TmpPlInvoice.objects.order_by().values_list("voucher_id", flat=True).distinct()
logger.info('Voucher ID already present %s', temp_vid)
if not temp_vid:
voucher_id = str(1).zfill(4)
else:
voucher_id = str(int(max(temp_vid)) + 1).zfill(4)
return voucher_id
voucher_id = get_new_voucher_id()
author_form = TmpForm(initial={'voucher_id': voucher_id})
author = TmpPlInvoice()
BookFormSet = inlineformset_factory(TmpPlInvoice, TmpPlInvoicedet, exclude=('emp_id', 'itemtot', 'voucher', 'lineitem','id'),
form=TmpFormDetForm, extra=1)
formset = BookFormSet(instance=author)
if request.method == 'POST':
logger.info('*'*50)
author = TmpForm(request.POST, initial={'voucher_id': voucher_id})
if author.is_valid():
logger.info('Data for Author is %s', author.cleaned_data)
created_author = author.save()
formset = BookFormSet(request.POST, instance=created_author)
if formset.is_valid():
logger.info('Data for Book is %s', formset.cleaned_data)
formset.save()
else:
logger.info('Formset errors %s', formset.errors)
else:
logger.info('Master form errors %s', author.errors)
logger.info('*'*50)
return HttpResponseRedirect('/new/')
else:
logger.info('Formset from GET is %s', formset.errors)
return render_to_response('new_invoice.html',
{'form': author_form, 'formset': formset},context_instance=RequestContext(request))
You seem to be creating a new invoice ID and then, in your form, attempting to get the invoice matching that ID. But that invoice doesn't exist yet, of course, because you haven't created it.
You might want to use get_or_create to ensure that the invoice is created if it doesn't exist.

Django Forms - Can't raise validation error in tests

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.