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")
Related
I have a form input inside my HTML page and want the figure ID that is entered inside the form to be passed into my django view to do a query, displaying info for the figure with the matching ID.
My form:
<form metohd="GET" id="figure_choice_form">
<label for="figure_id">Enter ID</label>
<input type="text" id="figure_id" name="figure_id">
<button> Submit </button>
</form>
My views.py
def from_DB(request):
#request being the ID entered from the form
figures_list = Figure.objects.filter(id=request)
context = {"figures_list":figures_list}
return render(request,"app1/data_from_DB.html",context)
Firstly , Update your html code snippet to correct form attribute "metohd" to "method" .
You are sending data via GET request . So you can access it via request.GET method .
Code snippet of django view.
def from_DB(request):
id = request.GET.get("figure_id")
figures_list = Figure.objects.filter(id=id)
context = {"figures_list":figures_list}
return render(request,"app1/data_from_DB.html",context)
This does your required work .
I tried looking at different resources on the internet regarding this request and request.data in django, but I couldn't fully understand it.
Why this request parameter is kept inside the function? What are we passing in this request parameter?? Also, what does this request. data do??
def index(request):
content = {
'Blogdata': Blog.objects.all(),
}
return render(request, 'index.html', content)
def somefunction (request):
data=request.data
As you can see I have two functions above both of them have request paramter inside the function. Also, I need the explanation on this request.data as this has to be used multiple times.
First, you should understand about HTTP Request(Header, Body). When you type in form and send to server, browser get data with name and add values into body request. In the backend server, we will get data from body with name.
Example:
I have form fill your name:
<form action="/signin" method="get" name="myForm">
<label for="name">Your name:</label>
<input type="text" id="name" name="name"><br><br>
<input type="button" value="Send form data!">
</form>
You type name : "Khoa", browser get values "Khoa" from input and add key:values with name in . Like this: "name": "Khoa"
In server django, you can get data with using request.data.get("name") = "Khoa"
request.data is body HTTP send to servere, "name" is key part of body have values is "Khoa"
So I have this simple polling application that continuously create polls that people can vote +1 or -1 on. However, since this website doesn't require user logins people can vote multiple of times on every poll.
<form name="poll" id='{{ item.id }}' method="post" action='/poll'>
<label class='lab-pos'>
<input type="radio" name="points" id='whine-pos' value=1>
<img class='img-pos'src="/static/item-pos.png">
</label>
<label class='lab-neg'>
<input type="radio" name="points" id='whine-neg' value=-1>
<img class='img-neg'src="/static/item-neg.png">
</label>
</form>
I am sending the submit with a javascript function to my sqlite3 database, there is no submit button but the script.
<script type="text/javascript">
$(document).ready(function() {
$('input[type=radio]').on('change', function() {
$(this).closest("form").submit();
});
});
</script>
Is it possible to save the the votes in cookies with flask so when a person visits the site again they will not be able to vote again but only change the vote? (if they want). I know they can just clear cookies and they can vote again but that doesn't really bothers me in this phase.
My database structure in SQLAlchemy looks like this at the moment, and my view in flask like below
class pollingresult(db.Model):
__tablename__ = "pollingresult"
id = db.Column('id', db.Integer, primary_key=True)
poll = db.Column('poll', db.Integer)
cookie = db.Column('cookie', db.String(255))
feed_id = db.Column('feed_id', db.Integer)
def __init__(self, poll):
self.poll = poll
and my view in flask like below
#app.route('/poll', methods=['GET', 'POST'])
def poll():
polltodb = pollingresult(request.form['points'])
session['points_session'] = request.form['points']
db.session.add(polltodb)
db.session.commit()
return ('',204)
I have played around with the session but it seems that on refresh the polls are still getting 'rested' so people can vote again.
edit 161202:
So, I am still struggling with this task, I can save the session['points_session'] to a session, but I need to save the session more like a dict, where the dict has id = item.id and points = points so I can prefill the forms with javascript 'if id = 1 and point = 1' prefill form with id = 1. I also need to prevent the form to be submitted again based on the session, so I guess i will have to create a somewhat dummy token for some kind of session key?
edit 161207:
So I would like to send the poll_id along with the form submit so I thought I could use an ajax post request, however, this throws the error "Failed to decode JSON object: No JSON object could be decoded".
<script type="text/javascript">
$(document).ready(function() {
$('input[type=radio]').on('change', function() {
$(this).closest("form").submit();
var poll_id = $(this).closest("div").attr("id");
var data = {poll_id};
console.log(JSON.stringify(data));
$.post('/poll', {
data: JSON.stringify(data),
}, function(data) {
console.log(data);
});
});
});
</script>
Along with the new poll route:
#app.route('/poll', methods=['GET', 'POST'])
def poll():
polltodb = pollingresult(request.form['points'])
session['points_session'] = request.form['points']
db.session.add(polltodb)
db.session.commit()
data = request.get_json(force=True)
print data
return ('',204)
This will later be inserted into the DB along with some kind of session key.
Instead of saving the polls the user has voted on in their session, simply attach a "poll_user_id" to the session so you can keep track of the user and their votes in the database.
from uuid import uuid1 as uuid
if "poll_user_id" not in session:
session["poll_user_id"] = uuid()
Here's some psuedo code as I'm not familar with Flask and their database engine.
old_vote = query(poll_user_id=session["poll_user_id"], poll=poll_id)
if not old_vote:
insert(poll_user_id=session["poll_user_id"], poll=poll_id, choice=form.choice)
else:
update(poll_user_id=session["poll_user_id"], poll=poll_id, choice=form.choice)
Now, when a user votes, either new or as an update, it checks if a vote already exists with the same "poll_user_id" value, if it does you'll do an update. If it doesn't do an insert.
i would suggest you don't use cookies at all. I use browser fingerprinting to identify users. The advantage is that you can id them even if they open the page in incognito again and again (which would clear all your cookies / sessions)
https://clientjs.org/#Fingerprints
You would be better off generating a (admittedly semi-unique) fingerprint and tracking duplicate user's this way.
I have been using this with good success and i have a link where the user can flag that he has not completed the vote/action and i have not
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>
I have a question regarding Django Forms and GET
I have a form to download student scores in CSV format. The fields are name and year so I have a forms.py
StudentDownloadForm(forms.Form):
name=forms.CharField()
year = forms.CharField()
And I want to use this form in the template.html with
context={'student_form' : StudentDownloadForm(),}
<form action ="" method="GET">
{% csrf_token %}{{ student_form|crispy }}
<input type="submit" value="Query"/>
</form>
So my questions are as follows:
If I use the method="GET" then the csrf token is visible in the URL, which is a security issue
Can I then use the method="POST" instead?
Alternatively, can I remove the csrf token in the form?
According to Django documentation (Cross Site Request Forgery protection):
For all incoming requests that are not using HTTP GET, HEAD, OPTIONS
or TRACE, a CSRF cookie must be present, and the ‘csrfmiddlewaretoken’
field must be present and correct. If it isn’t, the user will get a
403 error.
And:
It deliberately ignores GET requests (and other requests that are
defined as ‘safe’ by RFC 2616). These requests ought never to have any
potentially dangerous side effects , and so a CSRF attack with a GET
request ought to be harmless. RFC 2616 defines POST, PUT and DELETE as
‘unsafe’, and all other methods are assumed to be unsafe, for maximum
protection.
So, you can omit CSRF token for GET requiests