EDIT 2: added the whole main-element in the HTML code
I'm building my own website with Python, HTML, CSS and Jinja. On this page I'm trying to build a function where users can post comments and reviews on the given recipe.
So, I'd like to post the following code but it gives this error message:
"?[1m?[31mPOST /recipes HTTP/1.1?[0m" 400 -
{% block main %}
<div id="recipe_id" style="display: none;" name="recipe_id">
{% for recipe in recipes %}
{{ recipe.recipe_id }}
{% endfor %}
</div>
<table class="table table-striped">
{% for recipe in recipes %}
<h1>{{ recipe.recipe_name }}</h1>
<a href= {{ recipe.link_to_recipe }} ><h4>Link to recipe</h4></a>
<h5>{{ recipe.category }}</h5>
<h5>{{ recipe.review }}</h5>
{% endfor %}
</table>
<div>
<textarea name="comments" id="comment" type="text" autofocus="autofocus" form="commentrating">Give your comment</textarea>
</div>
<h3>Rate the recipe!</h3>
<form action="/recipes" method="post" id="commentrating">
<fieldset>
<div id="review">
<select name="rating">
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
<option value="4">4</option>
<option value="5">5</option>
</select>
</div>
<br>
<div>
<input type="submit" id="button" value="Submit">
</div>
</fieldset>
</form>
<br>
<table class="table table-striped" id="comments">
<thead>
<tr>
<th colspan="1">Posted by</th>
<th colspan="3">Comment</th>
</tr>
</thead>
{% for comment in comments %}
<tr>
<td colspan="1">{{ comment.user }}</td>
<td colspan="3">{{ comment.comments }}</td>
</tr>
{% endfor %}
</table>
{% endblock %}
After three hours, I'm finally 'admitting' that I'm stuck on why it gives error 400 as it looks like all my other form actions. It shows 31mPOST while my other form action show a 37mPOST. Maybe this helps.
Anyone knows what I'm doing wrong and how I can fix this?
EDIT
I have also tried to find a bug in the python code, but cannot find the cause of the 400 error.
As the problem is probably in the server-side code, here is the code:
#app.route("/recipes", methods=["POST"])
#login_required
def recipes():
"""Function where the comment and rating is added to the recipe."""
if request.method == "POST":
#get the comment and/or rating
comment = request.form["comments"]
rating = int(request.form["rating"])
recipe = int(request.form["recipe_id"])
db.execute("INSERT INTO comments (recipe_id, comments, user) \
VALUES (:recipe_id, :comments, :user)", \
recipe_id=recipe, comments=comment, user=session["user.id"])
givenrating = db.execute("SELECT reviews, number_of_reviews FROM recipes WHERE \
recipe_id=:recipe", recipe=recipe)
# check if there already is given a rating
if givenrating[0]["number_of_reviews"] == "None":
db.execute("UPDATE recipes SET review=:rating, number_of_reviews=:numb \
WHERE recipe_id=:recipe", recipe=recipe, rating=rating, numb=1)
#load chosen recipe
recipes = db.execute("SELECT * FROM recipes JOIN categories ON \
recipes.category = categories.cat_id WHERE recipe_id=:recipe", recipe=recipe)
#load comments of the recipe
comments = db.execute("SELECT * FROM comments JOIN users on \
comments.user = users.id WHERE recipe_id=:recipe", recipe=recipe)
return render_template("recipe.html", recipes=recipes, comments=comments)
else:
number = int(givenrating[0]["number_of_reviews"])
ratings = int(givenrating[0]["reviews"])
# update existing rating
fullrating = ratings * number
newrating = fullrating + rating
number += 1
averagerating = newrating / number
db.execute("UPDATE recipes SET review=:review, number_of_reviews=:newnumber \
WHERE recipe_id=:recipe", review=averagerating, newnumber=number, recipe=recipe)
#load chosen recipe
recipes = db.execute("SELECT * FROM recipes JOIN categories ON \
recipes.category = categories.cat_id WHERE recipe_id=:recipe", recipe=recipe)
#load comments of the recipe
comments = db.execute("SELECT * FROM comments JOIN users on \
comments.user = users.id WHERE recipe_id=:recipe", recipe=recipe)
return render_template("recipe.html", recipes=recipes, comments=comments)
This error code is for Bad Request.
you are trying to get three elements(comments, rating and recipe_id), but they are not present in the form, so flask throws a 400 error.
what you could do is using hidden inputs to send that kind of information.
<form action="/recipes" method="post" id="commentrating">
<input type="hidden" name="recipe_id" value="{{ recipe_id }}">
<textarea name="comments" id="comment" type="text" autofocus="autofocus" form="commentrating">Give your comment</textarea>
<fieldset>
<div id="review">
<select name="rating">
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
<option value="4">4</option>
<option value="5">5</option>
</select>
</div>
<br>
<div>
<input type="submit" id="button" value="Submit">
</div>
</fieldset>
</form>
but, you should design your code differently, so that the recipe_id is set based on the recipe you want to add comment's for.
you can achieve that in numerous ways, but since that's not in the context of this question, i will leave that part alone.
another way to do is, using a JS code(jquery post or AJAX) to post the data, and that way, you could have make a variable with the data you want, and then post it.
Related
ISSUE
I am making a CRUD in Flask. I have a user that has positions, and I would like to update a specific position.
The "URL was not found" problem occurs when I click on the anchor link in user_positions.html.
I am using WTF Forms, SQLalchemy, PostgreSQL, Bootstrap, and pgAdmin4.
I hope I have given you enough information.
The requested URL was not found on the server. If you entered the URL manually please check your spelling and try again.
http://0.0.0.0:5000/user/24/url_for('user_position_update',%20owner_id=current_user.id,%20position_id=position.id)%20%7D%7D
MY CODE
user_positions.html
<i class="mdi mdi-pencil"></i> Update
routes.py
#app.route("/user/<string:owner_id>/positions/<string:position_id>/update", methods=['GET', 'POST'])
def user_position_update(owner_id, position_id):
user = User.query.filter_by(id=owner_id).first()
position = Position.query.filter_by(id=position_id).first()
form = UpdatePositionForm()
if current_user.is_authenticated:
if form.validate_on_submit():
strSQl= "update position set position='"+position+"' where owner_id="+str(user.id)
db.session.execute(strSQl)
db.session.commit()
flash('Your position has been updated!', 'success')
return redirect(url_for('user_positions'))
elif request.method == 'GET':
form.position.data = position
return render_template('user_position_update.html', form=form, user=user, position=position)
forms.py
class UpdatePositionForm(FlaskForm):
position = StringField('Position',
validators=[DataRequired()])
date = DateTimeField('Date',
validators=[DataRequired()])
submit = SubmitField('Update')
user_position_update.html
{% extends "layout.html" %}
{% block content %}
<table class="table">
<tr>
<th>Position</th>
<th>Date</th>
</tr>
<tr>
<form method="POST" enctype="multipart/form-data">
{{ form.hidden_tag() }}
<td>
<div class="form-group">
{{ form.positions.label(class="form-control-label") }}
<select class="form-control form-control-sm" name="position">
<option>{{ current_user.positions[-1].position }}</option>
<option>1</option>
<option>2</option>
<option>3</option>
<option>4</option>
<option>5</option>
<option>6</option>
<option>7</option>
<option>8</option>
<option>9</option>
<option>10</option>
<option>11</option>
<option>12</option>
<option>13</option>
<option>14</option>
<option>15</option>
</select>
</div>
</td>
<td>
<div class="form-group">
{{ form.position.label(class="form-control-label") }}
<input type="date" class="form-control" value="{{ position.created_date }}" name="date">
</div>
</td>
<td>
<div class="form-group">
{{ form.submit(class="btn btn-outline-info") }}
</div>
</td>
</tr>
</table>
{% endblock content %}
PostgreSQL tables
user table
position table
I am trying to get data from a table from an HTML that I created, that table has data from a table from a sqlite database.
So, for each checkbox marked in that table, I need to get the data from that row.
However, I am only receiving data from the first line, even if I mark several lines.
I've tried using the getlist, and creating an array, but without success. Since I am still learning from Flask and etc., I already looked for examples but I also couldn't find a solution.
My HTML table:
HTML code:
<form method="POST">
<table id="example" class="table table-striped table-bordered" style="width:100%">
<thead>
<tr>
<th>Id_Game</th>
<th style="width: 800px;">Game</th>
<th style="width: 100x;">Critic Score</th>
<th style="width: 100px;"><input type="checkbox" id="all" onclick="checkAll(this)"></th>
</tr>
</thead>
<tbody>
{% for game in games.items %}
<tr>
<td name="ID_GAME" id="ID_GAME">{{ game.ID_GAME}}</td>
<input type="hidden" name="ID_GAME" value="{{ game.ID_GAME }}" />
<td name="NM_GAME"> {{ game.NM_GAME}} </td>
<input type="hidden" name="NM_GAME" value="{{ game.NM_GAME }}" />
<td name="NR_CRITICSCORE">{{ game.NR_CRITICSCORE}}</td>
<td>
<input type="checkbox" id="one_checkbox" name="one_checkbox">
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% if current_user.is_authenticated %}
<div class="form-group">
<button type="submit" class="btn btn-primary">Submit</button>
</div>
{% else %}
<div class="form-group">
<button type="submit" class="btn btn-primary" onclick="submitoff()">Submit</button>
</div>
{% endif %}
</form>
Python code:
#main.route('/games/<int:page_num>', methods=('GET', 'POST'))
def games(page_num):
games = V_GAMES.query.paginate(per_page=10, page=page_num, error_out=True)
if request.method =='POST':
if request.form.getlist('one_checkbox'):
ID_USER = current_user.id
ID_GAME = request.form.get('ID_GAME')
NM_GAME = request.form.get('NM_GAME')
IC_PLAYED = "YES"
SYSDATE = datetime.datetime.now()
# create new user with the form data. Hash the password so plaintext version isn't saved.
addprofile = USERGAMESPLAYED(ID_USER=ID_USER, ID_GAME=ID_GAME, NM_GAME=NM_GAME, IC_PLAYED=IC_PLAYED, DT_PLAYED=SYSDATE)
# add the new user to the database
db.session.add(addprofile)
db.session.commit()
flash('Games have been successfully added to your profile.')
if not request.form.get('one_checkbox'):
flash('You have to check at least one game to add to your profile!')
return render_template('games.html', games=games)
In this two lines of code
ID_GAME = request.form.get('ID_GAME')
NM_GAME = request.form.get('NM_GAME')
You are always getting the data from the first line since you are using get but not gelist.
The way I would do this is to first modify your checkbox
<td>
<input type="checkbox" id="one_checkbox" name="one_checkbox" value="{{ your_game_id }}">
</td>
So that you can get all the games that are checked by their id
print(request.form.getlist('one_checkbox'))
[checked_game_id_1, checked_game_id_2, ...]
Then, use a for loop to loop through the checked games, use their id to get ID_GAME and NM_GAME, and do your processing.
EDIT
To get NM_GAME by ID_GAME
for id in request.form.get('one_checkbox')
game = V_GAMES.query.filter_by(ID_GAME=id).first()
nm_game = game.NM_GAME
I have a table and I wish to add an edit button that updates the certain record both visually and in the database.
The HTML I have.
{% for work_entry in work_entries %}
{% if work_entry.date == date.date %}
<form action="{% url 'work_entries:object_edit' work_entry.id %}" method="post">
{% csrf_token %}
<tr>
<td>
<button onclick="return confirm('Are you sure you want this edit?')">Edit
</button>
</td>
<td> <form action="{% url 'work_entries:object_delete' work_entry.id %}" method="post">
{% csrf_token %}
<button onclick="return confirm('Are you sure you want to delete this record?')">Delete
</button>
</form>
</td>
<td>{{ work_entry.id }}</td>
<td><input type="text" value="{{ work_entry.description }}" name="description"></td>
<td><input type="number" value="{{ work_entry.num_hours }}" name="hours"></td>
</tr>
</form>
{% endif %}
{% endfor %}
The views.py
def object_edit(request, object_id):
record = get_object_or_404(WorkEntry, pk=object_id)
if request.method == "POST":
record.description = request.POST["description"]
record.num_hours = request.POST["hours"]
record.save()
return redirect("/employeePage")
return render(request, "employeePage.html")
And urls.py
app_name = "work_entries"
urlpatterns = [
path("", views.employee_view, name="employeePage"),
url(r"^delete/(?P<object_id>[0-9]+)/$", views.object_delete, name="object_delete"),
url(r"^edit/(?P<object_id>[0-9]+)/$", views.object_edit, name="object_edit"),
]
I used an input tag in the as I thought that would allow me to change data and save it. However this is giving me MultiValueDictKeyError at /employeePage/edit/14/
'description' error. I am not too experienced with jquery which from research I saw that could work but I don't seem to get it right. Can someone help or even suggestions on how I should approach this would be useful.
Note: there is already a button to delete the record which works, I tried a similar approach for editing, but it doesn't work.
I fully encourage you to use the forms provided by Django, it will make your life easier.
And I fully encourage you as well to not use a form for your delete stuff, it should be a simple link, it would avoid to have a form in a form. I think your problem is here. Having a form in a form with the button in the middle make impossible for your browser to know which parts of the form you want to submit.
As well you have two buttons but none of them is submit type.
If you don't want to use the Django forms a way to do it would be
{% for work_entry in work_entries %}
{% if work_entry.date == date.date %}
<form action="{% url 'work_entries:object_edit' work_entry.id %}" method="post">
{% csrf_token %}
<tr>
<td>
<button>
Delete
</button>
</td>
<td> <button type="submit" onclick="return confirm('Are you sure you want to update this record?')">
Update
</button>
</td>
<td>{{ work_entry.id }}</td>
<td><input type="text" value="{{ work_entry.description }}" name="description"></td>
<td><input type="number" value="{{ work_entry.num_hours }}" name="hours"></td>
</tr>
</form>
{% endif %}
{% endfor %}
It's not the most beautiful way to do it I tried to keep your achitecture
I create a table show book info from SQLite database and I'm trying to create an "Add New Book form" to make it add a new book into a table database. How can I add a book and then make it show into the table Book Info ?
<div class="container-fluid">
<!-- Add Book -->
<div class="add-book">
<h1>Add a Book</h1>
<form action="/add_book" method="POST" class="add_more_book">
{% csrf_token %}
<p>
<label for="title" id="title">Title:</label>
<input type="text" name="title" class="form-control" id="input-title" />
</p>
<p>
<label id="desc" for="desc">Description:</label>
<textarea class="form-control" name="description" id="input-desc" rows="3"></textarea>
</p>
<button type="submit" class="btn shadow btn-primary" id="addbtn">
Add
</button>
</form>
</div>
<!-- End of Add Book -->
<!-- Book info -->
<div class="book-info">
<form action="/" method="POST">
{% csrf_token %}
<table class="table table-bordered">
<thead>
<tr>
<th>ID</th>
<th>Title</th>
<th>Action</th>
</tr>
</thead>
<tbody>
{% for i in all_books_info %}
<tr>
<th scope="row">{{i.id}}</th>
<td>{{i.title}}</td>
<td>Views</td>
</tr>
{% endfor %}
</tbody>
</table>
</form>
</div>
Lets say you have an a table named Bio in your models.py file. Like so
class Bio(models.Model):
author = models.CharField(max_length= 20, null=True)
book = models.CharField(max_length= 20, null=True)
title = models.CharField(max_length=50)
desc = models.CharField(max_length=200)
Assuming you have imported you models at the top of you views.py file.
from .models import *
The * imports all the models from the models file.( There are reasons you may or may not want to import all the models in the future.)
In the views.py file
def processInfo(request):
print(request.POST["title"]
if len(str(request.POST["title"])) < 2:
print("Needs more the 2 characters to submit")
else:
print("Meets rule")
Bio.objects.create(title = request.POST["title"])
The print is there to see if anything is received. You always want to check first if you are not getting an empty string. I use len and other tools to check https://www.w3schools.com/python/ref_func_len.asp
There are a few ways on creating and getting data from the django models.
https://docs.djangoproject.com/en/3.0/topics/db/queries/
Finally for the function that renders "/" in the views.py file. Once you retrieve the data that you are looking for, return it with the request and html file.
I have a Django form that takes input values from users. The values are then used in making query to a table ResourceBase, which finally returns a list of filtered results.
Since the results might be a long list, I added a pagination function with "Prev" and "Next" buttons. My problem is that when I click "Prev" or "Next" button, the form gets restored into default values. And all returned results are all gone. How do I prevent this from happening?
I think the form gets reset because of "form1 = QueryForm()" when a request is not "POST". However I just have difficulty coming up with a neat solution since I'm new to Django and web dev.
In views.py:
def search(request):
if request.method == "POST":
form1 = QueryForm(data=request.POST)
layer_dict = []
if form1.is_valid():
inp_ct = form1.cleaned_data['country']
q1 = ResourceBase.objects.filter(country_name__iexact=inp_ct)
for layer in q1:
down_url = 'xxxxxxx'.format(layer.title)
view_url = 'xxxxxxx'.format(layer.title)
layer_dict.append((layer.title, down_url, view_url))
layer_dict = sorted(layer_dict, key = lambda x:x[0])
paginator = Paginator(layer_dict, 10)
page = request.GET.get('page', 1)
try:
layers = paginator.page(page)
except PageNotAnInteger:
# If page is not an integer, deliver first page.
layers = paginator.page(1)
except EmptyPage:
# If page is out of range (e.g. 9999), deliver last page of results.
layers = paginator.page(paginator.num_pages)
context = {'form1': form1, 'layers': layers}
else:
form1 = QueryForm()
context = {'form1': form1}
return render(request, 'my_app/search.html', context)
In search.html:
<br />
<h3>Pagination Test</h3>
<br /><br/>
<div class="row">
<div class="col-md-4">
<form method="POST">
{% csrf_token %}
<div class="form-controls">
{{ form1|as_bootstrap }}
</div>
<button class="btn btn-primary" type="submit" style="float: right;" title = "Click to search" ><i class="fa fa-search"></i></button>
</form>
<form method="GET">
<button class="btn btn-primary" type="submit" value="Reset" name="Reset" title="Reset all choices">Reset</button>
</form>
</div>
</div>
{% if layers %}
<div class="row">
<div class="col-md-8">
<div id = "search_results" >
<table class="table table-hover">
<thead>
<tr>
<th scope="col">Select</th>
<th scope="col">Layer Name</th>
<th scope="col">Download</th>
<th scope="col">View Layer</th>
</tr>
</thead>
<tbody>
{% for layer in layers %}
<tr>
<td><input class= messageCheckbox type="checkbox" name="checks" value="{{layer.1}}"/></td>
<td>{{layer.0}}</td>
<td> Download Layer </td>
<td><input class="btn btn-primary" onclick="window.open('{{layer.2}}')" id="view" type="button" name="view" value="View"></td>
</tr>
{% endfor %}
<tr>
<td><input type="checkbox" onClick="toggle(this, 'checks')"/> Select All</td>
<td></td>
<td></td>
<td></td>
</tr>
</tbody>
</table>
<button class="btn btn-primary" type="button" name="download" style="float: left;" onClick= "open_all_links();">Download Selected</button>
</div>
<div class="a_pagination" align="right">
<span class="step-links">
{% if layers.has_previous %}
<a class="btn btn-primary btn-sm" name="prev_page" href="?page={{ layers.previous_page_number }}" role="button">Prev.</a>
{% endif %}
<span class="current" style ="color:#2C689C;font-size:16px;padding:8px;">
page {{ layers.number }} of {{ layers.paginator.num_pages }}
</span>
{% if layers.has_next %}
<a class= "btn btn-primary btn-sm" href="?page={{ layers.next_page_number }}" role="button">Next</a>
{% endif %}
</span>
</div>
</div>
</div>
{% endif %}
<script type="text/javascript" >
.......
</script>
You don't need to use POST Method to pass your arguments to your views.py .
Follow the below example and rewrite your view and your html form.
here a simple form for user to enter the word for search:
<form method="get" action="">
<input type="text" name="search4" class="search_input" placeholder="Search" required="required">
<input type="submit" value="Search">
</form>
The next step is that you should check the input in your views.py, we named the input tage name="search4" so we check if there is any input in our form using this code in our views.py:
from django.db.models import Q
from django.core.paginator import Paginator
def search(request):
query = request.GET.get("search4")
if query:
queryset = ResourceBase.objects.objects.all() # this will get all of your object of your model
results = queryset.filter(Q(country_name__iexact=query)).all()
number_of_objects = results.count() # get the exact number of object to show in your html file
paginator = Paginator(results, 12) # Show 12 contacts per page
page_var = 'page' # this will use for pagination in your html file
page = request.GET.get(page_var) # this will use for pagination in your html file
contacts = paginator.get_page(page) # send only 12 object to your html file to show to user
context = {
"items": contacts,
"key": str(query),
'page': page_var,
"number_of_objects": number_of_objects,
}
return render(request=request, template_name='search.html', context=context, content_type=None, status=None,
using=None)
else:
... # if user didn't enter anything to search
After getting and searching the user input in your data base, You should show it to user in your search.html file like this:
{% for item in items %}
<div>
<div>
<div class="product_title">{{ item.title }}</div> # show the part that you want the users to see
... # rest of your item parts to show
</div>
</div>
{% endfor %}
<div class="pagination">
<span class="step-links">
{% if items.has_previous %} # check the pagination that if there is perivious pages
« first
previous
{% endif %}
<span class="current">
Page {{ items.number }} of {{ items.paginator.num_pages }} # example of result : Page 1 of 13
</span>
{% if items.has_next %}
<a href="?{{ page }}={{ items.next_page_number }}"</a> # check the pagination that if there is any next or perivious pages
last » # a link to last page
{% endif %}
</span>
{{ pagination }}
this is a basic search page with Paginator, if you need any further help or question, I will be happy to help.
The code <a class= "btn btn-primary btn-sm" href="?page={{ layers.next_page_number }}" role="button">Next</a> will indeed GET the page and the form1 = QueryForm() code will result in empty form. You are on a right track here.
You have two options:
1) Change the next/prev buttons so that they are inside the form1 form and they POST stuff. It might be challenging to move them inside the same form tag.
If you target modern browsers you can use HTML5 form tag in submit (https://www.w3schools.com/tags/att_button_form.asp).
<form method="POST" id="form1">
{{ form1|as_bootstrap }}
</form>
... outside the form tag, then
<button class="btn btn-primary btn-sm" form="form1" name="next" value="{{ layers.next_page_number }}" role="button">Next</button>
You should have in request.POST the next value.
2) Initialize the QueryForm from GET params.
form1 = QueryForm(data=request.GET)
and include the form parameters into the url. For this you would need some Javascript (for example How to use an input field as query parameter to a destination?) as Django doesn't know about the values in the form on rendering time before user inserts them.
You can change the pagination links into buttons to submit the form. More detailed answer in here:
How to define which input fields the form has by pressing different buttons?
You can read my comment on the answer. It explains how the answer can be helpful in pagination.
I modified the pagination code I found in https://simpleisbetterthancomplex.com/series/2017/10/09/a-complete-beginners-guide-to-django-part-6.html
The modified code for pagination is also as below:
<nav aria-label="Topics pagination" class="mb-4">
<ul class="pagination">
{% if page_obj.has_previous %}
<li class="page-item">
<button form="my_form" name="page" value="{{page_obj.number|add:'-1'}}" role="button" class="btn btn-link">Previous</button>
</li>
{% else %}
<li class="page-item disabled">
<span class="page-link">Previous</span>
</li>
{% endif %}
{% for i in page_obj.paginator.page_range %}
{% if page_obj.number == i %}
<li class="page-item active">
<span class="page-link">
{{ i }}
<span class="sr-only">(current)</span>
</span>
</li>
{% else %}
<li class="page-item">
<button form="my_form" name="page" value="{{i}}" role="button" class="btn btn-link">{{ i }}</button>
</li>
{% endif %}
{% endfor %}
{% if page_obj.has_next %}
<li class="page-item">
<button form="my_form" name="page" value="{{page_obj.number|add:1}}" role="button" class="btn btn-link">Next</button>
</li>
{% else %}
<li class="page-item disabled">
<span class="page-link">Next</span>
</li>
{% endif %}
</ul>
</nav>>