I followed the tutorial at https://flask.palletsprojects.com/en/2.0.x/tutorial/factory/ and successfully created the project there. I then tried to apply this to my new project and I'm getting hung up right away. The database schema doesn't seem to get read at any point. At other points in the code I tried to print the tables and got an empty list. Here are the relevant chunks in the code where it should happen.
__init__.py:
import os
from flask import Flask
def create_app(test_config=None):
app = Flask(__name__, instance_relative_config=True)
app.config.from_mapping(
SECRET_KEY='dev',
DATABASE=os.path.join(app.instance_path, 'projectdb.sqlite')
)
if test_config is None:
app.config.from_pyfile('config.py', silent=True)
else:
app.config.from_mapping(test_config)
try:
os.makedirs(app.instance_path)
except OSError:
pass
from . import db
db.init_app(app)
from . import auth
app.register_blueprint(auth.bp)
print("App created")
return app
db.py:
import sqlite3
import click
from flask import current_app, g
from flask.cli import with_appcontext
def get_db():
if 'db' not in g:
g.db = sqlite3.connect(
current_app.config['DATABASE'],
detect_types=sqlite3.PARSE_DECLTYPES
)
g.db.row_factory = sqlite3.Row
return g.db
def close_db(e=None):
db = g.pop('db', None)
if db is not None:
db.close()
def init_db():
db = get_db()
with current_app.open_resource('schema.sql') as f:
db.executescript(f.read().decode('utf8'))
#click.command('init-db')
#with_appcontext
def init_db_command():
init_db()
click.echo('Initialized the database.')
def init_app(app):
app.teardown_appcontext(close_db)
app.cli.add_command(init_db_command)
auth.py:
import functools
from flask import (
Blueprint, flash, g, redirect, render_template, request, session, url_for
)
from werkzeug.security import check_password_hash, generate_password_hash
from ldmt.db import get_db
bp = Blueprint('auth', __name__, url_prefix='/auth')
#bp.route('/login', methods=('GET', 'POST'))
def login():
if request.method == 'POST':
username = request.form['username']
password = request.form['password']
db = get_db()
error = None
user = db.execute(
'SELECT * FROM user WHERE username = ?', (username,)
).fetchone()
if user is None:
error = 'invalid username.'
elif not check_password_hash(user['password'], password):
error = 'incorrect password.'
if error is None:
session.clear()
session['user_id'] = user['id']
return redirect(url_for('index'))
flash(error)
return render_template('auth/login.html')
#bp.before_app_request
def load_logged_in_user():
user_id = session.get('user_id')
if user_id is None:
g.user = None
else:
g.user = get_db().execute(
'SELECT * FROM user WHERE id = ?', (user_id,)
).fetchone()
#bp.route('/logout')
def logout():
session.clear()
return redirect(url_for('auth.login'))
def login_required(view):
#functools.wraps(view)
def wrapped_view(**kwargs):
if g.user is None:
return redirect(url_for('auth.login'))
return view(**kwargs)
return wrapped_view
schema.sql:
DROP TABLE IF EXISTS user;
CREATE TABLE user (
id INTEGER PRIMARY KEY AUTOINCREMENT,
username TEXT UNIQUE NOT NULL,
password TEXT NOT NULL
);
The problem happens when I try to login. Nothing has been added to the database so I expect an error that that user is not found, but the error I get is sqlite3.OperationalError: no such table: user
I can also confirm that the .sqlite file is created but empty.
You created a CLI command for flask here...
#click.command('init-db')
#with_appcontext
def init_db_command():
init_db()
click.echo('Initialized the database.')
You will have to run the command in the command line like so...
flask init-db
Related
I'm really new to this stuff. I want to make it so that there can't be 2 duplicate or same usernames on the database. For example, if I register once with the username 'johndoe' and register again with the same username, it also gets registered in the database which I don't want to happen. I want so if the username exists, return 'unsuccessful' or something like that for now.
app.py:
from flask import Flask, redirect, url_for, render_template, request, session, flash
from flask_mysqldb import MySQL
import MySQLdb.cursors
import uuid
app = Flask(__name__)
app.secret_key = "abcd"
app.config['MYSQL_HOST'] = 'localhost'
app.config['MYSQL_USER'] = 'root'
app.config['MYSQL_PASSWORD'] = 'Root123'
app.config['MYSQL_DB'] = 'login'
mysql = MySQL(app)
#app.route('/')
def home():
return render_template("index.html")
#app.route('/login', methods=['POST', 'GET'])
def login():
if request.method == 'POST':
loginUsername = request.form['login_username']
loginPassword = request.form['login_password']
cursor = mysql.connection.cursor(MySQLdb.cursors.DictCursor)
cursor.execute("SELECT * FROM logininfo WHERE Username=%s AND Password=%s", (loginUsername, loginPassword))
loginInfo = cursor.fetchone()
if loginInfo is not None:
return "yes"
else:
return "no"
return render_template("login.html")
#app.route('/register', methods=['POST', 'GET'])
def register():
if request.method == 'POST':
userDetails = request.form
username = userDetails['register_username']
email = userDetails['register_email']
password = userDetails['register_password']
userID = str(uuid.uuid4())
cur = mysql.connection.cursor()
cur.execute("INSERT INTO logininfo(username, email, password, userID) VALUES(%s, %s, %s, %s)", (username, email, password, userID))
mysql.connection.commit()
cur.close()
return redirect(url_for("login"))
return render_template("register.html")
if __name__ == "__main__":
app.run(debug=True)
Generally, you have two options:
Make the verification exclusively in your Flask application code.
Use database features to block duplicate users to be inserted (e.g. add a unique key constraint on the username attribute).
If you're going to use 2) you need to handle in your Flask code the error returned by the DBMS when it detects that someone is trying to add a new row with an existing username value.
Otherwise, for option 1), you could do something similar to what you do in your login route:
cursor.execute("SELECT * FROM logininfo WHERE Username=%s", (username,))
existingInfo = cursor.fetchone()
if existingInfo is not None:
return "Error: user is already registered" # or render_template a special error template.
When creating the table use the unique constraint. Here is an example:
CREATE TABLE user (id int primary key, username varchar(255), pass_hash varchar(255), UNIQUE(username))
This will raise an error stating that the inserted value is a duplicate. Handle the error using some try except code.
I'm trying to create a registration page where the user's username and password would get sent to a database I have set up on Heroku. I do not get any errors and after clicking the submit button I get sent to the "you are registered" page, but the username and password don't get added to the db. Here is the code:
from flask import Flask, session, render_template, request
from flask_session import Session
from sqlalchemy import create_engine
from sqlalchemy.orm import scoped_session, sessionmaker
from models import *
app = Flask(__name__)
# Check for environment variable
if not os.getenv("DATABASE_URL"):
raise RuntimeError("DATABASE_URL is not set")
# Configure session to use filesystem
app.config["SESSION_PERMANENT"] = False
app.config["SESSION_TYPE"] = "filesystem"
app.config['SQLALCHEMY_ECHO'] = True
Session(app)
# Set up database
engine = create_engine(os.getenv("DATABASE_URL"))
db = scoped_session(sessionmaker(bind=engine))
db1 = SQLAlchemy(app)
class User(db1.Model):
__tablename__ = "users"
user_id = db1.Column(db1.Integer, primary_key=True,)
username = db1.Column(db1.String, nullable=False)
password = db1.Column(db1.String, nullable=False)
def add_user(self, u):
self.users.append(u)
u.user_id = self.id
with app.app_context():
db1.init_app(app)
db1.create_all()
#app.route("/", methods=['POST', 'GET'])
def main():
return render_template('main.html')
#app.route("/registered", methods=['POST'])
def registered():
username = request.form.get('rusername')
password = request.form.get('rpassword')
u1 = User(username=username, password=password)
db1.session.add(u1)
db1.session.commit()
return '<h1> You are registered! <h1>'
#app.route("/loggedin", methods=['POST'])
def loggedin():
return '<h1> You are logged in! <h1>'
Thank you for your help!
I have spent so much time trying to figure this out and I am lost.
I am trying to make a small web app, watching some tutorials...
I keep getting this error: MySQLdb._exceptions.OperationalError ('1046, No database selected')
This is my code:
# app.py
from flask import Flask, render_template, flash, redirect, url_for, session, logging, request
# from flask_sqlalchemy import SQLAlchemy
# from data import Articles
from data import Articles
from flask_mysqldb import MySQL
from wtforms import Form, StringField, TextAreaField, PasswordField, validators
from passlib.hash import sha256_crypt
app = Flask(__name__)
# app.config['SECRET_KEY'] = 'xxxxxxxxxx'
# Config MySQL
app.config['MySQL_HOST'] = 'localhost'
app.config['MySQL_USER'] = 'root'
app.config['MySQL_PASSWORD'] = ''
app.config['MySQL_DB'] = 'myflaskapp'
app.config['MySQL_CURSORCLASS'] = 'DictCursor'
# Init MySQL
mysql = MySQL(app)
# Setting articles
Articles = Articles()
#app.route('/')
def home():
return render_template('home.html')
#app.route('/about')
def about():
return render_template('about.html')
#app.route('/articles')
def articles():
return render_template('articles.html', articles=Articles)
#app.route('/article/<string:id>/')
def article(id):
return render_template('article.html', id=id)
class RegisterForm(Form):
name = StringField('Name', [validators.Length(min=1, max=50)])
username = StringField('Username', [validators.Length(min=4, max=25)])
email = StringField('Email', [validators.Length(min=6, max=50)])
password = PasswordField('Password', [validators.DataRequired(),
validators.EqualTo(
'confirm', message='Passwords do not match!')
])
confirm = PasswordField('Confirm Password')
#app.route('/register', methods=['GET', 'POST'])
def register():
form = RegisterForm(request.form)
if request.method == 'POST' and form.validate():
name = form.name.data
email = form.email.data
username = form.username.data
password = sha256_crypt.encrypt(str(form.password.data))
# Creat Cursor
cur = mysql.connection.cursor()
# Execute Query
cur.execute("INSERT INTO users(name, email, username, password) VALUES(%s, %s, %s, %s)",
(name, email, username, password))
# Commit to DB
mysql.connection.commit()
# close connection
cur.close()
# Message to user once registered
flash('You are now registered and can login, thank you', 'success')
return redirect(url_for('index'))
return render_template('register.html', form=form)
if __name__ == '__main__':
app.run(debug=True)
If I comment of MYSQL config things I don't get a password denied error, so I am very confused, did I mess up during the MYSQL install?
Further, I have checked my DB exists by using show database; I have accessed the user table inside myflaskapp and also went on phpmyadmin to check.
I solved my own problems.
1) app.config['MySQL_HOST'] = 'localhost' --> MySQL needs to be replaced with 'MYSQL'
return redirect(url_for('index')) --> index needs to be replaced with 'register'
simple spelling mistakes...
install the db:
pip install flask-mysqldb
create my database localy using xampp
setting the config
from flask import Flask, render_template, flash, redirect, url_for, request,
logging
from wtforms import Form, StringField, TextAreaField, validators
from flask_mysqldb import MySQL
app = Flask (__name__)
# Config MySQL
app.config['MYSQL_HOST'] = 'localhost'
app.config['MYSQL_USER'] = 'root'
app.config['MYSQL_PASSWORD'] = ''
app.config['MYSQL_DB'] = 'todo'
app.config['MYSQL_CURSORCLASS'] = 'DictCursor'
# init MYSQL
mysql = MySQL(app)
#app.route('/')
def index():
return render_template('index.html')
#app.route('/add')
def task():
return render_template('add.html')
# Task Form Class
class TaskForm(Form):
name = StringField('Name', [validators.Length(min=1, max=55)])
description = TextAreaField('Description', [validators.Length(min=5)])
# Add task
#app.route('/add', methods=['GET', 'POST'])
def add():
form = TaskForm(request.form)
if request.method == 'POST' and form.validate():
name = form.name.data
description = form.description.data
# Create Cursor
cur = mysql.connection.cursor()
# Execute
cur.execute("INSERT INTO todotask(name, description) VALUES(%s, %s)",
(name, description))
# Commit to DB
mysql.connection.commit()
#Close connection
cur.close()
flash('the task created ', 'success')
return redirect(url_for('add'))
return render_template('index.html', form=form)
if __name__ == '__main__':
app.run(debug = True)
when I run the server it's working normally and give me 200 status for the post method when inserting any thing to the db but when I'm trying to check nothing insert or saved in the db , how can I make it work?
It looks like you are doing everything right, except odd way of handling routes.
Provided that:
you are running Flask in development mode with default parameters i.e. development server listens on localhost:5000,
index.html contains link to http://localhost:5000/add,
after adding you want to redirect back to http://localhost:5000.
this code should work:
from flask import Flask, render_template, flash, \
redirect, url_for, request, logging
from wtforms import Form, StringField, TextAreaField, validators
from flask_mysqldb import MySQL
app = Flask (__name__)
# Config MySQL
app.config['MYSQL_HOST'] = 'localhost'
app.config['MYSQL_USER'] = 'root'
app.config['MYSQL_PASSWORD'] = ''
app.config['MYSQL_DB'] = 'todo'
app.config['MYSQL_CURSORCLASS'] = 'DictCursor'
# TODO Update secret key
app.secret_key = 'UPDATETHISPART'
# init MYSQL
# mysql = MySQL(app)
# Task Form Class
class TaskForm(Form):
name = StringField('Name', [validators.Length(min=1, max=55)])
description = TextAreaField('Description', [validators.Length(min=5)])
#app.route('/')
def index():
return render_template('index.html')
# Add task
#app.route('/add', methods=['GET', 'POST'])
def add():
form = TaskForm(request.form)
if request.method == 'POST' and form.validate():
name = form.name.data
description = form.description.data
# Create Cursor
cur = mysql.connection.cursor()
# Execute
cur.execute("INSERT INTO todotask(name, description) VALUES(%s, %s)", (name, description))
# Commit to DB
mysql.connection.commit()
# Close connection
cur.close()
flash('the task created ', 'success')
return redirect(url_for('index'))
return render_template('add.html', form=form)
if __name__ == '__main__':
app.run(debug=True)
What I have updated:
Removed first route for /add since it seems like it is unnecessary,
Added secret key,
Reorganized how adding in /add route handles redirects/rendering.
By looking at the db insert handling, it looks just fine. Probably routes did interfere with what you intended.
Try doing it like this:
conn = mysql.connect()
cur = conn.cursor()
cur.execute("INSERT INTO todotask(name, description) VALUES(%s, %s)",
(name, description))
conn.commit()
When I specify an IP and port without the preceding:
if __name__ == '__main__':
As such:
app.run(host="138.165.91.210",port=5017,threaded=True)
It works but it hangs a bit (does it hang on an infinite loop maybe?). When I do the usual:
if __name__ == '__main__':
app.run(host="138.165.91.210",port=5017,threaded=True)
It avoids the ip and port. What's wrong with my code?
Full script:
import os
from sqlite3 import dbapi2 as sqlite3
from flask import Flask, request, session, g, redirect, url_for, abort, \
render_template, flash
# create our little application :)
app = Flask(__name__)
# Load default config and override config from an environment variable
def connect_db():
"""Connects to the specific database."""
rv = sqlite3.connect(app.config['DATABASE'])
rv.row_factory = sqlite3.Row
return rv
def init_db():
"""Initializes the database."""
db = get_db()
with app.open_resource('schema.sql', mode='r') as f:
db.cursor().executescript(f.read())
db.commit()
#app.cli.command('initdb')
def initdb_command():
"""Creates the database tables."""
init_db()
print('Initialized the database.')
def get_db():
"""Opens a new database connection if there is none yet for the
current application context.
"""
if not hasattr(g, 'sqlite_db'):
g.sqlite_db = connect_db()
return g.sqlite_db
#app.teardown_appcontext
def close_db(error):
"""Closes the database again at the end of the request."""
if hasattr(g, 'sqlite_db'):
g.sqlite_db.close()
#app.route('/')
def show_entries():
db = get_db()
cur = db.execute('select title, text from entries order by id desc')
entries = cur.fetchall()
return render_template('show_entries.html', entries=entries)
#app.route('/add', methods=['POST'])
def add_entry():
if not session.get('logged_in'):
abort(401)
db = get_db()
db.execute('insert into entries (title, text) values (?, ?)',
[request.form['title'], request.form['text']])
db.commit()
flash('New entry was successfully posted')
return redirect(url_for('show_entries'))
#app.route('/login', methods=['GET', 'POST'])
def login():
error = None
if request.method == 'POST':
if request.form['username'] != app.config['USERNAME']:
error = 'Invalid username'
elif request.form['password'] != app.config['PASSWORD']:
error = 'Invalid password'
else:
session['logged_in'] = True
flash('You were logged in')
return redirect(url_for('show_entries'))
return render_template('login.html', error=error)
#app.route('/logout')
def logout():
session.pop('logged_in', None)
flash('You were logged out')
return redirect(url_for('show_entries'))
#app.route("/hello")
def hello():
return "Hello World!"
if __name__ == '__main__':
app.run(host="138.165.91.210",port=5017,threaded=True)
Edit:
I'm running the app using bash:
flask --app=flaskr run
You are using an unreleased version of Flask, so keep in mind that things might change.
__name__ == '__main__' only evaluates to True when you execute a file directly (i.e., python filename.py). Since that isn't how you're running it here, it will be False and that block is skipped.
To solve your port issue, when running your application using the flask command, you need to specify your options through the command line.
python -m flask --app flaskr --host 138.165.91.210 --port 5017 --with-threads
For more information, you should check the usage
python -m flask --help
To solve your delay issue, the if __name__ == '__main__': block is important. Without it, Flask will try to run two instances of the application (once from the flask command and one from the call to app.run).