Flask - Submit Button Actioning Database Update - python

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.

Related

Can’t loop value in html table

I’m building a function which can let admin to delete account in that page.
I have several accounts in admin page but all the delete buttons are holding a value from first account in that page instead of other account.
<form action="/admin" method="post">
<table class="table">
<thead class="table-dark">
<tr>
<th>Users</th>
</tr>
</thead>
{% for user in users %}
<tr style="background-color:burlywood">
<td><input type="hidden" name="user" value="{{user.username}}">{{user.username}}</td>
<td><input type="submit" for="user" value="Delete"></td>
{% endfor %}
</tr>
</table>
#app.route("/admin", methods=["GET", "POST"])
#login_required
def admin():
"""Manage account"""
if request.method == "GET":
user_id = session["user_id"]
admin = db.execute("SELECT id FROM users WHERE id = (?)",
user_id)
for row in admin:
if int(row["id"]) == 17:
users = db.execute("SELECT username FROM users")
return render_template("admin.html", users = users)
else:
return apology("Only Admin can access this page!")
return apology("valid return")
else:
username = request.form.get("user")
flash(f"{username} has been deleted!")
return redirect("/")
I expected each delete button is holding each value in column.
After the issue is fixed, when delete button is clicked flash function will show the different username based on the value not admin at all.
here is html page
From the tags I believe you're using Jinja to load and render the html page, however, you should be more clear about what you want to ask.
This may sound like a bad idea and definitely something you shouldn't do in production but how about creating different forms for each user?
Something like this :
<table class="table">
<thead class="table-dark">
<tr>
<th>Users</th>
</tr>
</thead>
{% for user in users %}
<form action="/admin" method="post">
<tr style="background-color:burlywood">
<td><input type="hidden" name="user" value="{{user.username}}">{{user.username}}</td>
<td><input type="submit" for="user" value="Delete"></td>
</tr>
</form>
{% endfor %}
</table>
I think your line only returns usernames in your users variable:
users = db.execute("SELECT username FROM users")
So users in your template is an array that only contains a list of names, or only contains one name.
1 / Whats contains users?
2 / what if you put {{ user }} instead of {{ user.username }} in your template?

Flask application - delete function isn't working

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

CS50(2021) - Finance. :( registration rejects duplicate username - expected status code 200, but got 400

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

How to compare user input to corresponding model field django

I'm trying to make learning app for myself and part of my plan is to make a quiz module.
My problem is, that I don't know how to compare user answer with the correct answer that's stored in the model.
Now, the only thing I've tried (except reading docs and stack overflow) was to inject the related model question inside of my HTML to later use in views.py, but from the beginning I felt like that's not the way it should work, so I guess I have to reorganize my models/forms or inside of views.py there is some way to query database for that specific model instance that I don't know.
Here is my code
Models:
class Question(models.Model):
question = models.CharField(max_length=100, unique=True)
answer = models.CharField(max_length=100, unique=False)
def __str__(self):
return self.question
Forms:
class Answer(forms.Form):
answer = forms.CharField()
Views:
def quiz(request):
questions = Question.objects.order_by('question')
form = Answer()
context_dict = {'form':form,'questions':questions}
if request.method == 'POST':
form = Answer(request.POST)
if form.is_valid():
#Here I want to make the comparison
pass
return render(request,"quiz_app/quiz.html",context_dict)
HTML:
<table>
{% for q in questions %}
<tr>
<td>{{ q.question }}</td>
<form method="POST">
<td>{{ form.answer }}</td>
{% csrf_token %}
<td>
<input type="submit" value="submit">
</td>
</form>
</tr>
{% endfor %}
</table>
You can pass the question_id with the post request and then get the question instance and compare the results.
HTML:
<form method="POST">
<td>{{ form.answer }}</td>
{% csrf_token %}
<input type="hidden" name="q_id" value="{{ q.id }}" />
<td>
<input type="submit" value="submit">
</td>
</form>
views:
def quiz(request):
questions = Question.objects.order_by('question')
form = Answer()
context_dict = {'form':form,'questions':questions}
if request.method == 'POST':
instance = Question.objects.get(id=request.POST['q_id'])
form = Answer(request.POST, instance=instance)
if form.is_valid():
#Here I want to make the comparison
if request.POST.get("answer").strip() == instance.answer:
# used strip() to remove whitespace before and after the text.
# other logic.
return render(request,"quiz_app/quiz.html",context_dict)

reload page automatically without needing to RE-enter URL again Django

My webpage consists of 2 parts, upper part is a section that let user to enter data while bottom part displays all the data in database in a table form. When user selects "Add" button in the upper part, the data will be saved into database and being outputted in a table in bottom part of the webpage. Is there anyway to show the table once i select the "Add" button ? Right now what the code is doing is when "Add" button is being selected, it will load a new form but then the whole table will disappear. I have to manually type this address again "http://127.0.0.1:8000/hrfinance/lscholarship/" then only the table will appear. Even with refreshing the page will not work. Below is my code in
views.py:
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)
models.py
class Application(models.Model):
studentID = models.CharField("Student ID", max_length=8, validators=[MinLengthValidator(8)], primary_key=True, default="")
studentName = models.CharField("Student Name", max_length=500, default="")
scholarship = models.TextField("Scholarship")
add_remove_scholarship.html
<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: <input id="odate" type="date" name="openDate"> </p>
<p>Closing date: {{ form.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>
{{query_results}}
<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>
Best practice is to use the Post/Redirect/Get pattern. This will solve your problems as well: the GET request will clear the form and show the new query results.
If the form is successfully saved, you simply return a redirect to the current page. The browser will then do a GET request. This prevents accidental duplicate form submissions when e.g. the user reloads the current page when it is still a POST request:
from django.shortcuts import redirect
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()
# Return a redirect to the same page
return redirect('/path/to/current/page/')
...
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:
id = request.GET.get('scholarship')
query_results = Scholarship.objects.all()
form = ScholarshipForm()
data = {
'query_results':query_results,
'form': form
}
return render(request, 'hrfinance/add_remove_scholarship.html', data)
you need to move the query out side the else condition.
Although, this method would take a lot of time as the number of rows in the database increases. A better method to do this would be to use jquery Ajax method to update the data in the database and show it dynamically using javascript/Jquery once the database is updated.

Categories

Resources