I can't get Django to retain entered form values when validation fails. I have created a test page and used the code in the Django guide, and it still doesn't work.
From the Django guide:
We call the form’s is_valid() method; if it’s not True, we go back to the template with the form. This time the form is no longer empty (unbound) so the HTML form will be populated with the data previously submitted, where it can be edited and corrected as required.
And here is my code:
#views.py
def test(request):
if request.method == 'POST':
form = NewAwardForm(request.POST)
if form.is_valid():
return redirect('/test/')
else:
form = NewAwardForm()
print(form)
return render(request, 'test.html', {'form': form})
test.html
<form action="/test/" method="post">
{% csrf_token %}
{{ form }}
<input type="submit" value="Submit">
</form>
forms.py
class NewAwardForm(forms.Form):
recipient_email = forms.EmailField(label='Recipient Email', max_length=100)
Can anyone spot my mistake?
Django renders the email field with type="email". The HTML validation will prevent the browser from submitting an invalid email to the server.
You could test the view by adding a clean method to the form that rejects any emails ending in "example.com".
class NewAwardForm(forms.Form):
recipient_email = forms.EmailField(label='Recipient Email', max_length=100)
def clean_recipient_email(self):
if self.cleaned_data['recipient_email'].endswith('example.com'):
raise forms.ValidationError("No example.org emails allowed")
Now, if you enter hello#example.com and submit the form the HTML validation will allow the request to be submitted, the clean_recipient_email method will raise a validation error, and you'll see that your view and template display the invalid form as expected.
Related
I am trying to understand how a very regularly used code-form in Django views.py actually works. I see the following (or a variation) used a lot, but I can’t find a line-by-line explanation of how the code works – which I need if I am to use it confidently and modify when needed.
Can you let me know if I have understood how Django processes these various components? If not, please indicate where I have misunderstood.
I will start with the model then introduce urls.py the view and the form. I will go through the relevant parts of the code. I will consider:
The model:
#models.py
class CC_Questions(models.Model):
# defining choices in Model : https://docs.djangoproject.com/en/4.0/ref/models/fields/
personality = [
('IF','IF'),
('IT','IT'),
('EF','EF'),
('ET','ET'),
]
q_text = models.CharField('Question text', max_length=200)
#C1_Type = models.CharField('Choice 1 Type', max_length=2)
C1_Type = models.CharField(choices=personality, max_length=2)
Choice1_text = models.CharField('Choice 1 text', max_length=100)
#C2_Type = models.CharField('Choice 2 Type', max_length=2)
C2_Type = models.CharField(choices=personality, max_length=2)
Choice2_text = models.CharField('Choice 2 text', max_length=100)
#
def __str__(self):
return self.q_text[:20]
The url
#
#urls.py
app_name = ‘polls’
urlpatterns = [
…..
# ex: /polls/p2create
path('p2create/', p2_views.p2create, name='p2create'),
…
The view:
#views.py
from.forms import Anyform
#
def p2create(request):
if request.method == 'POST':
form = AnyForm(request.POST)
if form.is_valid():
form.save()
return redirect('/polls/p2')
else:
form = AnyForm()
context = {'form' : form}
return render(request, 'pollapp2/create.html', context)
#
The form:
#forms.py
#
….
from django import forms
from .models import ….. CC_Questions …
…….
class AnyForm(forms.ModelForm):
class Meta:
model = CC_Questions
fields = ['q_text', 'Choice1_text', 'Choice2_text','C1_Type','C2_Type']
The template:
#
# Create.html
#
…..
{% load widget_tweaks %}
…..
<form method="POST">
{% csrf_token %}
…
<div class="row">
<div class="col-lg-5">
<div class="form-group">
<label for="Choice1_text ">Choice 1</label>
{% render_field form.Choice1_text class="form-control" %}
<label for="C1_type">Type 1</label>
{% render_field form.C1_Type class="form-control" %}
…….
Does the code operate as follows?
The user enters URL in browser: http://localhost:8000/polls/p2create/
The urls.py picks the view to execute
path('p2create/', p2_views.p2create, name='p2create'),
views.py runs the view:
def p2create(request):
Now, as no form has yet been "identified" or "loaded" (??) the following test fails:
if request.method == 'POST':
so the Else clause executes
else:
form = AnyForm()
that "sets" the variable form to "AnyForm()"
The following line creates a dictionary, named context, that creates a key 'form' that is linked with the value form (=Anyform)
context = {'form' : form}
The following line searches for create.html in the template directory, passing the directory context as a parameter
return render(request, 'pollapp2/create.html', context)
Then template, create.html, displays various input boxes (??) from :
<label for="Choice1_text ">Choice 1</label>
{% render_field form.Choice1_text class="form-control" %}
When the submit button is pressed on the displayed page, this "passes back" (??) the {% render_field .. %} values to the view (?????)
<form method="POST">
...
<div class="col-lg-4">
<button type="submit" class="btn btn-info">Submit</button>
</div>
...
</form>
the view is executed again (????) , but this time request method is set to "POST" because of the form method="POST" in the template (?????)
if request.method == 'POST':
Now the same form , AnyForm , is "reloaded" (????) but with the parameter value "POST"
if request.method == 'POST':
form = AnyForm(request.POST)
Now if the form is valid (I have no idea what a "valid" or "invalid" form is)
if form.is_valid():
then all the values "captured" in the template by (???)
<label for="Choice1_text ">Choice 1</label>
{% render_field form.Choice1_text class="form-control" %}
(??????)
are written by the view (?????)
form.save
to the corresponding fields in the ModelForm (?????)
class Meta:
model = CC_Questions
fields = ['q_text', 'Choice1_text', 'Choice2_text','C1_Type','C2_Type']
The view then redirects and loads the home page in the browser
return redirect('/polls/p2')
Ok, So with the help of the references (mentioned below) and the workflow suggested by you, let us first see, the Django MVT workflow and then address the various questions asked in between the post.
WebForms with Django MVT in brief:
Prepare data of several different types for display in a form.
Render data as HTML.
Edit, enter data using a convenient interface.
Validate and clean up the data.
Return data to the server.
Save, delete or pass on for further processing.
The URL:
When a user requests a page from your Django-powered site, this is the algorithm the system follows to determine which Python code to execute. Which is handled by our views.py. From the frontend, if the request is not 'POST', then it is a GET request, hence the else part of the handling function in the views.py executes. This you have mentioned already.
The View: - Form data sent back to a Django website is processed by a view, generally, the same view which published the form. This allows us to reuse some of the same logic. To handle the form we need to instantiate it in the view for the URL where we want it to be published.
If we use request.POST, as in this line:
form = AnyForm(request.POST)
It transforms the form into a bound form (Form bound with data). Read more about it here.
Questioned By You (QBY) - When the submit button is pressed on the displayed page, this "passes back" (??) the {% render_field .. %} values to the view (?????)
So, yes, If the action attribute is not mentioned in the form then the data will be passed to the view responsible for displaying the form.
QBY - the view is executed again (????), but this time request method is set to "POST" because of the form method="POST" in the template (?????)
The button type submit, submits the form and make the request a POST request. The Django template sends that submitted data in the request.POST.
QBY - Now the same form, AnyForm, is "reloaded" (????) but with the parameter value "POST"
Here, if the return method at the end of the POST condition is HttpResponseRedirect it will redirect the user to the mentioned URL page, but if the same HTML is used to be rendered then the form will be displayed as a bound form. (It depends upon the requirements)
QBY - Now if the form is valid (I have no idea what a "valid" or "invalid" form is)
Form.is_valid()
The primary task of a Form object is to validate data. With a bound Form instance, call the is_valid() method to run validation and return a boolean designating whether the data was valid. If yes, then the data is being saved in the model.
QBY - then all the values "captured" in the template by (???)
All the values are sent to views in the request.POST. We can check it by
print(request.POST)
QBY - are written by the view (?????), form.save to the corresponding fields in the ModelForm (?????)
Save method is called on the Django ModelForm instance in order to save the data to the database. Calling save would run the validation check. A ValueError will be raised if the data in the form doesn't validate.
This saved data can now be processed further.
References:
[https://docs.djangoproject.com/en/4.0/topics/forms/][2]
[https://www.tangowithdjango.com/book/chapters/models_templates.html][3]
[https://docs.djangoproject.com/en/4.0/ref/forms/api/][4]
I am sure this is a silly question but I am trying to understand how forms work in django and I am confused about the action attribute in the template and the httpresponseredirect in views.py
I have the following template:
<form action="/play_outcomes/output/" method="post">
{% csrf_token %}
{{ form }}
<input type="submit" value="Submit" />
</form>
And the following view:
def getinput(request):
if request.method == 'POST':
form = get_data(request.POST)
if form.is_valid():
down = form.cleaned_data['get_down']
ytg = form.cleaned_data['get_ytg']
yfog = form.cleaned_data['get_yfog']
return HttpResponseRedirect('/thanks/')
else:
form = get_data()
return render(request, 'play_outcomes/getinput.html', {'form': form})
And finally in forms.py
class get_data(forms.Form):
get_down = forms.IntegerField(label = "Enter a down")
get_ytg = forms.IntegerField(label = "Enter yards to go")
get_yfog = forms.IntegerField(label = "Enter yards from own goal")
When I go to play_outcomes/getinput my form comes up. When I press 'submit' it directs shows the play_outcomes/output page, presumably because that is what is specified by the <form action variable.
Anyway, my question is what is the purpose of the return HttpResponseRedirect('/thanks/') after it has checked form.is_valid(). This is TRUE so it must execute this code. But this return seems redundant.
Please can someone enlighten me on what the return in the views.py is doing
Try replacing the form action with action="". Everything should continue working normally, since your view handles both GET requests (to show an empty form) and POST requests (to process when a form is submitted). When you press "Submit", your browser is sending the form data back to the exact same place it got the original web page from. Same URL, same view (getinput()), same template file (play_outcomes/getinput.html).
Normally, right before that HttpResponseRedirect() part, you'd include some logic to save the content of that form, email a user, do some calculation, etc. At that point, since the user is all done with that page, you typically redirect them to somewhere else on your site.
Edit: empty action
I have, all day, tried to figure this out but I can't really see where the problem is coming from.
I have a Django AuthenticationForm that seems to be submitting data somehow but not getting validated.
forms.py:
class LoginForm(AuthenticationForm):
username = forms.CharField(widget=forms.TextInput(attrs={'name': 'username'}))
password = forms.CharField(widget=forms.PasswordInput(attrs={'name': 'password'}))
views.py:
def index(request):
template = 'myapp/login.html'
if request.method == "POST":
print request.POST #prints QueryDict with its data
reg = LoginForm(request.POST or None)
if reg.is_valid():
return HttpResponse('Success: Form is valid!')
else:
return HttpResponse('Error: Form not valid')
loginform = LoginForm()
context = {'loginform':loginform}
return render(request, template, context)
HTML:
<form method="post" action=".">
{% csrf_token %}
<h2>Sign in</h2>
<p class="text-danger">{{loginform.errors}}</p>
{{ loginform.as_p }}
<button name = "signin" type="submit" value="0">Sign in</button>
</form>
The print request.POST in my views.py prints QueryDict: {u'username': [u'yax'], u'csrfmiddlewaretoken': [u'r8y1PaVNjxNWypdzP
MaFe1ZL7IkE1O7Hw0yRPQTSipW36z1g7X3vPS5qMMX56byj'], u'password': [u'sdfdsfsddfs']
, u'signin': [u'0']} but the reg.is_valid() keeps returning false.
EDIT:
I have also tried printing out reg.errors but it doesn't print out anything.
Try making the following change:
reg = LoginForm(data=request.POST)
AuthenticationForm is slightly different in that it needs the data argument.
Also note that you should test it with actually valid username and password combinations (that correspond to an existing user), since AuthenticationForm checks for that as well.
I made one html form in which auto suggestion option also in city field. I want to know that how transfer the values of html form to django admin.
till now i made a model of form where fields like name,city,phone number,sex which register in admin.py. by this i am not able to register directly by django admin.
If you already have your html form, you have to create your function in views.py.
In this file, you have to write the code which is going to receive the data you send from the html form.
Read the docs : Django forms
You probably have a html form like the following :
<form action="/your-name/" method="post">
<label for="your_name">Your name: </label>
<input id="your_name" type="text" name="your_name" value="{{ current_name }}">
<input type="submit" value="OK">
</form>
You can create a form in forms.py :
from django import forms
class NameForm(forms.Form):
your_name = forms.CharField(label='Your name', max_length=100)
Your function can look like this (in views.py)
from django.shortcuts import render
from django.http import HttpResponseRedirect
from .forms import NameForm
def get_name(request):
# if this is a POST request we need to process the form data
if request.method == 'POST':
# create a form instance and populate it with data from the request:
form = NameForm(request.POST)
# check whether it's valid:
if form.is_valid():
# process the data in form.cleaned_data as required
# ...
# redirect to a new URL:
return HttpResponseRedirect('/thanks/')
# if a GET (or any other method) we'll create a blank form
else:
form = NameForm()
return render(request, 'name.html', {'form': form})
I am trying to create a simple subscription form in the front page of my site. I created the view with a model form (the model contains only name and e-mail as attributes). When I go to the root address (GET) it works fine and loads the form. I then fill it with some data, click the submit button (the form action can either be set to '' or '/', the result is the same) and it redirects to the same root page, but it does not load anything, the page remains blank. In the console I can see it calling through POST method, but not even the first print of the view function gets printed.
Any ideas? I know it must be something silly, but I spent sometime in it and haven't yet found out what it could be.
In urls.py:
url(r'', FrontPage.as_view(template_name='rootsite/frontpage.html')),
In rootsite/views.py
class FrontPage(TemplateView):
'''
Front (index) page of the app, so that users can subscribe to
have create their own instance of the app
'''
template_name = 'rootsite/frontpage.html'
def get_context_data(self,
*args,
**kwargs):
c = {}
c.update(csrf(self.request))
print self.request.method
if self.request.method is 'POST':
print 'OK - POST IT IS, FINALLY'
form = NewUsersForm(self.request.POST)
print form.__dict__
if form.is_valid():
form.save()
return HttpResponseRedirect('/' + '?thanks=1')
else:
form = NewUsersForm()
return {'form':form}
You can't return a redirect from within get_context_data - it's for context data only, hence the name.
You should really be using a proper form view for this, which includes methods for redirecting after form validation.
Did you include a csrf_token in your template (as per the example here: http://www.djangobook.com/en/2.0/chapter07.html)?
<form action="" method="post">
<table>
{{ form.as_table }}
</table>
{% csrf_token %}
<input type="submit" value="Submit">
</form>
I could be wrong, but I thought Django wouldn't accept a POST request without a csrf token?