Post to Django's definition from Advanced Rest Client - python

Posting values from Advanced Rest client to Django's definition returns "Forbidden(403)" alert
looks like CSRF token is missing in the header, What can be done to get rid of this issue? Below is my definition to receive the POST values
def saveToDb(request):
c = {}
c.update(csrf(request))
if request.method == 'POST':
form = RegisterForm(request.POST)
if form.is_valid():
form_unique_id = form.cleaned_data['form_id']
form_meta_data = form.cleaned_data['form_content']
meta_data = FormMetaData.objects.create(
form_id=form_unique_id,
form_content=form_meta_data
)
meta_data.save()
result = FormMetaData.objects.all()
return render(request, "form_saved.html", {'result': result})
There is no issue in the definition as it works well with form input

Post to Django From Advanced Rest Client with CSRF Token:
Set CSRF Token for the key "X-CSRFToken" in the Header Section, add the key-value pairs in the body section, Select the Content type as "application/x-www-form-urlencoded" and click the Send Button
Post to Django from Advanced Rest Client without CSRF Token: Add the key-value pairs in the body section, Select the Content type as "application/x-www-form-urlencoded" and click the Send Button.
Note:
Please make sure to set "#csrf_exempt" for the definition to which you post values
as shown below

You have to give {% csrf_token %} in your html;
<html>
<form method="post">
{% csrf_token %}
</form>
</html>

Related

FLask : submitting different actions with a dynamically generated form

I am building a basic Flask application with a sqlite database.
The feature I am having trouble implementing is for a user to be able to accept or refuse a contact request (like a friend request) from another user.
On the "/contacts" page, you can see all the contact requests you received from other users without problem, but I want to add the option to accept or refuse them and this is where I'm stuck.
A form is dynamically generated by fetching from the database all the contact requests the current user has received and displaying them on the page.
I have tried using two <input type="submit" name="accept/delete" value="id_of_the_request"> tags for each requests, one with the accept option, the other with the delete option, both leading to the same route, but unlike some other input types, the "value" property controls what text appears on the button, so I can't set that to, say, the id of the contact request (which I did in the code further below) because then I see two buttons with a number on my page.
I thought about doing the opposite and setting the name of the tag to the request's id instead, and the value to "delete" or "accept", but then on the server side I wouldn't know what name to get with request.form.get() since the request's id are dynamically generated in the form depending on what's in the database.
I feel like I'm missing some basic knowledge and that it shouldn't be too hard to do that though.
Here is my html code (the template is passed a list of dictionaries (requests) from the database, corresponding to the list of contact requests received by the current user. Each request consists of 3 columns : request_id, user_email, contact_email. request_id is the primary key, user_email is the email of the person who sent the request, while contact_email is the email of the person who received it. ):
<form action="/manage_requests" method="post">
<ul>
{% for request in requests %}
<li>{{request.user_email}} sent you a contact request.</li>
<input type="submit" name="accept" value="{{request.r_id}}">
<input type="submit" name="refuse" value="{{request.r_id}}">
{% endfor %}
</ul>
</form>
Here is my python code to handle accepting or refusing the request :
#app.route("/manage_requests", methods = ["POST"])
#login_required
def manage_requests():
acceptedID = int(request.form.get("accept"))
refusedID = int(request.form.get("refuse"))
## Add the user who sent the request as a contact for both them and us, then delete the request.
if acceptedID :
# fetch the info of the request corresponding id from the database requests table
# get the sender's user_email
# insert the data into the database contacts table for both the sender and the receiver (current user)
# delete the request from the requests table in the database
return redirect("/contacts")
## Delete the request
elif refusedID :
# delete the request from the database requests table
return redirect("/contacts")
This is how I would have done it:
First a route that returns all contact requests like you did:
#app.route("/contacts", methods = ["POST"])
#login_required
def contacts():
# requests_list = Find all the requests for a user
return render_template('contacts.html', requests_list=requests_list)
Then I return in my template all the requests found:
<ul>
{% for request in requests %}
<li>{{request.user_email}} sent you a contact request.</li>
<a href="{{ url_for('manage_requests', request_id=request.r_id, action='accept' )}}">
<input type="submit" name="accept">
</a>
<a href="{{ url_for('manage_requests', request_id=request.r_id, action='refuse' )}}">
<input type="submit" name="refuse">
</a>
{% endfor %}
</ul>
Note how I use the <a></a> tag around the submit button, with the Jinja2 url_for function passed to the href attribute, with the request_id as a parameter and a variable action which takes as the value either accept or refuse:
And finally:
#app.route("/manage_requests", methods = ["POST"])
#login_required
def manage_requests():
action = request.args.get('action')
request_id = int(request.args.get('request_id'))
## Add the user who sent the request as a contact for both them and us, then delete the request.
if action == "accept" :
# fetch the info of the request corresponding id from the database requests table
# get the sender's user_email
# insert the data into the database contacts table for both the sender and the receiver (current user)
# delete the request from the requests table in the database
return redirect("/contacts")
## Delete the request
else :
# delete the request from the database requests table
return redirect("/contacts")

What is the cause of the Bad Request Error when submitting form in Flask application?

After reading many similar sounding problems and the relevant Flask docs, I cannot seem to figure out what is generating the following error upon submitting a form:
400 Bad Request
The browser (or proxy) sent a request that this server could not understand.
While the form always displays properly, the bad request happens when I submit an HTML form that ties to either of these functions:
#app.route('/app/business', methods=['GET', 'POST'])
def apply_business():
if request.method == 'POST':
new_account = Business(name=request.form['name_field'], email=request.form['email_field'], account_type="business",
q1=request.form['q1_field'], q2=request.form['q2_field'], q3=request.form['q3_field'], q4=request.form['q4_field'],
q5=request.form['q5_field'], q6=request.form['q6_field'], q7=request.form['q7_field'],
account_status="pending", time=datetime.datetime.utcnow())
db.session.add(new_account)
db.session.commit()
session['name'] = request.form['name_field']
return redirect(url_for('success'))
return render_template('application.html', accounttype="business")
#app.route('/app/student', methods=['GET', 'POST'])
def apply_student():
if request.method == 'POST':
new_account = Student(name=request.form['name_field'], email=request.form['email_field'], account_type="student",
q1=request.form['q1_field'], q2=request.form['q2_field'], q3=request.form['q3_field'], q4=request.form['q4_field'],
q5=request.form['q5_field'], q6=request.form['q6_field'], q7=request.form['q7_field'], q8=request.form['q8_field'],
q9=request.form['q9_field'], q10=request.form['q10_field'],
account_status="pending", time=datetime.datetime.utcnow())
db.session.add(new_account)
db.session.commit()
session['name'] = request.form['name_field']
return redirect(url_for('success'))
return render_template('application.html', accounttype="student")
The relevant part of HTML is
<html>
<head>
<title>apply</title>
</head>
<body>
{% if accounttype=="business" %}
<form action="{{ url_for('apply_business') }}" method=post class="application_form">
{% elif accounttype=="student" %}
<form action="{{ url_for('apply_student') }}" method=post class="application_form">
{% endif %}
<p>Full Name:</p>
<input name="name_field" placeholder="First and Last">
<p>Email Address:</p>
<input name="email_field" placeholder="your#email.com">
...
The problem for most people was not calling GET or POST, but I am doing just that in both functions, and I double checked to make sure I imported everything necessary, such as from flask import request. I also queried the database and confirmed that the additions from the form weren't added.
In the Flask app, I was requesting form fields that were labeled slightly different in the HTML form. Keeping the names consistent is a must. More can be read at this question Form sending error, Flask
The solution was simple and uncovered in the comments. As addressed in this question, Form sending error, Flask, and pointed out by Sean Vieira,
...the issue is that Flask raises an HTTP error when it fails to find a
key in the args and form dictionaries. What Flask assumes by default
is that if you are asking for a particular key and it's not there then
something got left out of the request and the entire request is
invalid.
In other words, if only one form element that you request in Python cannot be found in HTML, then the POST request is not valid and the error appears, in my case without any irregularities in the traceback. For me, it was a lack of consistency with spelling: in the HTML, I labeled various form inputs
<input name="question1_field" placeholder="question one">
while in Python, when there was a POST called, I grab a nonexistent form with
request.form['question1']
whereas, to be consistent with my HTML form names, it needed to be
request.form['question1_field']

Do we need CSRF verification for every POST request?

I am just building a simple HTML form with POST method and unfortunately I am finding CSRF verification error.
This is just a simple html form using POST method on localhost. There are no cross sites involved. I could definitely fix it by using csrf_token but I still don't understand why django is asking me for that..
There are no re-directions/ iframes involved here...
So, why this is happening?? is this normal to all ??
# Also tried using RequestContext(request) but there isn't any change in the error
#settings.py
'django.middleware.csrf.CsrfViewMiddleware' in MIDDLEWARE_CLASSES
#views.py
# url for home page is "" i.e, http://127.0.0.1:8000/
def HomePage (request):
if request.method == "POST":
form = myForm(request.POST)
if form.is_valid():
data = form.cleaned_data
context = { "myForm" : myForm(choices),
"values" : data,
}
return render_to_response("home.html", context)
else:
form = myForm(choices)
context = {"myForm" : form}
return render_to_response("home.html", context)
# home.html
<div id="pingmeeForm">
<form action="" method="post">
<table>
{{myForm.as_table}}
</table>
<input name="enter" type="submit" value="enter"/>
</form>
{{values}}
</div>
# forms.py
class myForm (forms.Form):
def __init__(self, my_choices,*args, **kwargs):
super(myForm, self).__init__(*args, **kwargs)
self.fields['Friends'] = forms.ChoiceField(choices=my_choices)
message = forms.CharField()
If you do a post request, you typically change the state of the server. If you change the state of the server, you don't want to allow other sites to do so. To protect against other sites issueing post-requests on your server, you add csrf protection. Therefore the solution should (imho) never be to remove the Csrf protection. Depending on the situation, either of the following two is the case:
Your post request does not change the state. In that case, make it a get request.
Your post request changes the state. You need CSRF.
The error message you got but didn't show explains exactly what you are doing wrong: you should ensure that
the view function uses RequestContext
for the template, instead of Context.

How do I confirm a form has been submitted with django?

I'm submitting a form and instead of redirecting to a success url I would like to just show "Form has been submitted" in text on the page when the form has been submitted. Does anyone know how I can do so?
In your view:
if request.POST:
# validate form, do what you need
if form_is_valid():
message = 'Form has been submitted'
return render_to_response('path/to/template.html', {'message': message})
And then use code in your template like:
{% if message %}
<h4>{{ message }}</h4>
{% endif %}
Honestly, this isn't a Django-specific issue. The problem is whether you are doing a normal form submission or using AJAX.
The basic idea is to POST to your form submission endpoint using AJAX and the form data, and in the Django view, merely update your models and return either an empty 200 response or some data (in XML, JSON, small HTML, whatever you need). Then the AJAX call can populate a success message div on success, or display a failure message if it gets back a non-200 response.
Modify your view to return an HttpResponse object with the text you want as its parameter, after you have validated the request. See the example below.
from django.http import HttpResponse
def contact(request):
if request.method == 'POST': # If the form has been submitted...
form = ContactForm(request.POST) # A form bound to the POST data
if form.is_valid(): # All validation rules pass
# Process the data in form.cleaned_data
# ...
return HttpResponse('Form has been submitted.')

POSTing forms in Django's admin interface

I'm writing a Django admin action to mass e-mail contacts. The action is defined as follows:
def email_selected(self,request,queryset):
rep_list = []
for each in queryset:
reps = CorporatePerson.objects.filter(company_id = Company.objects.get(name=each.name))
contact_reps = reps.filter(is_contact=True)
for rep in contact_reps:
rep_list.append(rep)
return email_form(request,queryset,rep_list)
email_form exists as a view and fills a template with this code:
def email_form(request,queryset,rep_list):
if request.method == 'POST':
form = EmailForm(request.POST)
if form.is_valid():
cd = form.cleaned_data
send_mail(
cd['subject'],
cd['message'],
cd.get('email','noreply#localboast'),['redacted#email.com'],
)
return HttpResponseRedirect('thanks')
else:
form = EmailForm()
return render_to_response('corpware/admin/email-form.html',{'form':form,})
and the template exists as follows:
<body>
<form action="/process_mail/" method="post">
<table>
{{ form.as_table }}
</table>
<input type = "submit" value = "Submit">
</form>
</body>
/process_mail/ is hardlinked to another view in urls.py - which is a problem. I'd really like it so that I don't have to use <form action="/process_mail/" method="post"> but unfortunately I can't seem to POST the user inputs to the view handler without the admin interface for the model being reloaded in it's place (When I hit the submit button with , the administration interface appears, which I don't want.)
Is there a way that I could make the form POST to itself (<form action="" method="post">) so that I can handle inputs received in email_form? Trying to handle inputs with extraneous URLs and unneeded functions bothers me, as I'm hardcoding URLs to work with the code.
You can use django's inbuilt url tag to avoid hardcoding links. see...
http://docs.djangoproject.com/en/dev/ref/templates/builtins/#url
Chances are you'd be better off setting up a mass mailer to be triggered off by a cron job rather than on the post.
Check out the answer I posted here
Django scheduled jobs
Also if you insist on triggering the email_send function on a view update perhaps look at
http://docs.djangoproject.com/en/dev/topics/signals/

Categories