I'm trying to build a web app using Python Flask that can perform CRUD operations for a database that already exists in PostgreSQL. Below I'm trying to perform the "Update Operation"
Here is my table for the database;
class Intern(db.Model):
intern_id = db.Column(db.Integer, primary_key=True)
intern_name = db.Column(db.String(150), nullable=True)
date_created = db.Column(db.DateTime, default=datetime.utcnow)
def __repr__(self):
return '<Name %r>' % self.intern_id
Following is my update function in "app.py";
#app.route('/update/<int:intern_id>', methods=['POST', 'GET'])
def update(intern_id):
intern_to_update = Intern.query.get_or_404(intern_id)
if request.method == "POST":
intern_to_update.intern_name = request.form['name']
try:
db.session.commit()
return redirect('/successful')
except:
return "There was a problem updating... Please try again!"
else:
return render_template('update.html', intern_to_update=intern_to_update)
Following is my successful function in "app.py". This function creates the intern.
#app.route('/successful', methods=['POST', 'GET'])
def successful():
if request.method == "POST":
intern_name = request.form["intern_name"]
new_intern = Intern(intern_name=intern_name)
if not intern_name:
show_error = "You need to fill out the forms required. Please try again."
return render_template("fail.html", show_error=show_error)
try:
db.session.add(new_intern)
db.session.commit()
return redirect('/successful')
except:
return "There was an error adding the new intern! Please try again"
else:
interns = Intern.query.order_by(Intern.date_created)
return render_template("successful.html", interns=interns)
Here is my "update.html" file;
{% extends 'base.html' %}
{% block title %}Update{% endblock %}
{% block content %}
<div class="container">
<h1>Update Operation</h1>
<br/><br/>
<form action="/update/{{intern_to_update.intern_id}}" method="POST">
<div class="mb-3">
<b><p> {% block info %} Please enter the credentials below {% endblock %} </p></b>
</div>
<div class="mb-3">
<input type="text" placeholder="Intern Name" name="name" value="{{intern_to_update.name}}">
</div>
<button type="submit" class="btn btn-primary" value="Update Name">Create</button>
</form>
<br/>
<div class="mb-3">
<b><p> Thank you, your operation is successfully completed! </p></b>
</div>
</div>
{% endblock %}
Lastly, below is where the update link is coming from in "base.html" file;
<li class="nav-item">
<a class="nav-link" href="{{url_for('update', intern_id = intern_to_update.intern_id)}}">Update</a>
</li>
There are no errors whatsoever with creating an intern. So my "successful" function is working without any mistakes. But I can't even see if my "update" function is working. Can someone please tell me why I'm getting this error I couldn't figure out? I checked other solutions for other users, which is usually adding "return render_template" for ELSE (GET) part for their code but I already did that.
Related
I'm trying to create an edit form in flask, to compliment my working add and delete form - but I'm getting an error about items not being defined.
The template I have:
% extends "main/_base.html" %}
{% block title %}Edit Item{% endblock %}
{% from "main/_formshelper.html" import render_field %}
{% block content %}
<form action="{{ url_for('main.edit_item', items_id=item.Items.id) }}" method="post" name="edititemsform">
{{ form.csrf_token }}
<div class="form-group">
{{ form.name.label }}
{{ render_field(form.name, class="form-control", value=item.name) }}
</div>
<div class="form-group">
{{ form.notes.label }}
{{ render_field(form.notes, class="form-control", value=item.notes) }}
</div>
<input type="submit" value="Edit Item" class="btn btn-lg btn-success">
</form>
{% endblock %}
The view that drives it:
#main_blueprint.route("/edit_item/<int:items_id>", methods=["GET", "POST"])
def edit_item(items_id):
form = EditItemsForm(request.form)
item_with_user = db.session.query(Items).filter(Items.id == items_id).first()
if item_with_user is not None:
if request.method == 'POST':
if form.validate_on_submit():
try:
item = Items.query.get(items_id)
item.name = form.name.data
item.notes = form.notes.data
db.session.commit()
flash('Item edited')
return redirect(url_for('main.all_items'))
except:
db.session.rollback()
flash('Item edit error')
return render_template('main/edit_item.html', item=item_with_user, form=form)
else:
flash('Item does not exist')
return redirect(url_for('main.all_items'))
The error I get is :
jinja2.exceptions.UndefinedError: 'app.main.models.Items object' has no attribute 'Items'
This is my model which I think is the root of the issue, is it that is looking for Items inside my model Items?
from app import db
class Items(db.Model):
__tablename__ = 'items'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String, nullable=False)
notes = db.Column(db.String, nullable=True)
def __init__(self, name, notes):
self.name = name
self.notes = notes
def __repr__(self):
return '<id {}>'.format(self.id)
When creating the URL for the form's action parameter, you are referencing an incorrect attribute. You've already passed an Items object to the template and now you just need to request the id attribute and pass it to url_for.
<form
action="{{ url_for('main.edit_item', items_id=item.id) }}"
method="post"
name="edititemsform"
>
<!-- ... -->
</form>
In order not to manually assign a value to every input field within the template, I recommend that you simply assign the object to be edited to the form. The validate_on_submit function then checks whether the request is of the POST type and whether the input is valid. Then you can use populate_obj to update all existing attributes. The database entry is then saved by commit.
#main_blueprint.route("/edit_item/<int:items_id>", methods=["GET", "POST"])
def edit_item(items_id):
item = Items.query.get_or_404(items_id)
form = EditItemsForm(request.form, obj=item)
if form.validate_on_submit():
form.populate_obj(item)
try:
db.session.commit()
flash('Item edited')
return redirect(url_for('main.all_items'))
except:
db.session.rollback()
flash('Item edit error')
return render_template('main/edit_item.html', **locals())
I'm working on a web app right now and I don't know how to display an error message when the user types in an invalid log in name. How do I go about this?
this is my login.html
{% block body %}
<h1>Login</h1>
{% if error %}
<p>Invalid Username</p>
{% endif %}
<form method="POST" action="{{ url_for('login') }}">
<input type="text" name="username" placeholder="Your Username">
<input type="submit" value="Submit">
</form>
<a href='{{variable6}}'>Sign Up</a>
{% endblock %}
this is my app.py
#app.route("/login", methods=["GET", "POST"])
def login():
if flask.request.method == "GET":
return flask.render_template("login.html")
if flask.request.method == "POST":
username = flask.request.form["username"]
cursor.execute(
"SELECT user_name FROM public.users WHERE user_name = %s", [username]
)
results = cursor.fetchall()
if len(results) != 0: # if a user exists, "log" them in
return flask.redirect(flask.url_for("main"))
else:
return flask.render_template("login.html")
I believe that your problem is that you are not defining what the "error" variable is, you could fix this by when you are returning the render_template in the last line of app.py, adding error=True, leaving you with this as your last line for app.py.
return flask.render_template("login.html", error=True)
As otherwise Jinja (the templating language you used in login.html) would not know what error and would get the type None as the value of error in the {% if error %} statement in login.html and since None is not equal to true, it would skip over the code you want to run.
Hope this helps :)
I am trying to build a simple web app, which has a form to change one's password. I am using werkzeug.security functions (check_password_hash and generate_password_hash) to do so. This two functions work perfectly in during registering and logging in. But for some reason, when I change password, the password just doesn't match. I even wrote a code to check the password right away, passwordChange = check_password_hash(newHash, newPassword), then print(f'\n\n{passwordChange}\n\n')but for some reason it always returned false. Here is the full code. Any response is greatly appreciated :)
FLASK
#app.route("/passwordchange", methods=["GET", "POST"])
#login_required
def changepassword():
""""Change users' password"""
if request.method == "POST":
newPassword = request.form.get("newPassword")
newConfirmation = request.form.get("newConfirmation")
# Ensure that the user has inputted
if (not newPassword) or (not newConfirmation):
return apology("Please fill all of the provided fields!", 400)
# Check to see if password confirmation were the same or not
if newPassword != newConfirmation:
return apology("password did not match with password (again)", 400)
user_id = session["user_id"]
newHash = generate_password_hash("newPassword")
db.execute("UPDATE users SET hash = ? WHERE id = ?", newHash, user_id)
passwordChange = check_password_hash(newHash, newPassword)
print(f'\n\n{passwordChange}\n\n')
return redirect("/login")
else:
return render_template("password.html")
HTML
{% extends "layout.html" %}
{% block title %}
Change Password
{% endblock %}
{% block main %}
<form action="/passwordchange" method="post">
<div class="form-group">
<input class="form-control" name="newPassword" placeholder="New Password" type="password">
</div>
<div class="form-group">
<input class="form-control" name="newConfirmation" placeholder="New Password (again)" type="password">
</div>
<button class="btn btn-primary" type="submit">Change Password</button>
</form>
{% endblock %}
What I am trying to do is to trigger the function get_lyrics() when the Submit button is clicked. That action might run successfully which then triggers another action, or it might fail showing instead 'API calls reached. Try again later.'
However, as soon as I load the page, the text 'API calls reached. Try again later.' is already there whereas it should only appear when the function that is triggered fails.
This is my app.py:
import os
import json
from flask import Flask, render_template, request, session
from src.get_lyrics import get_lyrics
app = Flask(__name__)
#app.route('/')
def web_page():
return render_template('simple.html')
#app.route('/artist', methods=['POST'])
def insert_text():
if request.method=='POST':
artist_name = request.form.get("artist")
number_songs = request.form.get("number")
try:
titles, lyrics, no_of_songs = get_lyrics(artist = artist_name,
max_no_songs = number_songs,
path_to_txt="C:/Users/test_new.txt")
train = True
except:
train = False
titles= None
no_of_songs = None
return render_template('simple.html', titles=titles, no_songs=no_of_songs, train=train)
and this is the html bit:
<!DOCTYPE html>
<link href="static/simple.css" rel="stylesheet" type="text/css"</link>
<html>
<div class="image"></div>
<body>
<div class="gray-block-1">
<div class="block-1">
<h2>Instructions</h2>
</div>
<div class="block-2">
<form action = "./artist" method = "POST">
<input type="text" placeholder="Artist name" name="artist" />
<input type="text" placeholder="Number of songs" name="number" />
<div class="submit-button">
<button type = "submit"> Submit</button>
</div>
</form>
</div>
</div>
<div class="gray-block-2">
{% if train %}
{% for title in titles %}
<p>{{title}}</p
{% endfor %}
<form action = "./train" method = "POST">
<input type = "text" placeholder="Number of lines" name = "lines" />
<div class="submit-button-2">
<button type = "predict"> Predict</button>
</div>
</form>
{% else %}
API calls reached. Try again later.
{% endif %}
</div>
</body>
</html>
When simple.html is loaded for the first time with a GET request, the variable train is not defined. if train will therefore return False and the text in the else statement will be shown: “API calls reached. Try again later.”
You could set another variable in the backend.
#app.route('/artist', methods=['POST'])
def insert_text():
…
return render_template('simple.html', is_post=True, …)
And update the front end accordingly.
<div class="gray-block-2">
{% if is_post %}
{% if train %}
…
{% else %}
API calls reached. Try again later.
{% endif %}
{% endif %}
</div>
There is probably a better way. I'm just trying to point you in the right direction.
I want to make some kind of search engine for student's information by entering their first name in html input field, but I have some troubles with my code. I am using Flask with Python though.
Here is my project.py code:
#app.route('/search', methods=['GET', 'POST'])
def search():
if request.method == "POST":
db = MySQLdb.connect(user="root", passwd="", db="cs324", host="127.0.0.1")
c=db.cursor()
c.executemany('''select * from student where name = %s''', request.form['search'])
for r in c.fetchall():
print r[0],r[1],r[2]
return redirect(url_for('search'))
return render_template('search.html')
Here is my search.html code:
{% extends "hello.html" %}
{% block content %}
<div class="search">
<form action="" method=post>
<input type=text name=search value="{{ request.form.search}}"></br>
<div class="actions"><input type=submit value="Search"></div>
</form>
</div>
{% for message in get_flashed_messages() %}
<div class=flash>
{{ message }}
</div>
{% endfor %}
{% endblock %}
When I hit Search button nothing happens, I checked database it has some data in it so it is not empty, I can't find where am I making a mistake, please help?
Make sure, action point to proper url
I think you render the form with wrong action for submitting the form.
Your version is using action="" and I guess, it shall be action="/search"
So your template shall be changed like:
{% extends "hello.html" %}
{% block content %}
<div class="search">
<form action="/search" method=post>
<input type=text name=search value="{{ request.form.search}}"></br>
<div class="actions"><input type=submit value="Search"></div>
</form>
</div>
{% for message in get_flashed_messages() %}
<div class=flash>
{{ message }}
</div>
{% endfor %}
{% endblock %}
Do not redirect out of your result
Your existing code is processing POST, but within first loop it ends up returning with redirect
#app.route('/search', methods=['GET', 'POST'])
def search():
if request.method == "POST":
db = MySQLdb.connect(user="root", passwd="", db="cs324", host="127.0.0.1")
c=db.cursor()
c.executemany('''select * from student where name = %s''', request.form['search'])
for r in c.fetchall():
print r[0],r[1],r[2]
return redirect(url_for('search')) # <- Here you jump away from whatever result you create
return render_template('search.html')
Do render your template for final report
Your code does not show in POST branch any attempt to render what you have found in the database.
Instead of print r[0], r[1]... you shall call render_template()
Something like this
#app.route('/search', methods=['GET', 'POST'])
def search():
if request.method == "POST":
db = MySQLdb.connect(user="root", passwd="", db="cs324", host="127.0.0.1")
c=db.cursor()
c.executemany('''select * from student where name = %s''', request.form['search'])
return render_template("results.html", records=c.fetchall())
return render_template('search.html')