I'm building a simply flask application (CRUD app)
So far, I have managed to get input data from HTML page, save it to the SQLite DB and render it back to the page.
Now I want to render a delete button where I can click the button to delete the data, I wrote the code like this, I see no error when I click the button but also nothing has been deleted.
Can anyone spot any mistake or error, please?
#app.route('/users', methods=["GET", "POST"])
def users():
if request.form.get("delete"):
user_db = Users.query.filter_by(user_id=request.form.get("user_id")).first()
db.session.delete(user_db)
db.session.commit()
return redirect("user.html")
if request.form:
user_db = Users(user_name = request.form.get("user_name"),
age = request.form.get("age"),
gender=request.form.get("gender"),
activeness=request.form.get("activeness")
)
db.session.add(user_db)
db.session.commit()
user_data = Users.query.all()
return render_template("users.html", user_data = user_data)
<table>
<tr>
<th>User ID</th>
<th>User name</th>
<th>Age</th>
<th>Gender</th>
<th>Activeness<th>
</tr>
{% for i in user_data %}
<tr>
<td>{{ i.user_id }}</td>
<td>{{ i.user_name }}</td>
<td>{{ i.age }}</td>
<td>{{ i.gender }}</td>
<td>{{ i.activeness }}</td>
<td><form method="POST" action="./users">
<input type="hidden" value="{{ i.user_id }}" name="user_id">
<input type="submit" value="Delete">
</form></td>
</tr>
{% endfor %}
</table>
Aha...!
I fixed the code by writing another route for the delete button
#app.route('/users', methods=["GET", "POST"])
def users():
if request.form:
user_db = Users(user_name = request.form.get("user_name"),
age = request.form.get("age"),
gender=request.form.get("gender"),
activeness=request.form.get("activeness")
)
db.session.add(user_db)
db.session.commit()
user_data = Users.query.all()
return render_template("users.html", user_data = user_data)
#app.route('/delete_users', methods=["POST"])
def delete_user():
if request.form:
user_db = Users.query.filter_by(user_id=request.form.get("user_id")).first()
db.session.delete(user_db)
db.session.commit()
return redirect("/users")
Related
This question already has answers here:
Sending data from HTML form to a Python script in Flask
(2 answers)
Closed last year.
I'm creating a configurator app for heating systems. Essentially the idea is by putting in a few inputs - the app will spit out the part number for a system pack where a user can see a detailed bill of material (BOM).
One key element is the output where we need to show a few options. I.e. if someone needs a 200kW system, there could be 3-4 packs that are suitable (190kW -210kW might be more cost effective).
I want in the first instance to show on a route the pack options that are suitable- then the user selects the pack they want- which takes you to a route (/cart) which shows the BOM.
I have put in input variables min and max which searches a database cascades.db. This successfully shows me the table of options.
from cs50 import SQL
from flask import Flask, render_template, request, url_for, redirect
app = Flask(__name__)
db = SQL("sqlite:///cascades.db")
#app.route("/")
def index():
return render_template("index.html")
#app.route("/search", methods=["GET", "POST"])
def search():
output = request.form.get("output")
hydraulic = request.form.get("hydraulic")
layout = request.form.get("layout")
controls = request.form.get("controls")
min_output = int(output) - 15
max_output = int(output) + 15
cascades = db.execute("SELECT * FROM cascades WHERE hydraulic = ? AND layout = ? AND output BETWEEN ? and ?", hydraulic, layout, min_output, max_output)
return render_template("search.html", cascades=cascades)
#app.route("/cart", methods=["GET", "POST"])
def cart():
bom_id = request.form.get("bom_id")
bom = db.execute("SELECT * FROM bom WHERE bom_id = ?", bom_id)
return render_template("bom.html", bom = bom)
When running the app- the first bit works - i.e. it shows me a list of all the packs that meet the criteria- but when clicking the 'Choose' button Im getting stuck.
{% extends "layout.html" %}
{% block body %}
<h3> Boiler Cascade Pack Options:</h3>
<table class="table table-striped table-boardered">
<tr>
<th>Part Number</th>
<th>Description</th>
<th>Number of boilers</th>
<th>BOM</th>
</tr>
{% for cascades in cascades %}
<tr>
<td scope="cascades">{{ cascades["id"] }}</td>
<td>{{ cascades["description"] }}</td>
<td>{{ cascades["number_of_boilers"] }}</td>
<td>
<form action "/cart" method="post">
<input name="bom_id" type="hidden" value="{{ cascades["id"] }}">
<input class="btn btn-primary" type="submit" value="Choose">
</form>
</td>
</tr>
{% endfor %}
</table>
{% endblock %}
But when submitting the form- where they select the pack (which has the pack id number- as a hidden form) I get the following error:
File "/home/ubuntu/cascade2/application.py", line 26, in search
min_output = int(output) - 15 TypeError: int() argument must be a
string, a bytes-like object or a number, not 'NoneType'
It seems like the route is trying to use the same logic in the second search but at this point its redundant. The second search all I want is to show me information where the Pack id = BOM.
I've noticed the URL stays on the (/search) route with this and not going to the (/cart).
I've tried several things such as putting in a IF NOT output- redirect to Cart to try and bi pass this- which successfully loads the bill of material page but nothing comes up as I don't think its posted the id to the route.
I've also changed it to GET instead of POST, which results in the query string /search?bom_id=71723827132. Which shows its picking up the part number- but staying on the /search route where I see the error.
<h3> Boiler Cascade Pack Options:</33>
<table class="table table-striped table-boardered">
<tr>
<th>Part Number</th>
<th>Description</th>
<th>Number of boilers</th>
</tr>
{% for cascade in cascade %}
<tr>
<td scope="cascade">{{ cascades["id"] }}</td>
<td>{{ cascades["description"] }}</td>
<td>{{ cascades["number_of_boilers"] }}</td>
</tr>
{% endfor %}
</table>
<br>
<h4>Bill of material:</h4>
<br>
<table class="table table-striped table-boardered">
<tr>
<th>Product ID</th>
<th>Product Description</th>
<th>Quantity</th>
</tr>
{% for bom in bom %}
<tr>
<td scope="bom">{{ bom["part_number"] }}</td>
<td>{{ bom["product_description"] }}</td>
<td>{{ bom["quantity"] }}</td>
</tr>
{% endfor %}
</table>
Been stuck on this for a month. This to me is the last piece of the puzzle. Any help/suggestions would be great. I'm new to programming so I bet I've missed something obvious :)
Your <form action "/cart" method="post"> is missing an = after action. That means the action attribute is not properly defined, and the default for action always is the URL you're currently on. That's why it stays at /search.
Problem:
I would like to assign a bank statement for each user. Each user will log in using their username and password, and access their document. At the moment, one user can see the statement of all users.
Option 1:
I`ve added a manytomany relationship for the user to the model. When logged in on the Admin interface, I can assign the Bank Statement to the user.
Question:
What is the right way to define the views.py or the html file to only show the bank statement to the user logged-in.
models.py
class BankStatement(models.Model):
user = models.ManyToManyField(settings.AUTH_USER_MODEL,related_name='bs')
name = models.CharField(max_length=20)
date_created = models.DateTimeField(auto_now_add=True)
last_updated = models.DateTimeField(auto_now=True)
views.py
#login_required(login_url="/login/")
def bankstatement_detail(request, bankstatement_id):
bankstatement = BankStatement.objects.get(pk=bankstatement_id)
context = {
'bankstatement': bankstatement,
}
return render(request, 'administration/bankstatement_detail.html', context)
#login_required(login_url="/login/")
def index_bankstatement(request):
user = request.user
bankstatement = BankStatement.objects..filter(user=request.user)
context = {
'bankstatement': bankstatement,
}
return render(request, 'administration/bankstatement_detail.html', context)
bankstatement_detail.html
<div class="card-block table-border-style">
<div class="table-responsive">
<table class="table table-hover">
<thead>
<tr>
<th>#</th>
<th>Name</th>
<th>Date Created</th>
<th>Last Updated</th>
</tr>
</thead>
<tbody>
{% for bankstatement in bankstatements %}
<tr>
<td>{{ bankstatement.id }}</td>
<td>{{ bankstatement.name }}</td>
<td>{{ bankstatement.date_created }}</td>
<td>{{ bankstatement.last_updated }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
Many Thanks
I do not know if you are still working on this one, but
I do not agree with linking of the BankStatement to the User as ManyToMany. In my opinion, one user can have many statements, but one statement has only one user. So it is a one to many relationship defined by Foreign Key.
Let's fix the view:
#login_required(login_url="/login/")
def bankstatement_detail(request, bankstatement_id):
# this line forces to show the statement of the logged in user
user = request.user
bankstatement = BankStatement.objects.get(pk=bankstatement_id, user=user)
context = {'bankstatement': bankstatement,}
return render(request, 'administration/bankstatement_detail.html', context)
I´m new to programming, doing CS50 and currently struggling to complete the Pset9 - Finance, version from 2021. I´ve read as many threads I could find about the same issue, but none of the answers helped me to solve the problem yet.
Application.py is doing what the problem briefing required, and I passed all tests except for this one, check50 is expecting a status code 200 but I send a 400.
Check50 failed
Something to keep in mind:
Registration works as expected. I am able to register new users, and when the same username tries to register I show a 400 error.
Index function is completed, and fully functional showing all required data.
No script in Register.html to check username when pressing the submit button. I have been unable to write the right script. Not sure if this is something CS50 is expecting after all, but happy to hear anyone who has passed this test.
I would really appreciate it if someone can take a look at the code below, and let me know if I am doing something wrong or just point me in the right direction. Thanks in advance!
Here is my code for register in application.py :
#app.route("/register", methods=["GET", "POST"])
def register():
# Forget any user_id
session.clear()
# Registering a user when POST method
if request.method == "POST":
# Capturing name and password
name = request.form.get("username")
key1 = request.form.get("password")
key2 = request.form.get("confirmation")
# If fields are empty render apology
if not name:
return apology("Sorry, name is empty", 400)
elif not key1 or not key2:
return apology("Sorry, password is empty", 400)
# If keys are different render an apology
elif key1 != key2:
return apology("Sorry, passwords do not match", 400)
# Once password is valid then hash it before storing it.
key = generate_password_hash(key1, method='pbkdf2:sha256', salt_length=8)
# Checking if username exists in the database
raws = db.execute("SELECT * FROM users WHERE username = :name", name=name)
if len(raws) == 1:
return apology("Sorry, username already exists", 400)
# Include register into our database
db.execute("INSERT INTO users (username, hash) VALUES(?, ?)", name, key)
# Query again the user row, so we can pass into a session
rows = db.execute("SELECT * FROM users WHERE username = :name", name=name)
# Login the user just registered
session["user_id"] = rows[0]["id"]
# return render_template("register.html")
return redirect("/")
else:
return render_template("register.html")
Here is index in application.py :
#app.route("/")
#login_required
def index():
"""Show portfolio of stocks"""
# Query stock information from user session
stocks = db.execute("SELECT stock_symbol, stock_name, shares, value, price FROM (SELECT stock_symbol, stock_name, SUM(shares) as shares, SUM(value) as value, price FROM stocks WHERE user_id=:session GROUP by stock_symbol) WHERE shares>0", session=session["user_id"])
# Loop to sum up all stock value up
stockValue = 0
for stock in stocks:
stockValue += stock["value"]
# Query cash information from user session and send this to the html
row_cash = db.execute("SELECT cash FROM users WHERE id=:session", session=session["user_id"])
cash = row_cash[0]["cash"]
# Grand total variable adding up stock value and cash
grand_total = stockValue + cash
return render_template("index.html", stocks=stocks, cash=usd(cash), grand_total=usd(grand_total))
Here is Register.html:
{% extends "layout.html" %}
{% block title %}
Register
{% endblock %}
{% block main %}
<form action="/register" method="post">
<div class="form-group">
<input autocomplete="off" autofocus class="form-control" name="username" placeholder="Username" type="text">
</div>
<div class="form-group">
<input class="form-control" name="password" placeholder="Password" type="password">
</div>
<div class="form-group">
<input class="form-control" name="confirmation" placeholder="Repeat Password" type="password">
</div>
<button class="btn btn-primary" type="submit">Register</button>
</form>
{% endblock %}
And here is index.html:
{% extends "layout.html" %}
{% block title %}
Stocks
{% endblock %}
{% block main %}
<table class="table table-striped">
<thead>
<tr>
<th>Symbol</th>
<th>Name</th>
<th>Shares</th>
<th>Price</th>
<th>Grand Total</th>
</tr>
</thead>
<tbody>
<!-- Loop through the database entries to display them in this table -->
{% for stock in stocks %}
<tr>
<td>{{ stock.stock_symbol }}</td>
<td>{{ stock.stock_name }}</td>
<td>{{ stock.shares }}</td>
<td>{{ stock.price | usd }}</td>
<td>{{ (stock.value) | usd }}</td>
</tr>
{% endfor %}
</tbody>
<tfoot>
<tr>
<td>Overall stock value</td>
<td colspan=3></td>
<td>{{ "${:,.2f}".format(stocks|sum('value')) }}</td>
</tr>
<tr>
<td>CASH</td>
<td colspan=3></td>
<td>{{ cash }}</td>
</tr>
<tr>
<td colspan=4></td>
<td>{{ grand_total }}</td>
</tr>
</tfoot>
</table>
{% endblock %}
The issue was finally fixed when I deleted all my data in users.db thencheck50 passed it correctly. It seems that data was causing the whole problem, and once deleted everything got fixed.
The problem is in the index, where you query for stocks doesn't make sense to me
try using:
stocks=db.execute(
"SELECT * FROM stocks WHERE id = :user_id", user_id = session["user_id"])
It would also help if your index.html was visible
Working on converting an app from Flask to Django and getting stuck on how I can pull data from the value of a form button. I have some data that is rendered in a table with a for loop. Each entry has a button displayed that allows my to flip an integer field for the rendered data. Once it's flipped, it's no longer listed.
# manage.html
{% for ticket in tickets %}
<tr>
<td>{{ my_obj.id }}</td>
<td>{{ my_obj.tt }}</td>
<td>{{ my_obj.user }}</td>
<td>{{ my_obj.time }}</td>
<td><form action="/flip/" method="post">{% csrf_token %}
<input type="hidden" name="flip" value="{{ my_obj.id }}"/>
<input type="submit" class="btn btn-xs btn-danger" value="Kill"/>
</form>
</td>
</tr>
{% endfor %}
# views.py
def flip(request):
if request.method == 'POST':
tt_id = request.POST.get('value')
return HttpResponse(tt_id )
def manage(request):
my_obj = MyObject.objects.filter(status=1)
return render(request, 'manage.html', {'my_obj': my_obj})
Currently I am getting a response of None rather than the actual ID which is showing in the value field with firebug.
Thanks for looking!
You have to access the fields by the name, not with value. e.g.
request.POST.get('flip')
If you want to access the buttons value, you need to set the name attribute on it.
For details on your request it is helpful for print or return the whole request object. A debugger would be an other way to take a look at it.
I think I may have been at it too long today and I can't wrap my head around this very well. I am fairly new to python and have been playing with flask for about a week, this is my first 'real' app that I have built so please be gentle.
The app allows an internal customer (datatechs) to enter their ticket number and login into a form. That form is submitted and kept in a sqlite DB for now, with an ID, ticket#, login, create_time and an active flag.
On another page I have what could be called a management list, this renders all the tickets that are currently in the database and marked as active. I have populated this with a jinja2 for loop and have a button that resides inline.
For example:
[ 1 ] [ ticket number ] [ login ] [ button ]
[ 2 ] [ ticket number ] [ login ] [ button ]
The button is my issue. I am very unsure about how I can have the specific button clicked cause an action against that entry in the database. I am looking to flip the status field from an int of 1 to 0 (true and false works too), so these can be essentially marked as complete but still available for analytics.
Thanks for reading, here are some snippets.
views.py
#app.route('/', methods=['GET', 'POST'])
def index():
form = TicketForm()
ticket = Ticket.query.filter_by(status=1)
if request.method == 'GET':
return render_template('index.html', form=form, ticket=ticket)
elif request.method == 'POST':
if form.validate() == False:
flash('All fields are required, please update and try again.')
return render_template('index.html', form=form, ticket=ticket)
else:
remedy = Ticket(request.form['tt'], request.form['login'], create=datetime.datetime.utcnow(), status=1)
db.session.add(remedy)
db.session.commit()
flash('Your ticket has been added to the queue')
return redirect(url_for('index'))
#app.route('/manage', methods=['GET'])
def manage():
if request.method == 'GET':
ticket = Ticket.query.all()
return render_template('manage.html', ticket=ticket)
models.py
class Ticket(db.Model):
id = db.Column(db.Integer, primary_key=True)
tt = db.Column(db.String(10))
user = db.Column(db.String(20))
create = db.Column(DateTime, default=datetime.datetime.utcnow)
status = db.Column(db.Integer)
def __init__(self, tt, user, create, status):
self.tt = tt
self.user = user
self.create = create
self.status = status
manage.html
{% extends "base.html" %}
{% block content %}
<table class="table" width=50%>
<thead>
<tr>
<th>#</th>
<th>Ticket Number</th>
<th>Requester Login</th>
<th>Time Requested (UTC)</th>
<th>Cancel Request</td>
</tr>
</thead>
<tbody>
{% for ticket in ticket %}
<tr>
<td>{{ ticket.id }}</td>
<td>{{ ticket.tt }}</td>
<td>{{ ticket.user }}</td>
<td>{{ ticket.create }}</td>
<td><button type="button" class="btn btn-xs btn-danger">Kill</button></td>
</tr>
{% endfor %}
</tbody>
</table>
{% endblock %}
Generally you'd add another view to handle the flip. A note of caution is that some browser helpers preload 'normal' links on webpages to check for viruses and try helpful caching, so avoid using simple links for links that can change data. So we'll use a form instead:
change your <td><button type="button" class="btn btn-xs btn-danger">Kill</button></td> to:
<td>
<form action="{{ url_for('flip') }}" method="post">
<input type="hidden" name="flip" value="{{ ticket.id }}"/>
<input type="submit" class="btn btn-xs btn-danger" value="Kill"/>
</form>
</td>
Then add a view to do the heavy lifting:
#app.route('/flip', methods=['POST'])
def flip():
ticket = Ticket.query.filter_by(id=request.form["flip"]).first_or_404()
ticket.status = 0
db.session.commit()
return redirect(url_for('index'))
So we're just catching the id and seeing if it exists in the database, and if so, we'll flip the status to 0 and then redirect (from flask import redirect) the user back to the index view.