I am new in django and have faced a strange problem. If I create django template and insert {% csrf_token %} inside, it works well, but if I put #csrf_protect decorator to view, it gives me Forbidden 403 (CSRF verification failed. Request aborted). As I understand from django documentation, I can`t use both CsrfViewMiddleware and #csrf_protect.
So here is the question:
Is it possible to do csrf verification inside a view or should I always write templates in such situations?
#csrf_protect
def create_group(request):
if request.method == "POST":
Group.objects.create(**{key: request.POST[key] for key in request.POST})
return HttpResponseRedirect(reverse("groups:groups"))
elif request.method == "GET":
pass
return HttpResponse(create_group_form)
create_group_form = """
<form method="POST">
<label for="course">Course:</label><br>
<input type="text" id="course" name="course"><br><br>
<label for="length_in_months">Length in months:</label><br>
<input type="number" id="length_in_months" name="length_in_months" min=1 max=12 required><br><br>
<label for="price">Price:</label><br>
<input type="number" id="price" name="price" min=1000 max=50000 required><br><br>
<label for="number_of_students">Number of students:</label><br>
<input type="number" id="number_of_students" name="number_of_students" min=3 max=30 required><br><br>
<label for="lesson_duration">Lesson duration:</label><br>
<input type="number" id="lesson_duration" name="lesson_duration" min=1 max=5 required><br><br>
<label for="website">Website:</label><br>
<input type="url" id="website" name="website"><br><br>
<input type="submit" value="Submit">
</form> """
It's better to use CsrfViewMiddleware as an overall protection. If you forget to add the decorator to your views, it'll create security issues. You must use it on the views that assign CSRF tokens to the output and the ones that accept data from the POST form. So, as the best practice its better to use CSRF token in template unless we have a specific requirement.
Related
I'm creating a password generator app. The app is working and stores the value on db.
The problem is whenever I refresh, the form resubmits and takes the previous value and stores.
Also, I want to show the email and password on the same page.
Whenever I refresh, I want to show an empty form with empty fields.
Views.py
def home(request):
if request.method=='POST':
inputemail = request.POST.get('InputEmail')
gen = ''.join(random.choices((string.ascii_uppercase+string.ascii_lowercase+string.digits+string.punctuation), k=10))
newpass = Item(email=inputemail,encrypt=gen)
newpass.save()
return render(request,'home.html',{"gen":gen})
return render(request,'home.html',{})
Home.html
<form method = 'post' id='pass-form' >
{% csrf_token %}
<div class="mb-3">
<label for="exampleInputEmail1" class="form-label">Email address</label>
<input type="email" class="form-control" name="InputEmail" >
<div id="emailHelp" class="form-text">We'll never share your email with anyone else.</div>
</div>
<button type="submit" name = "submit" class="btn btn-primary">Generate Password</button><br><br>
</form>
<div class="mb-3">
<label for="exampleInputPassword1" class="form-label">Generated Password</label>
<input type="text" id="InputPassword" name = "genpassword" value = {{gen}} >
</div>
Urls.py
urlpatterns = [
path('',views.home,name='home'),
]
According to docs:
you should always return an HttpResponseRedirect after successfully dealing with POST data. This tip isn’t specific to Django; it’s good web development practice in general.
So you should make another page to show generated password, which will take submitted instance id of Item model created in home view so:
def home(request):
if request.method=='POST':
inputemail = request.POST.get('InputEmail')
gen = ''.join(random.choices((string.ascii_uppercase+string.ascii_lowercase+string.digits+string.punctuation), k=10))
newpass = Item(email=inputemail,encrypt=gen)
newpass.save()
return redirect('success', args=(newpass.pk))
return render(request,'home.html',{})
def success(request, pk):
item_obj = get_object_or_404(Item, pk=pk)
return render(request,'success.html', {'gen':item_obj.encrypt})
urls.py
urlpatterns=[
path('',views.home,name='home'),
path('success/<int:pk>/',views.success,name='success')
]
success.html
<body>
<h2>The form is successfully submitted.</h2>
<br>
<div class="mb-3">
<label for="exampleInputPassword1" class="form-label">Generated Password</label>
<input type="text" id="InputPassword" name="genpassword" value={{gen}} >
</div>
Again go to password generator page.
</body>
Another possible solution
You can make email field required in Html form and then hard refresh the page after submitting the form using Javascript's submit event so the template:
<form method='POST' id='pass-form'>
{% csrf_token %}
<div class="mb-3">
<label for="exampleInputEmail1" class="form-label">Email address</label>
<input type="email" class="form-control" name="InputEmail" required>
<div id="emailHelp" class="form-text">We'll never share your email with anyone else.</div>
</div>
<button type="submit" class="btn btn-primary">Generate Password</button><br><br>
</form>
<div class="mb-3">
<label for="exampleInputPassword1" class="form-label">Generated Password</label>
<input type="text" id="InputPassword" name = "genpassword" value ={{gen}} >
</div>
<script type='text/javascript'>
let form = document.getElementById('pass-form');
addEventListener('submit', (event) => {
location.reload(true); // hard refreshed
console.log('hard refreshed')
});
</script>
Note: Then also there are certain browsers like Microsoft Edge which gives pop up as Resubmit the form? in which they mention The page you're looking for used information that you entered. Returning to the page might trigger a repitition of any action you took there. Do you want to continue?
The moment you click on continue it creates duplicacy of records, so I think as docs mentions the first approach is better.
i would like to pass a value to a form through a get request. It can be done using wtforms but i'm not using that so i'd to know how to do it with request.form['test'] instead of form.test.data. Below is how it would work with wtform. I'd like to replicate that with a normal form.
if request.method == 'POST':
current_user.firstname= form.firstname.data
elif request.method == 'GET':
form.firstname.data= current_user.firstname
in other words, i'm making a page to update a user's data. I'd like to display what's in the database on the input form. Then when they edit it and hit update, whatever's on the form field will be updated in the DB. I'm not using WTForms. I hope there's a way to do this without it.
The form looks like this
<form method='post' action="/action_page">
First name:<br>
<input type="text" name="firstname" value="Mickey">
<br>
Last name:<br>
<input type="text" name="lastname" value="Mouse">
<br><br>
<input type="submit" value="Submit">
</form>
you can pass values to the form by passing parameters to the render_template method just like this
firstname= " ah"
lastname= " 1233"
return render_template("form.html", name=firstname, last=lastname)
then in HTML you can use jinja to put these values in the form
<form method='post' action="/action_page">
First name:<br>
<input type="text" name="firstname" value={{name}}>
<br>
Last name:<br>
<input type="text" name="lastname" value={{last}}>
<br><br>
<input type="submit" value="Submit">
</form>
I Want to process my form data and view and update my mongo DB if name not present else if name present update the DB.
Before that, I want to make sure if the entire subject is proper.. using some regex.
But when I add name and subject in below form code nothing is happening.
How do I get the form data in view and do the proper check?
And where should I do the input validation in views or form.html ??
if entered data is not proper format throw error? how can I do this?
form_template.html
<div class="container">
<div class="col-md-5">
<div class="form-area">
<form id="add-form" method="POST" action="{% url 'myapp:add_data' %}" role="form">
<br style="clear:both">
<h4 style="margin-bottom: 25px; text-align: center;"> New Data</h3>
<div class="form-group">
<input type="text" class="form-control" id="name" name="name" placeholder="Name" required>
</div>
<div class="form-group">
<input type="text" class="form-control" id="sub_list" name="sub_list" placeholder="Sub List comma separated" required>
</div>
<button type="button" id="submit" name="submit" class="btn btn-primary pull-right">Submit Form</button>
</form>
</div>
</div>
</div>
</div>
myapp/url.py
url(r'^new_data$', views.add_data, name='add_data'),
And below is my view function
def add_data(request):
print "Herfe"
if request.POST:
print "coming here"
I want validate user entered input is proper
messages.success(request, 'Form submission successful')
return render(request, '/index.html')
you can use request.POST.get(<name>) to get the data from the template and then preprocess it.
So in your case it simply becomes
def add_data(request):
print "Herfe"
if request.POST:
print "coming here"
data = request.POST.get('name')
# now validate to your heart's content
messages.success(request, 'Form submission successful')
return render(request, '/index.html')
On a side note I think you are missing out the benefits of forms in django. If you use the forms API properly you can easily pass values and validate them with inbuilt methods. In that case it would become
form = YourForm(request.POST)
if form.is_valid():
form.save()
your_variable = form.cleaned_data['name'] # validates for malicious data automatically
# Now you can do further validations accordingly.
I am new to Django. I needed to code the login feature to redirect to originally requested URL after login. I looked around and found the following on a stackoverflow thread.
django redirect after login not working "next" not posting?
I see the following in the Django documention
login_required() does the following:
If the user isn’t logged in, redirect to settings.LOGIN_URL, passing
the current absolute path in the query string. Example:
/accounts/login/?next=/polls/3/.
I also read that the login() method of django.contrib.auth package passes next to the template. So I defined the login() function in my view (I cannot use the one from the 'auth' package since I need some customization).
def login(request):
next = request.GET['next']
return render(request, 'user/login.html', {'next': next})
In my template
<form method="post" action="{% url 'user:login_user' %}" >
{% csrf_token %}
<p><label for="id_username">Username:</label> <input autofocus="" id="id_username" maxlength="254" name="username" type="text" required /></p>
<p><label for="id_password">Password:</label> <input id="id_password" name="password" type="password" required /></p>
<input type="hidden" name="next" value="{{next}}" />
<button type="submit">Login</button>
</form>
In my login_user() function, if I see that the value of next is present, I redirect to that URL once I have logged the user in. This works for me fine.
However, solution presented in the thread above was a bit more involved. I am curious as to why is it necessary to do all that. What am missing here?
Thanks
try changing your
<button type="submit">Login</button>
to
<input type="submit" value="Login" />
I have two completely different forms in one template. How to process them in one view? How can I distinguish which of the forms was submitted? How can I use prefix to acomplish that? Or maybe it's better to write separate views?
regards
chriss
Personally, I'd use one view to handle each form's POST.
On the other hand, you could use a hidden input element that indicate which form was used
<form action="/blog/" method="POST">
{{ blog_form.as_p }}
<input type="hidden" name="form-type" value"blog-form" /> <!-- set type -->
<input type="submit" value="Submit" />
</form>
...
<form action="/blog/" method="POST">
{{ micro_form.as_p }}
<input type="hidden" name="form-type" value"micro-form" /> <!-- set type -->
<input type="submit" value="Submit" />
</form>
With a view like:
def blog(request):
if request.method == 'POST':
if request.POST['form-type'] == u"blog-form": # test the form type
form = BlogForm(request.POST)
...
else:
form = MicroForm(request.POST)
...
return render_to_response('blog.html', {
'blog_form': BlogForm(),
'micro_form': MicroForm(),
})
... but once again, I think one view per form (even if the view only accepts POSTs) is simpler than trying to do the above.
like ayaz said, you should give unique name to form submit button
<form action="." method="post">
......
<input type="submit" name="form1">
</form>
<form action="." method="post">
......
<input type="submit" name="form2">
</form>
#view
if "form1" in request.POST:
...
if "form2" in request.POST:
...
If the two forms are completely different, it will certainly not hurt to have them be handled by two different views. Otherwise, you may use the 'hidden input element' trick zacherates has touched upon. Or, you could always give each submit element a unique name, and differentiate in the view which form was submitted based on that.