Cannot create database with SQLAlchemy - python

I am trying to run my create_db.py file to create the posts.db database, but it will not get created in my project directory. And when I run the main file for the blog and try to login in I get the error below.
I have looked up this error and seen that other people have gotten it as well and asked about it here on Stackoverflow, but none of them seems to help me. I have read that this could be because something in my blog.py file is running main before the database gets created. But, I am thinking that it has something to do with the configuration. Mainly the PATH of the database could be getting mixed up with the app.config['SQLAlCHEMY_DATABASE_URI'] = 'sqlite:///' line.
Any ideas?
Here is the error
sqlalchemy.exc.OperationalError
OperationalError: (OperationalError) no such table: posts u'SELECT posts.id AS posts_id, posts.title AS posts_title, posts.post AS posts_post \nFROM posts' ()
OperationalError: (OperationalError) no such table: posts u'SELECT posts.id AS posts_id, posts.title AS posts_title, posts.post AS posts_post \nFROM posts' ()
Here is my code. There are three files here: blog.py, models.py, create_db.py
blog.py
# controller of blog app
from flask import Flask, render_template, request, session,\
flash, redirect, url_for, g
from flask.ext.sqlalchemy import SQLAlchemy
import sqlite3
from functools import wraps
# create the application object
app = Flask(__name__)
# configuration
app.secret_key = 'x13xa8xf5}[xfexd4Zxb8+x07=7xc9xe1Bxcfxbdt.ox87oxc9'
app.config['SQLAlCHEMY_DATABASE_URI'] = 'sqlite:///'
# create sqlalchemy object
db = SQLAlchemy(app)
from models import *
# login required decorator
def login_required(test):
#wraps(test)
def wrap(*args, **kwargs):
if 'logged_in' in session:
return test(*args, **kwargs)
else:
flash('You need to login first.')
return redirect(url_for('login'))
return wrap
#app.route('/', methods = ['GET','POST'])
def login():
error = None
if request.method == 'POST':
if request.form['username'] != 'admin' or request.form['password'] != 'admin':
error = 'Invalid Credentils. Please try again.'
else:
session['logged_in'] = True
return redirect(url_for('main'))
return render_template('login.html', error=error)
#app.route('/main')
#login_required
def main():
posts = db.session.query(BlogPost).all()
return render_template('main.html', posts=posts)
#app.route('/add', methods=['POST'])
#login_required
def add():
title = request.form['title']
post = request.form['post']
if not title or not post:
flash("All fields are required. Please try again.")
return redirect(url_for('main'))
else:
db.session.add(title)
db.session.add(post)
db.session.commit()
db.session.close()
flash('New entry was successfully posted!')
return redirect(url_for('main'))
#app.route('/logout')
def logout():
session.pop('logged_in', None)
flash('You were logged out')
return redirect(url_for('login'))
def connect_db():
return sqlite.connect('posts.db')
if __name__ == '__main__':
app.run(debug = True)
models.py:
from blog import db
class BlogPost(db.Model):
__tablename__ = "posts"
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String, nullable=False)
post = db.Column(db.String, nullable=False)
def __init__(self, title, post):
self.title = title
self.post = post
def __repr__(self):
return '<title {}'.format(self.title)
create_db.py
from blog import db
from models import BlogPost
# create the database and the db tables
db.create_all()
# insert
db.session.add(BlogPost("Good","I\'m good."))
db.session.add(BlogPost("Well","I\'m well."))
db.session.add(BlogPost("Post","I\'m a post."))
# commit the changes
db.session.commit()

There's a typo in SQLAlCHEMY_DATABASE_URI, should be SQLALCHEMY_DATABASE_URI, the 2nd l.
When running from blog import db some statements in blog.py get executed, including the one with sqlite:/// which is where the path is set. Modifying this line
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///posts.db'
and then after running create_db.py should create a database in app's root directory. There doesn't seem to be another place in the code where the db path is set.
It looks like you may also run into some circular import problems (will throw ImportError: cannot import name [name]). Some solutions are putting the app init stuff like db = SQLAlchemy.. into a separate file or importing at the end of the file.

Related

I've set nullable to true for my row in SQLAlchemy database but I keep getting IntegrityError for the same. Help me through [duplicate]

This question already has answers here:
SQLAlchemy: How to add column to existing table?
(2 answers)
Closed 9 months ago.
from flask import Flask, redirect, url_for, render_template, request
from flask_sqlalchemy import SQLAlchemy
from datetime import datetime
import uuid
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///passwords.db'
db = SQLAlchemy(app)
class Passwords(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(200), nullable=False)
key = db.Column(db.String(200), nullable=False)
date_created = db.Column(db.DateTime, default=datetime.utcnow)
def __repr__(self):
return '<Name %r>' % self.id
#app.route("/passwords")
def passwords():
return render_template("passwords.html")
#app.route("/")
def home():
return render_template("index.html")
#app.route("/signup", methods=["POST", "GET"])
def new_user():
if request.method == "POST":
a = str(uuid.uuid4())
print(a)
key = Passwords(key=a)
username = request.form["nm"]
newuser = Passwords(name=username)
try:
db.session.add(newuser)
db.session.commit()
db.session.add(key)
db.session.commit()
return redirect(url_for("new_user"))
except:
return "There was an error with your registration"
else:
passcodes = Passwords.query.order_by(Passwords.date_created)
return render_template("signup.html", passcodes=passcodes)
#app.route("/login", methods=["POST", "GET"])
def login():
a = str(uuid.uuid4())
print(a)
if request.method == "POST":
user = request.form["pw"]
if user == a:
return redirect(url_for("user", usr='Η Ακύρωση κράτησης ολοκληρώθηκε'))
else:
return render_template("login.html")
else:
return render_template("login.html")
# app.route("/<usr>")
def user(usr):
return "<h1>{}</h1>".format(usr)
if __name__ == "__main__":
app.run(debug=True)
I get my website up and running but when I go to /signup, an error occurs:
sqlalchemy.exc.OperationalError: (sqlite3.OperationalError) no such column: passwords.key
[SQL: SELECT passwords.id AS passwords_id, passwords.name AS passwords_name, passwords."key" AS passwords_key, passwords.date_created AS passwords_date_created
FROM passwords ORDER BY passwords.date_created]
(Background on this error at: https://sqlalche.me/e/14/e3q8)
Any help is much appreciated
SQLAlchemy only handles creating and dropping tables; it has no support for adding, removing or amending columns, other than executing raw SQL ALTER TABLE statements. If you need to perform such an action your options are, in ascending order of difficulty*:
Drop the entire database (or delete the SQLite file) and run db.create_all() to recreate it
all data will be lost unless you back up the database
Drop the affected table and run db.create_all() to recreate it
Use your database's console or some other database management application to drop the table
If the table is related to other tables via foreign keys then the related tables may need to be deleted too
All data in the table will be lost unless it's backed up
Manually execute the necessary ALTER TABLE statement(s) in your database's console or some other database management application
You will need to ensure that the results match what SQLAlchemy would have created
Use a migration utility to apply model changes to the database
Flask-Migrate is the obvious candidate, but there may be others
* All options assume you have updated the model class

SQLAlchemy OperationalError no such table

I am creating a Flask app, and I have a page with a form to register new users. But when I type some data, I get the error sqlalchemy.exc.OperationalError: (OperationalError) no such table: user. In the file config.py I have this line
SQLALCHEMY_DATABASE_URI = "sqlite:///example.sqlite" and this is the rest of my code:
flask_start.py
import os
from flask import Flask
from config import Config
from flask_utils import page_bp
from db_creation import db
def create_app():
appname = "IOT - PROJECT NASTIS"
app = Flask(appname)
myconfig = Config
app.config.from_object(myconfig)
app.register_blueprint(page_bp, url_prefix='')
db.init_app(app)
return app
def setup_database(app):
with app.app_context():
db.create_all()
if __name__ == "__main__":
app = create_app()
if not os.path.isfile('/tmp/test.sqlite'):
setup_database(app)
port = 8000
interface = '0.0.0.0'
app.run(host=interface, port=port, debug=True)
db_creation.py
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
flask_utils.py
db_creation import db
from User import User
page_bp = Blueprint("page_bp", __name__)
#page_bp.route('/mainPage/register', methods=['GET', 'POST'])
def register():
form = RegistrationForm(request.form)
if request.method == 'POST' and form.validate():
user = User(form.username.data, form.email.data,
sha256_crypt.encrypt(str(form.password.data)))
db.session.add(user)
db.session.commit()
flash('Thanks for registering, you can now log in!!', 'success')
return redirect(url_for('page_bp.register'))
return render_template('register.html', form=form)
User.py
from db_creation import db
class User(db.Model):
username = db.Column(db.String(100), primary_key=True)
email = db.Column(db.String(100))
password = db.Column(db.String(100))
def __init__(self, username, email, password):
self.username = username
self.email = email
self.password = password
What am I doing wrong? I can't figure out what is the mistake
When you run db.create_all() it needs to know which tables to create-- it can tell that by which models it currently can see.
User isn't imported in your start_flask.py file-- so when db.create_all() is run it pretty much goes 'huh, I'm not aware of any models, I guess I'll just create an empty database with no tables' it doesn't know to go and look in other files, so you just need to introduce them :)
Adding from User import User to the imports in the start_flask.py file should solve that.
Also-- you're checking for a file in /tmp/... to trigger your 'no database file yet' check, but your SQLALCHEMY_DATABASE_URI isn't matching that.

sqlalchemy.exc.OperationalError

I am doing a flask tutorial from youtube. But however i always get this error. I even copied his code and tried on my pc, but it still raise that error.
from flask import Flask, render_template, url_for, request, redirect
from flask_sqlalchemy import SQLAlchemy
from datetime import datetime
app = Flask(__name__)
app.config["SQLALCHEMY_DATABASE_URI"] = "sqlite:///test.db"
db = SQLAlchemy(app)
class Todo(db.Model):
id = db.Column(db.Integer, primary_key=True)
content = db.Column(db.String(200), nullable=False)
date_created = db.Column(db.DateTime, default=datetime.utcnow)
def __repr__(self):
return "<Task %r>" % self.id
#app.route("/", methods=["POST", "GET"])
def index():
if request.method == "POST":
task_content = request.form["content"]
new_task = Todo(content=task_content)
try:
db.session.add(new_task)
db.session.commit()
return redirect("/")
except:
return "There was an issue adding your task"
else:
tasks = Todo.query.order_by(Todo.date_created).all()
return render_template("index.html", tasks=tasks)
# return "hello"
if __name__ == "__main__":
app.run(debug=True)
And this is the error:
sqlalchemy.exc.OperationalError: (sqlite3.OperationalError) no such table: todo
[SQL: SELECT todo.id AS todo_id, todo.content AS todo_content, todo.date_created AS todo_date_created
FROM todo ORDER BY todo.date_created]
You must create db with tables before trying to call it. Answer in the error text.
Try to read documentation There is lot of information for you. Check method: create_all and second code example from documentation:
>>> from yourapplication import db
>>> db.create_all()

Can't find where sqlite3 database is

I'm folowing Flaskr Tutorial (http://flask.pocoo.org/docs/tutorial/setup/) and missunderstand one thing:
Both aplication that I did and the original Flaskr from github (https://github.com/mitsuhiko/flask/tree/master/examples/flaskr/) work properly, but I dont see any database files created, even though I manually created '/tm/flaskr.db', this file is still empty.
Please, could you explain me, in what a magic place sqlite3 keeps data?
here is py file that I execute:
# coding: utf-8
import sqlite3
import inspect
from flask import Flask, request, session, g, redirect, url_for, abort, render_template, flash
from contextlib import closing
app = Flask(__name__)
app.config.from_object('config')
def connect_db():
return sqlite3.connect(app.config['DATABASE'])
def init_db():
with closing(connect_db()) as db:
with app.open_resource('schema.sql') as f:
db.cursor().executescript(f.read())
db.commit()
#app.before_request
def before_request():
g.db = connect_db()
#app.teardown_request
def teardown_request(exception):
g.db.close()
#app.route('/')
def show_entries():
cur = g.db.execute('select title, text from entries order by id desc')
entries = [dict(title=row[0], text=row[1]) for row in 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)
g.db.execute('insert into entries (title, text) values (?, ?)',
[request.form['title'], request.form['text']])
g.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'))
if __name__ == '__main__':
app.run()
here is config file:
# coding: utf-8
DATABASE = '/tmp/site.db'
DEBUG = True
SECRET_KEY = 'dev_key'
USERNAME = 'admin'
PASSWORD = 'pass'
schema.sql
drop table if exists entries;
create table entries (
id integer primary key autoincrement,
title string not null,
text string not null
);
Thanks so much for your answers, I've noticed, that db is created in the '/tmp/'folder in the root of file system. I expected, that it would be created in my project folder like in django with sqlite.
The database will be stored in "/tmp/site.db".
try doing sqlite3 /tmp/site.db
If you want to keep this file in the projects directory just change it to site.db
You need the init_db() to be run seperately just once.
From Python command line import flaskr and run flaskr.init_db()
I would suggest looking at Flaskr and trying to understand it first. The default Flaskr will re-initiated the db every time it is executed.
Joe

flask-login: can't understand how it works

I'm trying to understand how Flask-Login works.
I see in their documentation that they use a pre-populated list of users. I want to play with a database-stored users list.
However, I don't understand some things in this Flask-Login module.
#login_manager.user_loader
def load_user(userid):
#print 'this is executed',userid
return user(userid, 'asdf')
This code will be called at every request? This is used to load all the details of my user object?
For now, I have this code:
#app.route('/make-login')
def make_login():
username = 'asdf'
password = 'asdf'
user_data = authenticate(username, password)
user_obj = user(user_data[0], user_data[1])
login_user(user_obj)
return render_template('make-login.html')
When I access /make-login, I want to log in.
My user class:
class user(object):
def __init__(self, id, username, active=True):
self.username = username
self.id = id
#self.active = active
def is_authenticated(self):
return True
def is_active(self):
return True
def is_anonymous(self):
return False
def get_id(self):
return 5
Also, I wrote another two functions for authenticate/register
def authenticate(username, password):
cursor = db.cursor()
password = md5.md5(password).hexdigest()
try:
query = "SELECT * FROM `users` WHERE `username` = %s AND `password` = %s"
cursor.execute(query, (username, password))
results = cursor.fetchall()
#print results[0][0]
#print "here i am"
if not results:
return False
else:
user_data = [results[0][0], results[0][1]]
return user_data
#self.authenticated = True
#self.user_id = results[0][0]
#session['username'] = results['username']
#print type(results)
except db.Error, e:
return 'There was a mysql error'
def register(username, password, email, *args):
cursor = db.cursor()
password = md5.md5(password).hexdigest()
try:
#query = "INSERT INTO `users` (`username`, `password`, `email`) VALUES ('%s', '%s', '%s')" % (username, password, email)
query = "INSERT INTO `users` (`username`, `password`, `email`) VALUES (%s, %s, %s)"
cursor.execute(query, (username, password, email))
db.commit()
return True
except db.Error, e:
print 'An error has been passed. %s' %e
db.rollback()
return False
I don't know how to make this Flask-Login work with MySQL. Also, I don't know if the user are logged-in. How can I get the user ID or the username?
Anyone can explain me in some rows how this Flask-Login works?
Flask-login doesn't actually have a user backend, it just handles the session machinery to help you login and logout users. You have to tell it (by decorating methods), what represents a user and it is also up to you to figure out how to know if a user is "active" or not (since being "active" can mean different things in different applications).
You should read the documentation and be sure what it does and does not do. Here I am only going to concentrate on wiring it up with the db backend.
To start off with, define a user object; which represents properties for your users. This object can then query databases, or LDAP, or whatever and it is the hook that connects the login mechanism with your database backend.
I will be using the login example script for this purpose.
class User(UserMixin):
def __init__(self, name, id, active=True):
self.name = name
self.id = id
self.active = active
def is_active(self):
# Here you should write whatever the code is
# that checks the database if your user is active
return self.active
def is_anonymous(self):
return False
def is_authenticated(self):
return True
Once you have the user object created, you need to write a method that loads the user (basically, creates an instance of the User class from above). This method is called with the user id.
#login_manager.user_loader
def load_user(id):
# 1. Fetch against the database a user by `id`
# 2. Create a new object of `User` class and return it.
u = DBUsers.query.get(id)
return User(u.name,u.id,u.active)
Once you have these steps, your login method does this:
Checks to see if the username and password match (against your database) - you need to write this code yourself.
If authentication was successful you should pass an instance of the user to login_user()
Flask-login will try and load a user BEFORE every request. So yes, your example code below will be called before every request. It is used to check what userid is in the current session and will load the user object for that id.
#login_manager.user_loader
def load_user(userid):
#print 'this is executed',userid
return user(userid, 'asdf')
If you look at the Flask-login source code on github, there is a line under function init_app which goes:
app.before_request(self._load_user)
So before every request, the _load_user function is called. The _load_user functions actually calls another function "reload_user()" based on conditions. And finally, reload_user() function calls your callback function that you wrote (load_user() in your example).
Also, flask-login only provides the mechanism to login/logout a user. It does not care if you are using mysql database.
As per from the Flask-Login's document a user object must be returned and if the user id is not found it should return None instead of Exception.
#login_manager.user_loader
def load_user(userid):
try:
#: Flask Peewee used here to return the user object
return User.get(User.id==userid)
except User.DoesNotExist:
return None
You might want to use Flask-Security, which combines Flask-Login with SQLAlchemy for database access and automates much of the back-end handling of user records.
The Quick Start tutorial will get you started. Set app.config['SQLALCHEMY_DATABASE_URI'] to your MySQL database connection string.
After great everything explained I will try with the code to give a simple example of how to use and at the same time answer it below:
Once you have these steps, your login method does this:
Checks to see if the username and password match (against your database) - you
need to write this code yourself.
If authentication was successful you should pass an instance of the user to
login_user()
Let's say this is the structure of the project:
├─stackoverflow
│ run.py
│
└───site
│ forms.py
│ models.py
│ routes.py
│ site.db
│ __init__.py
│
├───static
│ main.css
│
└───templates
about.html
account.html
home.html
layout.html
login.html
register.html
What interests us most is the model:
# models.py
from site import db, login_manager
from flask_login import UserMixin
#login_manager.user_loader
def load_user(user_id):
return User.query.get(int(user_id))
class User(db.Model, UserMixin):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(20), unique=True, nullable=False)
email = db.Column(db.String(120), unique=True, nullable=False)
image_file = db.Column(db.String(20), nullable=False, default='default.jpg')
password = db.Column(db.String(60), nullable=False)
def __repr__(self):
return f"User('{self.username}', '{self.email}', '{self.image_file}')"
And we will call it in the user's login, more precisely after the user has registered - after the user exists.
Specifically, the answer to the two steps that need to be implemented can be found in the following two lines of code:
Checks to see if the username and password match (against your database) - you
need to write this code yourself.
A: if user and bcrypt.check_password_hash(user.password, form.password.data):
If authentication was successful you should pass an instance of the user to
login_user()
A: login_user(user, remember=form.remember.data)
# routes.py
from flask import render_template, url_for, flash, redirect, request
from site import app, db, bcrypt
from site.forms import RegistrationForm, LoginForm
from site.models import User
from flask_login import login_user, current_user, logout_user, login_required
#app.route("/")
#app.route("/home")
def home():
return render_template('home.html', title="Welcome")
#app.route("/register", methods=['GET', 'POST'])
def register():
if current_user.is_authenticated:
return redirect(url_for('home'))
form = RegistrationForm()
if form.validate_on_submit():
hashed_password = bcrypt.generate_password_hash(form.password.data).decode('utf-8')
user = User(username=form.username.data, email=form.email.data, password=hashed_password)
db.session.add(user)
db.session.commit()
flash('Your account has been created! You are now able to log in', 'success')
return redirect(url_for('login'))
return render_template('register.html', title='Register', form=form)
#app.route("/login", methods=['GET', 'POST'])
def login():
if current_user.is_authenticated:
return redirect(url_for('home'))
form = LoginForm()
if form.validate_on_submit():
user = User.query.filter_by(email=form.email.data).first()
if user and bcrypt.check_password_hash(user.password, form.password.data):
login_user(user, remember=form.remember.data)
next_page = request.args.get('next')
return redirect(next_page) if next_page else redirect(url_for('home'))
else:
flash('Login Unsuccessful. Please check email and password', 'danger')
return render_template('login.html', title='Login', form=form)
# Views that require your users to be logged in can be decorated with the `login_required` decorator
#app.route("/account")
#login_required
def account():
return render_template('account.html', title='Account')
# When the user is ready to log out:
#app.route("/logout")
#login_required
def logout():
logout_user()
return redirect(url_for('home'))
You can then access the logged-in user with the current_user proxy, which is available in every template:
{% if current_user.is_authenticated %}
Hi {{ current_user.username }}!
{% endif %}
By default, when a user attempts to access a login_required view without being logged in, Flask-Login will flash a message and redirect them to the log in view. (If the login view is not set, it will abort with a 401 error.)
The name of the log in view can be set as LoginManager.login_view:
login_manager.login_view = 'login'
# __init__.py
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_bcrypt import Bcrypt
from flask_login import LoginManager
app = Flask(__name__)
app.config['SECRET_KEY'] = 'ENTER_SECRET_KEY'
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///site.db'
db = SQLAlchemy(app)
bcrypt = Bcrypt(app)
login_manager = LoginManager(app)
login_manager.login_view = 'login'
login_manager.login_message_category = 'info'
from site import routes
And finally, run the project:
# run.py
from site import app
if __name__ == '__main__':
app.run(debug=True)
I hope that, in addition to a great explanation, this simple example is helpful.
Here is a Flask example of using login: https://bitbucket.org/leafstorm/flask-login/src/3160dbfc7cfc/example/login-example.py You need to use #login_required for every method that requires login. For example,
#app.route('/make-login')
#login_required
def make_login():
...
flask-login asks for a User object per request by calling user_loader.
If you use DB each time, you can expect a performance hit. (accepted answer suffers from this)
Your login route, on the other hand, is only called once during the session.
So the typical (session based) implementation should:
Fetch data from DB in your /login route, and cache it in session
Load user from cache in user_loader
Something like this:
#app.route("/login")
def login_callback():
user_data=my_fetch_from_db_based_on_whatever()
if my_check_credentials_ok(user_data)
session["user"]=user_data
login_user(User(user_data))
else:
abort(400)
:
#login_manager.user_loader
def load_user(user_id):
user_data = session["user"]
user=User(user_data)
return user if user.userid==user_id else None

Categories

Resources