I have SQLite database which contains data regarding products such as Product Name, Description and Like which shows if the user likes or doesn't like the product.
A user search for different products which populate a table with the name, description and a checkbox which is clicked in if the value of Like is 1 in the database and unchecked if not.
I have implemented the button through (as seen in index.html)
<form method = "POST"> <input type="checkbox" name="like" {% if product.like == 1 %} checked {% else %} {% endif %}>
I now want the user to be able to check and uncheck the boxes in the table, and then press a button that updates the Like column in the database, how do I do this?
(I can't even access the value of the button in app.py. When trying print(requests.form['like']) on the line after name=requests.form['search'] it returns BadRequestKeyError: The browser sent a request that this server could not understand. KeyError: 'like')
app.py
...
product = []
#app.route('/', methods = ['POST'])
def index():
name = None
if request.method == 'POST':
name = request.form['search']
if name is not None or name != "":
query_product = Products.query.filter(Products.productname==name).first()
if (query_product is not None) and (query_product not in product):
company.append(query_company)
print(company, file = sys.stderr)
return render_template('index.html', companies = company)
Products class
class Products(db.Model):
index = db.Column(db.Integer(), primary_key = True)
productname = db.Column(db.String(), primary_key = False)
description = db.Column(db.String(), primary_key = False)
like = db.Column(db.Integer(), primary_key = False)
...more columns
index.html
<!-- /templates/index.html -->
{% extends 'base.html' %}
{% block content %}
<form method="POST">
<input type="text" placeholder="Search for Product.." name="search">
<button type="submit" name="submit">Search</button> <button type="submit" name="update" value = company.index style = margin-left:45%>Update</button>
<div class="product-container" style="overflow: auto; max-height: 80vh">
<div class="table-responsive">
<table class="table" id="products">
<thead>
<tr>
<th scope="col">Product Name</th>
<th scope="col">Description</th>
<th scope="col">Like</th>
</tr>
</thead>
<tbody>
{% for product in products %}
<tr {{company.index}}>
<th scope="row">{{ product.productname}}</th>
<td> {{ product.description }} </td>
<td><form method = "POST"> <input type="checkbox" name="like" {% if product.like == 1 %} checked {% else %} {% endif %}></form></td>
{% endfor %}
</tbody>
</table>
</div>
</div>
{% endblock %}
I guess you need to do this with javascript and add another route to your backend which then updates the database.
maybe something like this, if it should happen automatically:
<input type="checkbox" onchange="updateLike('productId', this.checked)">
<script>
async function updateLike(productId, doesLike) {
let response = await fetch("http://localhost/products/productId/like", {
method:"POST",
headers: {"Content-Type":"application/json"},
body: JSON.stringify({
productId: productId,
like: doesLike
})
});
}
</script>
or you could add a button which sends the request to the server.
<input type="checkbox" name="like"/>
<button onclick="updateLike('productId', document.querySelector('input[name=like]').checked)">confirm</button>
Related
I have been trying to make an 'update function' in my table so that my data can be changed by a user.
So here is my html with the form where i want my data to go back in to, so when the user presses submit again it saves that value to the database:
<h3>Add a location </h3></br></br>
<div class="addition">
<form method="POST" action="" autocomplete="off">
{% csrf_token %}
<div class="row">
<div class="col">
<input type="text" class="form-control" aria-describedby="emailHelp" placeholder="Location name" name="name" autocomplete="off">
</div>
<div class="col">
<input type="text" class="form-control" aria-describedby="emailHelp" placeholder="Description" name="desc" autocomplete="off">
</div>
<div class="col">
<button type="submit" class="btn btn-primary">Submit</button>
</div></br></br>
</form> </br></br>
<h3>Overview locations</h3>
<div class="table-responsive">
<table class="table table-striped table-sm">
<thead>
<tr>
<th>#</th>
<th>Name</th>
<th>Description</th>
<th>Description</th>
</tr>
</thead>
<tbody>
{% for item in all_locations %}
<tr>
<td>{{ forloop.counter }}</td>
<td>{{ item.name }}</td>
<td>{{ item.desc }}</td>
<td><a class="btn btn-sm btn-info" href="{% url 'update_location' item.id %}" >update</a></td>
</tr>
my views.py with my creation function and my update function
def location(request):
all_locations = Location.objects.all()
if request.method == "POST":
loc_form = LocationForm(request.POST or None)
if loc_form.is_valid():
loc_form.save()
return render(request, 'location.html', {"all_locations": all_locations})
else:
return render(request, 'location.html', {"all_locations": all_locations})
return render(request, 'location.html', {"all_locations": all_locations})
def updateLocation(request, pk):
all_locations = Location.objects.all()
location = Location.objects.get(id=pk)
loc_form= LocationForm(instance=location)
name = loc_form.instance.name
desc = loc_form.instance.desc
print(name)
print(desc)
return render(request, 'location.html', {"all_locations": all_locations, "name": name, "desc": desc})
my models.py
class Location(models.Model):
creation_date = models.DateTimeField(auto_now = True)
name = models.CharField(max_length=50, default=None)
desc = models.CharField(max_length=250, default="", null=True, blank=True)
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
and my urls.py
urlpatterns = [
path('', views.index, name="index"),
path('locations', views.location, name="location"),
path('locations/<str:pk>', views.updateLocation, name="update_location"),
]
So basically i'm stuck at putting the old data back into the html input.
I've tried with a different view as well but i had basically the same issue.
Any suggestions? Thanks a lot!
I figured it out whilest doing something else:
add value="{{ name }}" to the html file on the input line. Do the similar thing for description
I am trying to create a form with an embedded table that the user can dynamically add and remove table rows while entering content into the cell inputs.
HTML
<form id="myForm" action="{{ url_for('hello_world') }}" method="POST">
<div class="form-row text-left">
<div class="col-1 text-left">
<input type="checkbox" id="skills" name="skills" value="Yes">
</div>
<div class = "col-11 text-left">
<h2>TECHNICAL SKILLS</h2>
</div>
</div><!--form-row-->
<div class="form-row">
<table id="myTable" name="skillsTable">
<tr>
<th>Category</th>
<th>Entries</th>
</tr>
</table>
</div><!--form-row-->
<br>
<button type="button" onclick="addSkill()">Add Row</button>
<button type="button" onclick="deleteSkill()">Delete row</button>
<hr>
<input type="submit" value="Submit" onclick="submit()" />
</form>
As you can see in the screenshot [![screenshot of the user form][1]][1] the name attribute is correctly being appended to added cell.
The goal is to have a way to get the table values dynamically created by the user over to the flask template where they can be displayed.
Javascript
<script>
var c1=0;
var c2=0;
function addSkill() {
var table = document.getElementById("myTable");
var row = table.insertRow(-1);
var cell1 = row.insertCell(0);
var cell2 = row.insertCell(1);
cell1.innerHTML = "<input type='text' value=' ' name=cell1_"+c1.toString()+"> ";
cell2.innerHTML = "<input type='text' value=' ' name=cell2_"+c2.toString()+"> ";
c1++;
c2++;
}
function deleteSkill() {
document.getElementById("myTable").deleteRow(-1);
}
</script>
I have tried setting the name attribute for each newly created cell using a counter, but this still does not show up rendered in the flask template:
flask
#app.route('/hello_world', methods=['GET', 'POST'])
def hello_world():
if request.method == 'POST':
result = {}
try:
skills = request.form['skills']
result['skills'] = skills
result['value'] = request.form['cell1_1']
except:
pass
return render_template("result.html",result = result)
result.html
{% if result.skills %}
<p>{{ result.value }}</p>
{% endif %}
In this example, I would expect to see "Language" show up on rendered after submitting the form if the checkbox is selected.
How can I refer to the table in the form from flask and loop through the <input> elements if they are dynamically created? Thx
[1]: https://i.stack.imgur.com/samhG.png
result.html
{% if result.skills %}
{% set skillsTable = result.skillsTable %}
<h2>TECHNICAL SKILLS</h2>
<table>
{% for skill in skillsTable %}
{% if loop.index|int % 2 == 0 %}
<tr><td>{{ skillsTable.pop(0) }}:</td><td>{{ skillsTable.pop(0) }}</td></tr>
{% else %}
<tr><td>{{ skillsTable.pop(0) }}:</td><td>{{ skillsTable.pop(0) }}</td></tr>
{% endif %}
{% endfor %}
{% endif %}
flask
#app.route('/hello_world', methods=['GET', 'POST'])
def hello_world():
if request.method == 'POST':
result = {}
try:
skills = request.form['skills']
result['skills'] = skills
result['skillsTable'] = []
form = request.form
for key, value in form.items():
if key.startswith("cell"):
result['skillsTable'].append(value)
except:
pass
return render_template("result.html",result = result)
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.
I have an HTML table which pulls data from an SQLAlchemy database.
In this table, for each row, I have a Delete button, which should delete that row and database entry when I click on it.
The issue I have, is that if I try to delete any row in this table, it deletes only the first row (see image below) and the associated data in the database.
The row and the data I wanted deleted remain unchanged (the 3rd row in the image below).
If I click Delete on the first row in the table, it works without issues.
The Update button works as indented (updating the correct entry)
This is the database model I have:
class ActualPost(db.Model):
__tablename__ = 'actualpost'
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
title_actual = db.Column(db.String(30), nullable=False, default='actual')
category_actual = db.Column(db.String(30), nullable=False, default=None)
actual_amount_name = db.Column(db.String(30), nullable=True)
actual_amount = db.Column(db.Float, nullable=False)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
date_posted = db.Column(db.DateTime, nullable=False, default=datetime.utcnow)
comments = db.Column(db.Text, nullable=True)
def __repr__(self):
return f"ActualPost('{self.title_actual}, '{self.category_actual}'\
, '{self.actual_amount_name}', '{self.actual_amount}'\
, '{self.date_posted}', '{self.comments}')"
Here are my routes (Update and Delete)
#posts.route("/post/<int:post_id>/update_actual", methods=['GET', 'POST'])
#login_required
def update_actual_post(post_id):
post = ActualPost.query.get_or_404(post_id)
if post.actual_author != current_user:
abort(403)
form = PostActualForm()
if form.validate_on_submit():
post.title_actual = form.title_actual.data
post.category_actual = form.category_actual.data
post.actual_amount_name = form.actual_amount_name.data.name
post.actual_amount = form.actual_amount.data
post.comments = form.comments.data
db.session.commit()
flash('Your post has been updated!', 'success')
return redirect(url_for('main.actual', post_id=post.id))
elif request.method == 'GET':
form.title_actual.data = post.title_actual
form.category_actual.data= post.category_actual
form.actual_amount_name.data = post.actual_amount_name
form.actual_amount.data = post.actual_amount
form.comments.data = post.comments
return render_template('create_actual_post.html', title='Update Actual',
form=form, legend='Update Actual')
#posts.route("/post/<int:post_id>/delete_actual", methods=['POST'])
#login_required
def delete_actual_post(post_id):
post = ActualPost.query.get_or_404(post_id)
if post.actual_author != current_user:
abort(403)
db.session.delete(post)
db.session.commit()
flash('Your post has been deleted!', 'success')
return redirect(url_for('main.actual', post_id=post.id))
This is the HTML code I use for the Delete button:
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Close</button>
<form action="{{ url_for('posts.delete_actual_post', post_id = post.id) }}" method="post">
<input class="btn btn-danger" type="submit" value="Delete">
</form>
</div>
Here is my code to delete particular row by id. You can co-relate it with your code
SQL-alchemy Modal :
class Componants(db.Model):
__tablename__ = 'componants'
id = db.Column(db.Integer, primary_key=True)
cmp_options = db.Column(db.Text, default=None)
updated_at = db.Column(db.DateTime, nullable=True)
created_at = db.Column(db.DateTime, nullable=True)
def __init__(self , cmp_options ):
self.cmp_options = cmp_options
self.created_at = datetime.now()
self.updated_at = datetime.now()
def __repr__(self):
return '<id {}>'.format(self.id)
Controller :
#componants_blueprint.route('/delete_component/<component_id>')
#login_required
def delete_component(component_id):
component = Componants.query.filter_by(id=component_id).first_or_404()
db.session.delete(component)
db.session.commit()
return True
View:
<tbody>
{% for item in all_components %}
<tr>
<td>{{ item.id }}</td>
<td>{{ item.cmp_options }}</td>
<td>{{ item.created_at }}</td>
<td class="text-center">
</td>
</tr>
{% endfor %}
</tbody>
The above did not help me.
But the comments in this post did help me.
In short: the problem is inside the HTML tags. I used a 'modal' from the Bootstrap.
The comments in this page suggested to inspect the page elements.
Also in your chrome/firefox developer tools, make sure you check that form's action property and ensure that its correct in the case of that delete button. –
Prahlad Yeri
Jun 10, 2018 at 23:48
The inspection showed that each row in the table did create 'some' unique features such as the name of the item I like to delete. However, during 'inspect' I clicked on my delete button. This showed that the action was stuck in the first line of the table. It did not jump to the line where I clicked 'delete'.
What is inside the HTML generating the issue? Is it the row or is it the modal?
I noticed that the html tag for the modal 'id' was the same for all rows in my table. Question: shouldn't it be unique for each row? I guess, yes.
So, I added the jinja tag to my ID. Would it work? Yes, for me it did...
{% block table %}
<table id="data" class="table table-striped">
<thead>
<tr>
<th scope="col">Name</th>
<th scope="col">Latitude</th>
<th scope="col">Longitude</th>
<th scope="col">Jars</th>
<th scope="col">Actions</th>
</tr>
</thead>
<tbody>
{% for map in maps %}
<tr>
<td>{{ map.map_name }}</td>
<td>{{ map.latitude }}</td>
<td>{{ map.longitude }}</td>
<td><span style="white-space: pre-line">{% for jar in map.jar_id %}
{{ jar.jar_name }}
{% endfor %}
</span>
</td>
<td><!-- Button trigger modal -->
<button type="button" class="btn btn-warning" data-bs-toggle="modal" data-bs-target="#{{map.map_name}}Modal">
Delete
</button>
<!-- Modal -->
<div class="modal fade" id="{{map.map_name}}Modal" tabindex="-1" aria-labelledby="{{map.map_name}}ModalLabel" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="{{map.map_name}}ModalLabel">Are you sure?</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div class="modal-body">
<p>Are you sure to delete the item : {{ map.map_name }}</p>
</div>
<div class="modal-footer">
Yes
No
</div>
</div>
</div>
</div>
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% endblock %}
I would like to add data filled into database and output it. But i have no idea where is wrong because my data was't saved into database at all. In views.py, Scholarship is just one scholarship object, LScholarship is displaying all the data in Scholarship. I have similar code for other models and views but i have no idea what i did wrong in here, making the data can't be saved into database. Could anyone please advice me where am i wrong
add_remove_scholarship.html
<div align="center" >
<form method="POST" onsubmit="return validation()" action="">
{% csrf_token %}
{{ form.errors }}
<p>Upload File: {{scholarship.doc}} <input id="doc" type="text" name="doc"> </p>
<p>Faculty: {{scholarship.faculty}} <input id="faculty" type="text" name="faculty"> </p>
<p>Opening date: {{scholarship.openDate}} <input id="odate" type="date" name="openDate"> </p>
<p>Closing date: {{scholarship.closeDate}} <input id="edate" type="text" name="closeDate"> </p>
<input type="submit" name="AddScholarship" value="Add Scholarship" >
</form>
</div>
<br></br>
<button id="button" type="button">Delete Selected Scholarship</button>
<br></br>
<form method="POST" action="">
{% csrf_token %}
<table id="example" class="display" cellspacing="0" width="100%" border="1.5px">
<tr align="center">
<th> Scholarship </th>
<th> Faculty </th>
<th> Open Date </th>
<th> Close Date </th>
</tr>
{% for item in query_results %}
<tr align="center">
<td>{{item.doc}}</td>
<td>{{item.faculty}}</td>
<td>{{item.openDate}}</td>
<td>{{item.closeDate}}</td>
</tr>
{% endfor %}
</table>
</form>
models.py
#consists of all the details of the scholarship under the 'Add/Remove
Scholarship'
class Scholarship(models.Model):
doc = models.TextField("Doc", max_length=1000)
faculty = models.TextField("Faculty", max_length=1000)
openDate = models.DateField("Opening Date", max_length=8)
closeDate = models.TextField("CLosing Date", max_length=18)
def __str__(self):
return self.doc
#consists of all the details of the scholarship under the 'Add/Remove
Scholarship'
class LScholarship(models.Model):
scholarship = models.ForeignKey(Scholarship, on_delete=models.CASCADE)
views.py
def scholarship(request):
if request.method == "POST":
form = ScholarshipForm(request.POST)
if form.is_valid():
scholarship = form.save(commit=False)
scholarship.save()
else:
form = ScholarshipForm()
return render(request, 'hrfinance/add_remove_scholarship.html', {'form': form})
def lscholarship(request):
query_results = Scholarship.objects.all()
data={'query_results':query_results}
return render(request, 'hrfinance/add_remove_scholarship.html', data)
class ScholarshipForm(forms.ModelForm):
class Meta:
model = Scholarship
fields = '__all__'
You are using two views to render a single template.
You can merge your views into a single one and maybe reduce the logic.
If I have understood your problem correctly you could use your view more like this,
def scholarship(request, id=None):
query_results = []
if request.method == "POST":
form = ScholarshipForm(request.POST)
if form.is_valid():
scholarship = form.save(commit=False)
scholarship.save()
else:
form = ScholarshipForm()
id = request.GET.get('scholarship')
query_results = Scholarship.objects.all()
data = {
'query_results':query_results,
'form': form,
}
return render(request, 'hrfinance/add_remove_scholarship.html', data)
In your template,
<div align="center" >
<form method="POST" onsubmit="return validation()" action="">
{% csrf_token %}
{{ form.errors }}
<p>Upload File: {{ form.doc }}</p>
<p>Faculty: {{ form.faculty }} </p>
<p>Opening date: {{ form.startDate }} </p>
<p>Closing date: {{ closeDate }} </p>
<button type="submit" name="AddUser" value="Add Scholarship" onclick="add()" >Add Scholarship</button>
</form>
</div>
<table id="example" class="display" cellspacing="0" width="100%" border="1.5px">
<tr align="center">
<th> Scholarship </th>
<th> Faculty </th>
<th> Open Date </th>
<th> Close Date </th>
</tr>
{% for item in query_results %}
<tr align="center">
<td>{{item.doc}}</td>
<td>{{item.faculty}}</td>
<td>{{item.openDate}}</td>
<td>{{item.closeDate}}</td>
</tr>
{% endfor %}
</table>