Form Validation Not Working Django - python

I'm not sure where I'm going wrong with the form validation... I also want to display error messages wether the for was submitted successfully or not.
Currently, I'm using a DetailView, where a person can fill in a BookingForm()
forms.py
from django.core.validators import RegexValidator
class BookingForm(forms.Form):
Name = forms.CharField()
Postcode = forms.CharField(max_length=8,label='Postcode')
phone_regex = RegexValidator(regex=r'^\+?1?\d{9,15}$', message="Phone number must be entered in the format: '+999999999'. Up to 15 digits allowed.")
Phone = forms.CharField(max_length=15,validators=[phone_regex],label='Phone')
Date = forms.CharField(max_length=30,label='Date')
Time = forms.CharField(max_length=10,label='Time')
In my views.py I defined def post to allow post requests. However, I always get the ELSE loop returned
def post(self,request,**kwargs):
# put your own credentials here
form = BookingForm(self.request.POST)
if form.is_valid():
user_phone = form.cleaned_data['Phone']
postcode = form.cleaned_data['Postcode']
date = form.cleaned_data['Date']
account_sid = "***"
auth_token = "***"
found = Model.objects.get(id=self.kwargs['pk'])
client = Client(account_sid, auth_token)
client.messages.create(
to=Model.phone_number,
from_="+442033225719",
body="You have a new booking." +
"Call phone number:{}. Address: {}. Date:{}"
.format(user_phone,postcode,date))
messages.success(request, 'Your booking was reserved.')
else:
messages.error(request, 'Error occurred.')
return redirect('users:detail', pk=self.kwargs['pk'])
And my model_detail.html which handles the form.
FURTHER EDIT:
I created the following HTML template which I include in the main template using {% include 'booking_form.html' %}:
<!-- Book Now -->
<form action="" method="post">
{{ form.non_field_errors }}
{% csrf_token %}
<div class="boxed-widget">
<h3><i class="fa fa-calendar-check-o "></i> Book Now</h3>
<div class="row with-forms margin-top-0">
<div class="col-lg-6 col-md-12">
{{ form.name.errors }}
<label for="{{ form.name.id_for_label }}">Full Name:</label>
{{ form.name }}
</div>
<div class="col-lg-6 col-md-12">
{{ form.postcode.errors }}
<label for="{{ form.postcode.id_for_label }}">Postcode:</label>
{{ form.postcode }}
</div>
</div>
<div class="row with-forms margin-top-0">
<div class="col-lg-12 col-md-12">
{{ form.name.errors }}
<label for="{{ form.name.id_for_label }}">Full Name:</label>
{{ form.name }}
</div>
</div>
<div class="row with-forms margin-top-0">
<div class="col-lg-6 col-md-12">
{{ form.date.errors }}
<input name="Date" type="text" id="booking-date" data-lang="en" data-large-mode="true" data-min-year="2017" data-max-year="2020">
</div>
<div class="col-lg-6 col-md-12">
{{ form.time.errors }}
<input name="Time" type="text" id="booking-time" value="9:00 am">
</div>
</div>
<!-- progress button animation handled via custom.js -->
<button type="submit" class="progress-button button fullwidth margin-top-5"><span>Book Now</span></button>
</div>
</form>
<!-- Book Now / End -->
My BookingForm in forms.py has remained unchanged. However, now, I see no fields to input. I'm assuming this is because the form is not passed into the template.
Full content of my views.py of the DetailView:
class TeacherView(generic.DetailView,FormMixin):
model = Teacher
form_class = BookingForm
def post(self,request,**kwargs):
form = BookingForm(self.request.POST)
if form.is_valid():
user_phone = form.cleaned_data['Phone']
account_sid = "***"
auth_token = "***"
teacher = Teacher.objects.get(id=self.kwargs['pk'])
client = Client(account_sid, auth_token)
client.messages.create(
to=teacher.phone_number,
from_="+442033225719",
body=""
messages.success(request, 'Success Message')
return redirect('users:index')
else:
messages.error(request, 'Error occured.')
return redirect("users:index")
def get_context_data(self, **kwargs):
# Call the base implementation first to get a context
context = super(TeacherView, self).get_context_data(**kwargs)
# Add extra context from another model
context['form'] = self.get_form()
return context

You're always redirecting, whether the form is valid or not, so obviously you can't have the form errors displayed. If you want the form errors to be shown in your template, you need to render the template and return it as response when the validation fail - example with a plain function-based view (if you insist on using generic class-based views then use a FormMixin with your DetailView):
def myview(request, ...):
if request.method == "POST":
form = MyForm(request.POST):
if form.is_valid():
handle_the_form(...)
messages.success(request, "success message here")
return redirect(where you want to redirect)
else:
# GET, display an empty form
form = MyForm()
return render(request, "mytemplate.html", context={"form":form})
Also in your template you need to use the form itself instead of hardcoding it, as explained here in the FineManual.
wrt/ the messages framework's messages not displaying either, this is certainly a configuration issue, so re-read the messages doc and check your settings etc.

Related

Passing username (and viewing as uneditable in html) in form Django

I have a form where I would like to have a username and production line send along. The thing is that the username should be taken from current logged user (and viewed as uneditable field) but the production line should be selected via dorpdown.
It works by somehow since when I click on "User" dropdown it shows only the logged user.
views:
def order(request):
storage = PartNumber.objects.all()
username = request.user.username
if request.method == 'POST':
order_form = OrderForm(username=username, data=request.POST)
if order_form.is_valid():
order_form.save()
return redirect('order')
elif request.method == 'GET':
order_form = OrderForm(username=username)
return render(request, 'dashboard/order.html', {"form": order_form, "username": username})
forms:
class OrderForm(forms.ModelForm):
class Meta:
model = Order
fields = ('end_user_id', 'production_line_id')
def __init__(self, *args, **kwargs):
username = kwargs.pop('username', None)
super(OrderForm, self).__init__(*args, **kwargs)
self.fields['end_user_id'].queryset = User.objects.filter(username=username)
models:
class Order(models.Model):
end_user_id = models.ForeignKey(User, on_delete=models.CASCADE)
production_line_id = models.OneToOneField(ProductionLine, on_delete=models.CASCADE)
date_ordered = models.DateTimeField(auto_now_add=True, null=True)
date_completed = models.DateTimeField(auto_now=True, null=True)
def __str__(self):
return str(self.status)
html:
{% extends 'dashboard/main.html' %}
{% load static %}
{% load bootstrap %}
{% block content %}
<h3>Zamowienie</h3>
<br>
<!--{{ form|bootstrap }}-->
<form role="form" action="" method="post">
{% csrf_token %}
<div class="form-group">
<label>Uzytkownik </label>
<!--<input class="form-control" placeholder="{{user}}" readonly>-->
{{ form.end_user_id }}
</div>
<br>
<div class="form-group">
<label>Linia produkcyjna </label>
{{ form.production_line_id }}
</div>
<br>
<div class="text-center">
<button type="submit" class="btn btn-primary">Przeslij</button>
</div>
</form>
{% endblock content %}
Now if you look at the html above, the commented line :
<!--<input class="form-control" placeholder="{{user}}" readonly>-->
actually gives me the look I want but doesn't send the username (doesn't work)
I was trying different solutions from different posts that I found here but nothing seems to work for me.
Any ideas ?
You are passing username to the template using the variable username.
In the template you are trying to get the username by using the variable user.
<input class="form-control" placeholder="{{user}}" readonly>
Should be :
<input class="form-control" placeholder="{{username}}" readonly>

how can i access a context of function view with a separate page in base page?

i created newsletters app with below code after that i have a URL : subscribe that perfectly work and save my email after that i want to add this ability in base page for example a user don't go to subscribe page and subscribed i want directly have access in base template and subscribe .
i want to know this because i want add login form to base page and have this problem with that .
this my code and this is my template
tnx for help.
views.py
from django.shortcuts import render
def Subscribe(request):
form = SiqnupNewslettersForm(request.POST or None)
if form.is_valid():
instance = form.save(commit=False)
if SignupNewsletters.objects.filter(email=instance.email).exists():
print("this email already taken ")
else :
instance.save()
context = {
'form':form
}
template_name = "subscribe.html"
return render(request,template_name,context)
def Unsubcribe(request):
form = SiqnupNewslettersForm(request.POST or None)
if form.is_valid():
instance = form.save(commit=False)
if SignupNewsletters.objects.filter(email= instance.email).exists():
SiqnupNewslettersForm.objects.filter(email = instance).delete()
else:
print("your email is not here")
context = {
'form' : form
}
template_name = "unsubscribe.html"
return render(request,template_name,context)
subscribe.html
{% block content %}
<div class="container">
<div class="row">
<form method="POST" action="">
{% csrf_token %}
<div class="form-group">
{{ form }}
</div>
<input type='submit' class="btn btn-primary"
value="submit">
</form>
</div>
</div>
{% endblock %}
urls.py
urlpatterns = [
path('subscribe/',views.Subscribe,name="subscribe"),
path('unsubscribe/',views.Unsubcribe,name= "unsubscribe"),
]
and finally what i have to do for add newsletter form in base page ?
base.html

Django save data from forms to database

Hi i am relatively new to Django. I am currently trying to store values into database. Timesheet.html is basically my form and i have tried to do the validation but it doesn't work. I have searched everything i can but the data being filled out wont be saved into database. Is there anywhere to check that the checkbox has been ticked before user can submit it ?
timesheet.html
<form method="POST" onsubmit="return validation()" action="">
{% csrf_token %}
<div class="content-wrapper">
<div class="sub-content">
<div>
<p>Student ID: {{timesheet.studentID}}</p>
<input id="sid" type="field" name="studentid">
</div>
</div>
<div class="sub-content">
<div>
<p>Student Name: {{timesheet.studentName}}</p>
<input id="sname" type="field" name="studentname">
</div>
</div>
<div class="sub-content">
<div>
<p>Start Date: {{timesheet.startDate}}</p>
<input id="sdate" type="date" name="startdate">
</div>
</div>
<div class="sub-content">
<div>
<p>End Date: {{timesheet.endDate}}</p>
<input id="edate" type="date" name="enddate">
</div>
</div>
</div>
<div class="end-content">
<div class="center-align">
<div class="checklist">
<p>By checking this box I agree that I have satisfied all requirements to continue receiving my scholarship
allowance.</p>
<input id="agree" type="checkbox" name="checkbox" class="tick-att">
</div>
<br>
<div class="align-right">
<input type="submit" class="button" name="submit" value="submit" >
</div>
</div>
</div>
</form>
models.py
class Timesheet(models.Model):
studentID = models.CharField("Student ID", max_length=8, primary_key=True, default="")
studentName = models.CharField("Student Name", max_length=500, default="")
startDate = models.DateField("Start Date", max_length=8)
endDate = models.DateField("End Date", max_length=8)
def __str__(self):
return self.studentID
class TimesheetForm(forms.ModelForm):
class Meta:
model = Timesheet
fields = '__all__'
views.py
def timesheet(request):
if request.method == "POST":
form = TimesheetForm(request.POST)
if form.is_valid():
timesheet = form.save(commit=False)
timesheet.studentID = request.POST.get('studentID')
timesheet.studentName = request.POST.get('studentName')
timesheet.startDate = request.POST.get('startDate')
timesheet.endDate = request.POST.get('endDate')
timesheet.save()
return HttpResponseRedirect(reverse('hrfinance/timesheet.html'))
#if the form is not valid, redirect the student to the same page
else:
form = TimesheetForm()
return render(request, 'hrfinance/timesheet.html', {'form': form})
else:
form = TimesheetForm()
return render(request, 'hrfinance/timesheet.html', {'form': form})
There are lots of very strange things here.
Firstly, there is no need to set the fields manually on save. That is exactly what form.save() does in the first place. (And if you ever did need to set something manually, you should always get it from form.cleaned_data rather than request.POST.)
Secondly, you re-instantiate the form if it fails validation. That means that the users can never see the errors that are preventing it from validating.
Thirdly, you should show errors in the template. Along with that, you should let Django itself output your fields so that they are automatically prepopulated when the form is invalid.
Finally, you should add your checkbox as a field on the form, so that it is validated along with everything else.
class TimesheetForm(forms.ModelForm):
checkbox = forms.BooleanField()
class Meta:
model = Timesheet
fields = '__all__'
...
def timesheet(request):
if request.method == "POST":
form = TimesheetForm(request.POST)
if form.is_valid():
timesheet = form.save()
return HttpResponseRedirect(reverse('hrfinance/timesheet.html'))
else:
form = TimesheetForm()
return render(request, 'hrfinance/timesheet.html', {'form': form})
...
<form method="POST" onsubmit="return validation()" action="">
{% csrf_token %}
{{ form.errors }}
<div class="content-wrapper">
<div class="sub-content">
<div>
<p>Student ID: {{timesheet.studentID}}</p>
{{ form.studentID }}
</div>
</div>
</div>
.... etc...
<div class="checklist">
<p>By checking this box I agree that I have satisfied all requirements to continue receiving my scholarship
allowance.</p>
{{ form.checkbox }}
</div>

Django : How to show form validation errors if form is not valid on base.html?

I have a form on my base.html since I want to show it on every pages, I'd like to show validations errors whenever the form is not valid, the problem is that it redirects me to my view even if it's the validation is false and throws me an error 500.
Here how I did : views.py
def askProject(request):
if request.method == 'POST':
form = AskProjectForm(request.POST)
if form.is_valid():
form.save()
return redirect(reverse('success-msg'))
else:
form = AskProjectForm()
forms.py :
class AskProjectForm(forms.ModelForm):
class Meta:
model = AskProject
fields = ['prenom', 'nom', 'numero']
def clean_prenom(self):
prenom = self.cleaned_data['prenom']
if len(prenom) < 3:
raise ValidationError('Votre prénom doit etre plus long que 1 caractère.')
return prenom
...
context_processors.py :
from pages.forms import AskProjectForm
def AskProjectFormProcessor(request):
return {'AskProjectForm' : AskProjectForm()}
base.html :
<form method="post" action="{% url 'ask-project' %}">
{% csrf_token %}
<div class="col-lg-4 col-md-4 col-sm-12 col-xs-12">
<p>{{ AskProjectForm.prenom.errors }}</p>
<label for="prenom">Votre prenom<span class="form-required" title="Ce champ est requis.">*</span></label>
{{ AskProjectForm.prenom }}
</div>
...
<div class="form-button col-lg-12 col-md-12 col-sm-12 col-xs-12">
<button class="btn btn-default submit">C'est parti !</button>
</div>
</form>
How can I resolve this issue since I cannot user render(request, ..., {...}) on base.html ?
I'd like to return to the same page where the user is while showing the errors validation messages.
You can post the data using an ajax request(jQuery).
That would not cause the reloading or redirecting to another page.
Also if there is any error in the form, that could also be validated in the frontend.
If the form is valid, then response object can be manipulated however you care.

Multiple Django Crispy Forms In One View

When placing 2 forms in a view using the form|crispy filter and this answer to handle 2 forms in a single view: Proper way to handle multiple forms on one page in Django I am getting this error.
views.py:
def test_form(request):
if not request.user.is_authenticated():
return redirect(settings.LOGIN_URL)
title = 'test form'
row_control_form = RowControlForm(request.POST or None)
entry_form = EntryForm(request.POST or None)
context = {
'title': title,
'row_control_form': row_control_form,
'entry_form': entry_form,
}
if 'row_control_submit' in request.POST:
if row_control_form.is_valid():
row_control_form.save()
if 'entry_submit' in request.POST:
if entry_form.is_valid():
entry_form.save()
return render(request, "timesheet/test_form.html", context)
forms.py
class RowControlForm(forms.ModelForm):
class Meta:
model = RowControl
fields = ['month_control_record', 'department', 'activity', 'notes']
def clean(self):
cleaned_data = self.cleaned_data
# Ensures row is unique
try:
RowControl.objects.get(month_control_record=cleaned_data['month_control_record'],
department=cleaned_data['department'],
activity=cleaned_data['activity'],
notes=cleaned_data['notes'])
except RowControl.DoesNotExist:
pass
else:
raise ValidationError('This row already exists')
# Always return cleaned data
return cleaned_data
class EntryForm(forms.ModelForm):
class Meta:
model = Entry
fields = ['row_control', 'date', 'hours']
def clean(self):
cleaned_data = self.cleaned_data
# Ensures data is unique (only 1 hours entry for each date and row_control)
try:
Entry.objects.get(row_control=cleaned_data['row_control'],
date=cleaned_data['date'])
except Entry.DoesNotExist:
pass
else:
raise ValidationError('This entry already exists')
# Always return cleaned data
return cleaned_data
test_form.html
{% extends "base.html" %}
{% load crispy_forms_tags %}
{% block content %}
<div class="col-md-6 col-md-offset-3">
<h1 class="page-header"> Form Test </h1>
<form method="POST" action="{{ request.path }}">
{% csrf_token %}
{{ row_control_form|crispy }}
<button class="btn btn-primary" type="submit" value="Submit" name="row_control_submit" ><i class="fa fa-lg fa-floppy-o"></i> Save</button> </form>
</br>
</div>
<div class="col-md-6 col-md-offset-3">
<h1 class="page-header"> Form Test </h1>
<form method="POST" action="{{ request.path }}">
{% csrf_token %}
{{ entry_form|crispy }}
<button class="btn btn-primary" type="submit" value="Submit" name="entry_submit" ><i class="fa fa-lg fa-floppy-o"></i> Save</button> </form>
</br>
</div>
{% endblock %}
To provide context to the error:
Line 42 of forms.py is:
Entry.objects.get(row_control=cleaned_data['row_control'],
EDIT: Further investigation has shown that the issue is that both form validations are being run no matter which submit button is pressed, the request.POST when submitting valid data for the RowControlForm is:
<QueryDict: {'csrfmiddlewaretoken': ['HffmmbI31Oe0tItYDfYC4MoULQHL0KvF'], 'notes': ['Cool'], 'row_control_submit': ['Submit'], 'month_control_record': ['1'], 'department': ['1'], 'activity': ['1']}>
Therefore entry_submit is not in the request.POST and that validation should not run yet it is?
Firstly, you need to fix this line of your form's clean method
def clean(self):
...
Entry.objects.get(row_control=cleaned_data['row_control'],
You can't assume that row_control will be in the cleaned_data. You either need to add a check if 'row_control' in cleaned_data or catch the KeyError, then update the rest of the method appropriately. You should fix this, even though you didn't see this error until you put multiple forms on one page. It shouldn't be possible to cause a 500 server error by leaving a value out of a POST request. Users could do this even if there is only one form on the page.
Validation is running for both forms, because you are instantiating both forms with the post data, regardless of which submit button was pressed.
row_control_form = RowControlForm(request.POST or None)
entry_form = EntryForm(request.POST or None)
You should only use the POST data for the form you wish to submit.
row_control_form = RowControlForm()
entry_form = EntryForm()
if 'row_control_submit' in request.POST:
row_control_form = RowControlForm(request.POST)
if row_control_form.is_valid():
if 'entry_submit' in request.POST:
entry_form = EntryForm(request.POST)
if entry_form.is_valid():
entry_form.save()
Finally, it's good practice to redirect the user once they have successfully submitted a valid form.

Categories

Resources