Flask sending info with GET when POST specified - python

I am creating an online bookstore and am having trouble with one specific entry. When a user purchases a book they are able to rate it and this functionality works except for the first book they purchased. The function works with the POST method and the form passes in a book_id and a rating which I then enter into a database using SQLAlchemy. The HTML code to submit the form is as such
<form action="/add_rating" method="POST">
<select name="rating" id="rating">
<option value="1">1</option>
<option value="3">2</option>
<option value="3">3</option>
<option value="4">4</option>
<option value="5">5</option>
</select>
<button class="btn btn-success" id='adding_rating' name="book_id" type="submit" value='{{display_cart[book].book_id}}'>Rating</button>
</form>
As you can see the method is set to POST. The background function/route to enter the rating into the database is as follows:
#app.route('/add_rating',methods=["POST"])
def add_rating():
if "cart" not in session:
flash("There is nothing in your cart.")
return render_template("cart.html", display_cart = {}, total = 0)
dict_of_books = {}
## getting user info
user = User.query.get(session['user_id'])
user_name = user.first_name + ' ' + user.last_name
##Get order history by user_id
order = get_order_history(session['user_id'])
dict_of_books = {}
total_price = 0
for i in range(len(order)):
total_price += order[i].total
book = get_book_by_id(order[i].book_id)
details = {'book_id': order[i].book_id, "title": book.title, "quantity": order[i].quantity, 'total': order[i].total}
dict_of_books[i] = details
book_id = request.form.get('book_id')
ratings = request.form.get('rating')
user_id = session['user_id']
add_rating = create_rating(ratings,book_id,user_id)
return render_template("rating.html", display_cart = dict_of_books, total = total_price, user_info = user_name)
Now when I rate the first book it is sending the information through GET into the URL, but all the other purchases are sending through POST. The output flask/sqlalchemy gives for the first book is "GET /rating?rating=3&book_id=4 HTTP/1.1" 200 - but for the rest of the books it is "POST /add_rating HTTP/1.1" 200 -. The /rating route is basically just getting information about purchased books and displaying to the page and I do not specify GET/POST in the route. In case the route is needed I will post below, but I cant imagine it being of use to this issue.
#app.route('/rating')
def rate_book():
if "cart" not in session:
flash("There is nothing in your cart.")
return render_template("cart.html", display_cart = {}, total = 0)
dict_of_books = {}
## getting user info
user = User.query.get(session['user_id'])
user_name = user.first_name + ' ' + user.last_name
##Get order history by user_id
order = get_order_history(session['user_id'])
dict_of_books = {}
total_price = 0
for i in range(len(order)):
total_price += order[i].total
book = get_book_by_id(order[i].book_id)
details = {'book_id': order[i].book_id, "title": book.title, "quantity": order[i].quantity, 'total': order[i].total}
dict_of_books[i] = details
return render_template("rating.html", display_cart = dict_of_books, total = total_price, user_info = user_name)

I figured out the issue, it seems that I have added a "parent" form tag and within it added another form tag to handle the button press. By changing the first form (one without method="POST") to just a div tag, it fixed the issue.
Example,
Original:
<div class="container h-100">
<div class="row align-items-center h-100">
<div class="col-6 mx-auto">
<div style="display: flex; align-items: center;">
<form>
...
<form action="/add_rating" method="POST">
...
</form>
</div>
</div>
</div>
</div>
To
<div class="container h-100">
<div class="row align-items-center h-100">
<div class="col-6 mx-auto">
<div style="display: flex; align-items: center;">
<div>
...
<form action="/add_rating" method="POST">
...
</div>
</div>
</div>
</div>
</div>

Related

Flask always send post request

I have coded the below form and controller but when the page is loaded, the page automatically send request to my database. How to change the form or controller and just send request when I clicked create button.
html code:
<form method="POST" action="/conference/create">
<div class="field">
<div class="control">
<input class="input is-large" type="text" name="name" placeholder="Your Name" autofocus="">
</div>
</div>
<input class="input is-large" type="text" name="shortname" placeholder="Your Shortname">
</div>
</div>
<div class="field">
<div class="control">
<input class="input is-large" type="text" name="year" placeholder="Year">
</div>
</div>
<button class="button is-block is-info is-large is-fullwidth">Create</button>
</form>
</div>
</div>
{% endblock %}
controller function:
#main.route('/conference/create', methods=['POST','GET'])
#login_required
def create_conference():
name = request.form.get('name')
shortname = request.form.get('shortname')
year = request.form.get('year')
startdate = request.form.get('startdate')
enddate = request.form.get('enddate')
submissiondeadline = request.form.get('submissiondeadline')
website = request.form.get('website')
tag = request.form.get('tag')
datem = datetime.today().replace(day=1)
conference = Conference(confid="1", creationdatetime=datem, name=name, shortname=shortname, year=year, startdate=startdate,
enddate=enddate, submissiondeadline=submissiondeadline, creatoruser=12, website=website)
conferenceTag = ConferenceTags("1", tag)
db.session.add(conference)
db.session.commit()
db.session.add(conferenceTag)
db.session.commit()
return render_template('create_conference.html')
By the way, I have changed controller's method parameters with just 'POST' when I do that it gives me not allowed methods error.
you should add an if statement to specify if the incoming request is POST or GET and act accordingly.
if request.method=='GET':
#load page
elif request.method=='POST':
#update database
#main.route('/conference/create', methods=['POST','GET'])
#login_required
def create_conference():
if request.method == 'POST':
name = request.form.get('name')
shortname = request.form.get('shortname')
year = request.form.get('year')
startdate = request.form.get('startdate')
enddate = request.form.get('enddate')
submissiondeadline = request.form.get('submissiondeadline')
website = request.form.get('website')
tag = request.form.get('tag')
datem = datetime.today().replace(day=1)
conference = Conference(confid="1", creationdatetime=datem, name=name, shortname=shortname, year=year, startdate=startdate,
enddate=enddate, submissiondeadline=submissiondeadline, creatoruser=12, website=website)
conferenceTag = ConferenceTags("1", tag)
db.session.add(conference)
db.session.commit()
db.session.add(conferenceTag)
db.session.commit()
return 'you want to do.'
return render_template('create_conference.html')

how to sort an array of object in django?

I am making an project with python and django about github profile. I already successful call user github API and it give me like a lot of repos from that user and I want to display like 8 of them and sorted by stars or forks or size of that repo. How can I do that?
Here's an image of my page:
Here is my views.py:
def user(req, username):
username = str.lower(username)
# Get User Info
with urlopen(f'https://api.github.com/users/{username}') as response:
source = response.read()
data = json.loads(source)
# Get Limit Call API
with urlopen(f'https://api.github.com/rate_limit') as response:
source = response.read()
limit_data = json.loads(source)
# Get User Repo Info
with urlopen(f'https://api.github.com/users/{username}/repos') as response:
source = response.read()
user_repos = json.loads(source)
def sort_user_repo_by_stars(user_repos):
return user_repos['stargazers_count']
user_repos.sort(key=sort_user_repo_by_stars, reverse=True)
created_at = data['created_at']
created_at = datetime.datetime.strptime(created_at, "%Y-%m-%dT%H:%M:%SZ")
created_at = created_at.strftime("%B %d, %Y")
context = {
'username': username,
'data': data,
'created_at': created_at,
'limit_data': limit_data,
'user_repos': user_repos,
}
return render(req, 'user.html', context)
and here is my template user.html:
<div class="repos">
<div class="top-repo">
<label for="top-repos" class="col-sm-3 col-form-label">Top Repos <span>by </span></label>
<select class="custom-select bg-light text-primary" name="pick">
<option selected="stars">stars</option>
<option value="forks">forks</option>
<option value="size">size</option>
</select>
</div>
<div class="repo-info">
{% for repo in user_repos %}
<div class="card-deck">
<div class="card shadow">
<div class="card-body">
<h4 class="card-title">{{repo.name}}</h4>
<p class="card-text clearfix">
<i class="fas fa-circle"></i> {{repo.language}}
<i class="fas fa-star"></i> {{repo.stargazers_count}}
<i class="fal fa-code-branch"></i> {{repo.forks}}
<span class="float-right">{{repo.size}} KB</span>
</p>
</div>
</div>
</div>
{% endfor %}
</div>
</div>
If you only want an array of 8 elements then you can use a slice operator on your list.
context = {
"username": username,
"data": data,
"created_at": created_at,
"limit_data": limit_data,
"user_repos": user_repos[:8],
}

iterate users in friends list over same modal and access database content

I am making a chat box for my website. As per current implementation,i i am iterating over the current friends list and and anchor tag triggers a modal for that particular user. The modal is common for all users as it only changes the data inside.
Now, i have used jquery to fetch the message history ( from model object ) and display is modal body.
I can click on different users and view the messages in their respective models correctly. However, when i try to submit the form to send another message it gets added to the message box of first user. This is happening for all users in the friend list.
How can i trigger the form to post in the model of the correct user.
Template
{% for friend in friends_list %}
<li style="padding:10px">{{friend.to_user.usercreation.fname}} {{friend.to_user.usercreation.lname}} <i style ="color:green;font-size:0.65rem;text-align:justify;float:right;margin-top:8.5px;" class="fa fa-circle" aria-hidden="true"></i></li>
<form action="{% url 'usercreation:addmessage' friend.to_user.usercreation.pk %}" method="post">
{% csrf_token %}
<div class="modal fade" id="chatbox" tabindex="-1" role="dialog">
<div class="modal-dialog" role="document">
<div class="modal-content">
<div class="modal-header text-center">
<h6 class="modal-title w-100">BlogChat</h6>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<pre><div class="modal-body"></div></pre>
<div class="modal-footer">
<input style="width:330px;height:40px" type="text" name="addmessage" value="" placeholder="Type..">
<button style="float:right;" type="submit" class="btn btn-primary">Send message</button>
</div>
</div>
</div>
</div>
</form>
{% endfor %}
JQUERY:
<script type="text/javascript">
$('#chatbox').on('show.bs.modal', function (event) {
var button = $(event.relatedTarget)
var recipient = button.data('whatever')
attributes
$.ajax({
url: "{% url 'fetcher' %}",
data: {
'search': recipient
},
dataType: 'json',
success: function (data) {
list = data.list;
$('#chatbox').find('.modal-body').text(list)
}
});
var modal = $(this)
modal.find('.modal-title').text('Chat with ' + recipient)
})
</script>
VIEWS:
def fetcher(request):
if request.is_ajax():
name = User.objects.get(username=request.GET.get('search', None))
b = ChatMessage.objects.get(user1 = request.user,user2 = name)
print(b.message)
data = {
'list': b.message,
}
print(data)
return JsonResponse(data)
def addmessage(request,pk):
if request.method=='POST':
print(request.user)
print(User.objects.get(username=UserCreation.objects.get(pk=pk)))
obj = ChatMessage.objects.get(user1 = request.user , user2 = User.objects.get(username=UserCreation.objects.get(pk=pk)) )
obj2 = ChatMessage.objects.get(user2 = request.user , user1 = User.objects.get(username=UserCreation.objects.get(pk=pk)) )
name = request.POST.get('addmessage')
obj.message += ('\n'+str(request.user)+': '+name)
obj2.message += ('\n'+str(request.user)+': '+name)
obj.save()
obj2.save()
return HttpResponseRedirect(reverse('usercreation:profile',args=[request.user.usercreation.pk]))
Models:
class ChatMessage(models.Model):
user1 = models.ForeignKey(User, on_delete=models.CASCADE ,related_name="participant1")
user2 = models.ForeignKey(User, on_delete=models.CASCADE , related_name="participant2")
message = models.TextField(default="")
date = models.DateTimeField(auto_now=True, db_index=True)

Unable to update record with Flask, MySQL

I'm working on a music database app with Flask, and I have a page where I can insert a record into the database that works how it should. Yesterday, I built a page where you can edit the values of the record. For the route, I copied the code from another, more simple app I made and re-wrote it for this app. When I visit the edit page, it fills the text boxes with the current values for the record...but when I change any of the items and submit it, nothing happens. It renders the page that I specified in the route after submit, but when I query the table nothing has changed.
Here's the route:
#app.route('/edit_album/<string:catno>/', methods=['GET', 'POST'])
def edit_album(catno):
cur = mysql.connection.cursor()
# Get article by catno
result = cur.execute("SELECT * FROM albums WHERE catno = %s", [catno])
album = cur.fetchone()
form = AlbumForm()
form.artist.data = album['artist']
form.title.data = album['title']
form.year.data = album['year']
form.rlabel.data = album['label']
form.genre.data = album['genre']
if request.method == 'POST':
# album art
#cover =
catno = album['catno']
artist = form.artist.data
title = form.title.data
year = form.year.data
rlabel = form.rlabel.data
genre = form.genre.data
# format (lp or tape)
# Create Cursor
cur = mysql.connection.cursor()
# Execute cursor
cur.execute("UPDATE albums SET artist=%s, title=%s, year=%s, label=%s, genre=%s WHERE catno=%s", (artist, title, year, rlabel, genre, catno))
# Commit to DB
mysql.connection.commit()
# Close DB connection
cur.close()
return redirect(url_for('view_album', catno=catno))
return render_template('edit_album.html', album=album, form=form)
And here's the actual edit page:
{% extends 'layout.html' %}
{% block body %}
<div class="container">
<div class="col-md-12 text-center border mt-3">
<h1 class="text-white">{{album.artist}} :: {{album.title}}</h1>
</div>
<div class="row mt-3">
<div class="col-sm-4 col-md-4 text-center">
{% if album.albumArt == None %}
<img src="/static/album_art/not_available.png" height="300" width="300">
<a class="btn btn-primary mt-3">Upload Cover</a>
{% endif %}
</div>
<div class="col-sm-8 col-md-8">
<table class="table table-light table-striped">
<tr>
<td>Artist: {{album.artist}}</td>
</tr>
<tr>
<td>Album: {{album.title}}</td>
</tr>
<tr>
<td>Catalog No: {{album.catno}}</td>
</tr>
<tr>
<td>Record Label: {{album.label}}</td>
</tr>
<tr>
<td>Year Released: {{album.year}}</td>
</tr>
<tr>
<td>Genre: {{album.genre}}</td>
</tr>
</table>
</div>
</div>
<div class="card text-center mt-3">
<div class="card-header text-center bg-primary">
<p>EDIT ALBUM</p>
</div>
<form method="POST" action="{{ url_for('edit_album', catno=album.catno) }}" class="card-footer text-center">
<div class="row">
{{ form.csrf_token}}
<div class="col-sm-4">
{{ form.artist.label }}<br>
{{ form.artist }}<br>
</div>
<div class="col-sm-4">
{{ form.title.label }}<br>
{{ form.title }}<br>
</div>
<div class="col-sm-4">
{{ form.year.label }}<br>
{{ form.year }}<br>
</div>
</div>
<div class="row">
<div class="col-sm-4">
{{ form.rlabel.label }}<br>
{{ form.rlabel }}<br>
</div>
<div class="col-sm-4">
{{ form.genre.label }}<br>
{{ form.genre }}
</div>
</div>
<p><input class="btn btn-primary mt-3" type="submit" value="Submit">
</form>
</div>
</div>
{% endblock %}
The only thing I really got from searching last night, is that I may have two connections to the DB open, but I don't since I just have the one connection at the beginning of the script. It wouldn't be an issue with too many cursors, would it?
Otherwise, this is the first app I've used the Flask-WTF module for the forms, so could it be something wrong I'm doing with that? Here's that class if there's any questions:
# Form for adding record to database
class AlbumForm(FlaskForm):
# Album Art - figure out image uploads
cover = FileField('Upload Cover Art')
catno = StringField('Catalog Number')
artist = StringField('Artist')
title = StringField("Album Title")
year = StringField('Year Released')
rlabel = StringField('Record Label')
genre = StringField('Genre')
The app doesn't throw any errors, so I'm not sure what's going on, or if I'm just overlooking something.
Seems like you are overwriting your form on post, because on both get and post you are fetching an album entry and filing a form with it's data. It should work if you structure it like this:
def edit_album(catno):
cur = mysql.connection.cursor()
if request.method == 'POST':
form = AlbumForm() # WTForms will extract data from the request's body by itself
# other code from a if request.method == 'POST' block
elif request.method == 'GET':
cur.execute("...")
album = cur.fetchone()
form = AlbumForm()
form.artist.data = album['artist']
form.title.data = album['title']
form.year.data = album['year']
form.rlabel.data = album['label']
form.genre.data = album['genre']
return render_template...
P.S. A good but too high-level and magic-y (and hard to understand as a result) example is given in the WTForms docs: wtform has formdata and obj arguments; each time wtform instance is created (form = AlbumForm) it tries to extract data from a request to populate it's fields. If it fails (and it would on a get-request because no form-data exist) it will get data from a second source -- the obj argument, which has your current db-entry value. But on post wtform successfully retrieves data from a post-request-formdata which then populates db-entry which is then saved.
The solution was this:
The value of the variables that are used to update the database were supposed to be like this:
catno = album['catno']
artist = request.form['artist']
title = request.form['title']
year = request.form['year']
rlabel = request.form['rlabel']
genre = request.form['genre']
And not:
catno = album['catno']
artist = form.artist.data
title = form.title.data
year = form.year.data
rlabel = form.rlabel.data
genre = form.genre.data
Because in the latter method, I was just passing the same data into the variables that was loaded into the form when the page was opened, instead of the updated values in the text boxes.

GAE Python self.request.get_all not returning selected value

I am trying to get a value out of a SELECT tag.
My HTML is
<div class="form-group">
<label for="acadYear">Academic Year:</label>
<select id="acadYear" name"acadYear">
<option value="2017-18">This Academic Year</option>
<option value="2018-19">Next Academic Year</option>
</select>
</div>
My python code to get this value is
acadYear=self.request.get('acadYear')
This doesn't return anything. When I try
acadYear=self.request.get_all('acadYear')
throws an error
BadValueError: Expected string, got []
What's happening? Any clues?
MORE DETAILS
The Entity
from google.appengine.ext import ndb
class Allocation(ndb.Model):
acadYear = ndb.StringProperty()
branch = ndb.StringProperty()
semester = ndb.StringProperty()
subjectCode = ndb.StringProperty()
subjectName = ndb.StringProperty()
facultyId = ndb.StringProperty()
facultyName = ndb.StringProperty()
choiceNumber = ndb.StringProperty()
status = ndb.StringProperty()
createdOn = ndb.DateTimeProperty(auto_now_add=True)
#classmethod
def faculty_query(cls, parent_key):
return cls.query(ancestor=parent_key).order(-cls.createdOn)
HTML Code to Receive Data
<form action="" method="post">
<legend>Mention Your New Preferences Here</legend>
<div class="form-group">
<label for="acadYear">Academic Year:</label>
<select id="acadYear" name="acadYear">
<option value="2017-18">This Academic Year</option>
<option value="2018-19">Next Academic Year</option>
</select>
</div>
<div class="form-group">
<label for="branch">Branch:</label>
<input type="text" id="branch" name"branch" value="CSE or ISE or MCA">
</div>
<div class="form-group">
<label for="semester">Semester:</label>
<input type="text" id="semester" name="semester" value="From 1 to 8"/>
</div>
<div class="form-group">
<label for="choiceNumber">Choice#:</label>
<input type="text" id="choiceNumber" name="choiceNumber" value="1,2,3"/>
</div>
<div class="form-group">
<label for="subjectCode">Subject Code:</label>
<input type="text" id="subjectCode" name="subjectCode" value="Example: 10CS43"/>
</div>
<div class="form-group">
<label for="subjectName">Subject Name:</label>
<input type="text" id="subjectName" name="subjectName" value="Example: Design and Analysis of Algorithms"/>
</div>
<div class="form-group">
<button type="submit">Save Preference</button>
</div>
</form>
Python Handler
MainHandler Class
class MainHandler(webapp2.RequestHandler):
def _render_template(self, template_name, context=None):
if context is None:
context = {}
# Get the logged in user
user = users.get_current_user()
ancestor_key = ndb.Key("User", user.nickname())
qry = Allocation.faculty_query(ancestor_key)
context['allocs'] = qry.fetch()
template = jinja_env.get_template(template_name)
return template.render(context)
#ndb.transactional
def _create_alloc(self, user):
alloc = Allocation(parent=ndb.Key("User", user.nickname()),
acadYear=self.request.get_all('acadYear'),
branch=self.request.get('branch'),
semester=self.request.get('semester'),
subjectCode=self.request.get('subjectCode'),
subjectName=self.request.get('subjectName'),
facultyId=user.user_id(),
facultyName=user.nickname(),
choiceNumber=self.request.get('choiceNumber'),
status='Requested')
alloc.put()
def get(self):
user = users.get_current_user()
if user is not None:
logout_url = users.create_logout_url(self.request.uri)
template_context = {
'user': user.nickname(),
'logout_url': logout_url,
}
self.response.out.write(
self._render_template('main.html', template_context))
else:
login_url = users.create_login_url(self.request.uri)
self.redirect(login_url)
def post(self):
user = users.get_current_user()
if user is None:
self.error(401)
self._create_alloc(user)
logout_url = users.create_logout_url(self.request.uri)
template_context = {
'user': user.nickname(),
'logout_url': logout_url,
}
self.response.out.write(
self._render_template('main.html', template_context))
acadYear=self.request.get_all('acadYear') returns a list - apparently in Unicode - which need to become string for putting into data store.
I have tried the following
(1) Use an intermediate variable = does not work.
(2) Encoding to UTF-8 - can't because the list does not have an encode method.
(3) I don't know which element would be chosen [0] or [1]. Hence I can't try
acadYear=self.request.get_all('acadYear')[0].encode('UTF-8').
Such a simple thing - but - why it had to be so complex!
Any help is appreciated. I am losing face before kids.
You have a typo in this line:
<select id="acadYear" name"acadYear">
You need to add an "=" after "name". Because of this typo, your browser is not sending the data for the select field.

Categories

Resources