Python Flask Button to get Unique values - python

I am trying to get a list sets of information :
Name:
Date:
Status:
It will be displayed in the HTML page so e.g
Name: John
Date: 11 January 2018
Status: Pending
Name: Alex
Date: 10 January 2018
Status: Pending
There will be a button for each of the above data named 'Confirm'.
How do I go about so that when I click the 'Confirm' button on one of the data above, it will only process and make use of that data?
e.g if I click the button on Name : Alex, it will only get the info of Alex.
How do I go about doing this in Python Flask?
I can do a POST but how do I capture that the data being sent is from that specific set?
Please give me some suggestions.
Thank you

Use Identities
Information that is stored on a computer has to be identified through a means somehow. This can be done with an index such as an incrementing index, a GUID, a SHA hash or some other kind of random generator that can create an random id. The random id can then be given a 'purpose' to represent that data.
I'm not sure how you have implemented this in Flask. Whether this is done with
some kind of SQL driver, a JSON/XML object, or whether this is written in plain text. However, the place to start is how you're storing your data so that you can figure out the best way to use it.
Use of Payloads
Payloads are basically where you can give or send all data rather than metadata or identities. This could allow you to identify or change the information before it is stored or sent. However this is normally done to upload/download and change data with an API rather than used to identify data as it would take longer to process.
Solution
The best one out of the two would be to use identification. The implementation could be as simple as visiting a link that contains an ID that (REST) API's would typically use (e.g. /statuses/1 or /statuses/321THISISUNIQUE).
You could create a form that contains that id as part of a value such as:
<form action='/statuses' method='POST'>
<input type='hidden' name='id' value='321THISISUNIQUE'/>
<input class='confirm' type='submit' value='confirm'/>
</form>
Example to get a status using an id
#app.route('/statuses/<id>')
def getStatus(id):
return 'Status for %s' % id
Example to use POST with an id (Refer to Flask - the request object)
#app.route('/statuses', methods=['POST'])
def changeStatus():
# Technically this should be a PUT request as part of REST
id = request.args.id
return 'Change status with id %s' % id

Using flask-sqlalchemy would make it easy for you. Then you can retreive all data from database, and when user click the button you will call a view function which will take Name or you can change to the person id.
db = SQLAlchemy(app)
class Person(db.Model):
Name = db.Column(db.String)
Date = db.Column(db.DateTime)
Status = db.Column(db.String)
#app.route('/')
def index():
persons = Person.query.all()
return render_template('index.html', persons=persons)
#app.route('person-info/<name>')
def person_info(name):
person = Person.query.filter_by(Name=name).first()
return render_template('person.html', person=person)
#index.html
{% for person in persons %}
...
<a href="{{ url_for('person_info', name=person.Name)}}">
{% endfor %}
#person.html
{{person.Name}}{{person.Status}}{{person.Date}}

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")

WTForm used in two different views validate only using one view

I'm having a problem using a WTForm in two different views. One view creates a new item, using the form data. The other view display information for items already in the database and the user should be able to update the form data from there.
I can create and view the items in the database. The problem is updating the information when in in the display view. The steps I take are something like:
Create a item in /create. Before saving to database check that the same ID is not already in database. If exists show a flash and do not permit saving the item. Reloading /create
To display information about a existing item the route /display/<item> call the database, populate the form and show the information using the same form.
Desired behavior
To update information when in /display/<item>. After updating any field data and pressing the submit button, the form should save to the database and reload /display/<item>.
Actual behavior
After pressing the submit button the form takes me back to the /create route, and performs the ID validation.
I don't understand why, being in a different view /display/<item>, the form's submit button takes me to the /create view.
The code goes something like this:
This is a simple form
class ItemForm(FlaskForm):
id = IntegerField("id", validators=[DataRequired()])
name = StringField("Email", validators=[DataRequired()])
submit = SubmitField("Submit")
With this I create a new item in the database, checking that the same Id is not already in the database:
#app.route("/create", methods=["GET", "POST"])
def create():
form = ItemForm()
if form.validate_on_submit():
item = item = Item.query.filter(Item.id == form.id).first()
# Check the same id is not already created
if item is not None:
flash('item already in database')
return redirect(url_for("create"))
item = Item()
form.populate_obj(item)
db.session.add(item)
db.session.commit()
return redirect(url_for("display", item = form.id))
else:
return render_template("create.html", form=form)
And then after there is created items in the database i can call a display route. In this route I should be able to update the fields in the form and save it to the database. Whit out validating as the the same ID is already present in the database (I'm updating, no creating a new one).
#app.route("/display/<item>", methods=["GET", "POST"])
def display(item):
item = Item.query.filter(Item.id == item).first()
form = ItemForm(obj=item)
if form.validate_on_submit():
form.populate_obj(item)
db.session.add(item)
db.session.commit()
return redirect(url_for("display", item=form.id))
else:
return render_template("display.html", form=form)
The problem is that when in the /display/<item> route, the submit button takes me back to the /create route. Performing the validations in there. Of course the item is already created in the database so I get the flash and I'm redirected to /create, when it should reload /display/<item>.
Could someone point me in the right direction, please. Thank you all!
My mistake, but #the_gañañufla asked the right question. The problems of coding alone.
I had added an action to the HTML and i forgot to remove it.
I had
<form id="form_item" action="{{ url_for('create') }}" class="form" method=post>
After correct it I have:
<form id="form_product" action="" class="form" method=post>

Grab and manipulate user data without models

I'm not sure if this is even possible, but I would like to grab a user's input, pull it into my views.py, manipulate it, and then use that data in other views.
I do not need this data stored in a database, as I won't be referencing it again, and I want to keep this as lightweight as possible.
Currently, I'm trying to pull data from espn's fantasy football site using the python library espnff. My homepage consists of a textfield box and a submit button (Think of google.com).
I have functions set up that will comb through an espn url such as http://games.espn.com/ffl/clubhouse?leagueId=123456 to grab the leagueID, from there I make use of espnff to grab more info on that league.
My ideal use case is someone comes to my site, copies and pastes their league url like the one above, clicks submit and then brings them to https://example.com/{{ leagueID}/ which will display different info that I gather.
I have not found a way to do this without submitting the user input to a model. Is possible to avoid using the database? If so how?
Not sure I understood it right, but what you are trying to do can easily be done without using any models/database or any other kind of persistent storage.
The user submits that information using the form, you grab the URL from the request object in your view, parse the URL to get the league_id and then redirect the user to /{league_id}.
Then on that view, you gather the league_id parameter (from the url), use the library (espnff) to fetch the data with that id and then render the template with that data.
For example, the implementation would be something in these lines:
Make a form in your html template:
<form method="post" action="/">
{% csrf_token %}
<input type="text" name="league_url"/>
<input type="submit" value="Submit" />
</form>
in urls.py:
url(r'^$', index_view, name="index"),
url(r'^(?P<league_id>[0-9]+)$', league_view, name="league_view")
in views.py:
def index_view(request):
if request.method == 'POST':
league_url = request.POST.get('league_url', None)
# Your code to parse the URL and extract the ID
return HttpResponseRedirect('/{}'.format(league_id))
else:
# render form template
def league_view(request, league_id):
# your code here using the league_id
# and render the page with data
(I didn't tested that code, I just wrote it quickly as an example of the flow)
The django documentation describes quite extensively how to do caching with django. You can find the documentation on how to set that up here
Once it's been set up you simply use the cache in the following way
from django.core.cache import cache
cache.set('my_key', 'my_value', 60) # number is in seconds
value = cache.get('my_key')
You can provide dictionaries and such as values. The caching framework will serialize that for you using pickle.

Flask url_for stop accepting double-digit user IDs. BUG?

I am relatively new to Flask and I could not figure out what is the problem with my function nor I could find that someone had had the same problem. It is even difficult to explain.
Template url_for link:
<a href="{{ url_for('view_post', pc=post.product_category_by.product_cat_name, post_id=post.id, ui=post.author_id) }}">
Flask view function: (Flask SQLAlchemy. PostgreSQL)
#app.route('/viewref/<pc>/<int:post_id><int:ui>')
def view_post(pc, post_id, ui):
post = db.session.query(Post).filter_by(id=post_id).first()
db.session.commit()
gc.collect()
return render_template('view_post.html', post=post)
Example:
Lets take a post with ID 26 and two users (or authors of post) with IDs 9 and 10. When I pass a user with ID 9, I get redirected to the correct view with the end url: /viewref/productCat/269 (26 is post id and 9 is user id). Great, it works and I see a post!
The problem:
But when a user with ID 10 or higher (12, 299 etc.) get passed in the url_for, the query of view_post function pulls None from the database, subsequently my view_post.html template throws an Error
(UndefinedError: 'None' has no attribute 'product_category_by')
What is the connection?
The key of the view_post function is only to fetch a post by a given post_id. Why does it get None in the cases where user ID > 9, if my function takes only post_id for the query and other variables are used for the url construction only?
P.S. It all works fine if I exclude ui from the url_for and view_post function.
I'm not sure how you're expecting the URL router to tell where post_id ends and ui starts. You should use an actual separator:
#app.route('/viewref/<pc>/<int:post_id>/<int:ui>')
I think you should change your URL scheme. What if the user id is 12 and the post_id is 34? In your URL it would be concatenated to 1234. How can we tell this apart from post_id: 1, user_id: 234 versus post_id: 123, user_id: 4?

post request with a form, then being able to save the data it returned on a separate request

let's say I have a form like this
<form role="form">
<div class="form-group">
<label for="exampleInputEmail1">Email address</label>
<input type="email" class="form-control" id="exampleInputEmail1" placeholder="Enter email">
</div>
<div class="form-group">
<label for="exampleInputPassword1">Password</label>
<input type="password" class="form-control" id="exampleInputPassword1" placeholder="Password">
</div>
</form>
then I have a fairly generic route that sorts through a static file
#app.route('/search/properties/', methods=['GET', 'POST'])
def properties_search():
form = request.form
if request.method == 'POST':
properties = CSVReader('properties.csv')
d = [x._asdict() for x in properties.data]
gen = stream_with_context(search_csv(d, form))
return Response(stream_with_context(
stream_template('advanced_search/results.html', form=form, rows=gen)
))
return render_template('advanced_search/advanced.html', form=form)
This will render a page with the results that I am looking for -- and all is well with that. However, on the page, I also want to add the capability to download the data as a CSV.
| download csv | | return to search |
- result 1
- result 2
...
The problem I am having is that, after the post request, I need some way to know what their last form request was on the application.
What is the best way to do this?
There isn't a single "best way" to do this, because what's "best" in your specific case might not suit another scenario. That being said, below are a couple of possible ways (which barely scratch the surface of what you need to do with them to make them production-ready) and there are many, many more.
You really need to research the options yourself here; the scenario you have is not specific to python or flask, it's pretty much a problem for anyone building an HTTP application that needs to remember state!
Render the received form values in hidden inputs
When you are rendering the initial search results. Render the "Download CSV" as the submit input of a form that will post these values back but to the download renderer.
I wouldn't be a huge fan of this as it requires a button and a POST when you are really looking for a GET and if you actually need the password in order to render the results, it's a security problem.
Whilst rendering the initial search results page, render the link for "Download CSV" such that it contains some kind of session ID.
When you receive the initial POST of the form to generate the search results. Take the email and store it in a database table (or some other persistent storage mechanism), recording some kind of generated ID as a result of this operation. You could also save any other search parameters the user submitted, etc.
This ID then becomes a querystring parameter of the link to "Download as CSV". i.e. when your template renders the initial search page, the link ends up like "http://blahblah.com/download?ID=12345"
When the link is requested by the user, lookup the search/ user information from the database based on the querystring ID parameter, and then pass it to whatever template will render the CSV for you.
There are many flavours of this approach and you need to pick the best for your scenario - you can save the search criteria for replay, or save the actual search results, it depends on the nature of the search, how expensive it is to run and whether "download as CSV" has to replay the search OR return the exact results originally obtained, etc), and you will also need to harden it. Don't send raw database IDs - send a hashed/encrypted version of them so that users cannot "guess" download IDs, etc.
I'd recommend this kind of approach because it doesn't require you to return the username/password to the client at all.
Hopefully that will get you thinking :)

Categories