I'm trying to create a function that can be scheduled to delete all rows within an SQLAlchemy model.
I'm trying to use apscheduler to accomplish this task. But I keep getting an error that says:
sqlalchemy.orm.exc.UnmappedInstanceError: Class 'flask_sqlalchemy.model.DefaultMeta' is
not mapped; was a class (app.models.User) supplied where an instance was required?
Am I missing something?
Here is my app/__init__.py:
from flask import Flask
from flask_login import LoginManager
from flask_sqlalchemy import SQLAlchemy
from config import Config
app = Flask(__name__)
db = SQLAlchemy()
login = LoginManager()
app.config.from_object(Config)
db.init_app(app)
login.init_app(app)
login.login_view = 'login'
from app import routes, models
and here is my manage.py:
from apscheduler.schedulers.background import BackgroundScheduler
from app import app, db
from flask_migrate import Migrate
from flask_script import Manager
from app.models import User
manager = Manager(app)
migrate = Migrate(app, db)
def clear_data():
db.session.delete(User)
print("Deleted User table!")
#manager.command
def run():
scheduler = BackgroundScheduler()
scheduler.add_job(clear_data, trigger='interval', seconds=5)
scheduler.start()
app.run(debug=True)
if __name__ == '__main__':
manager.run()
Also, here's my model:
from app import db, login
from datetime import datetime
from flask_login import UserMixin
from werkzeug.security import generate_password_hash, check_password_hash
class User(UserMixin, db.Model):
id = db.Column(db.Integer, primary_key=True, unique=True)
api_token = db.Column(db.String(50), unique=True)
username = db.Column(db.String(64), index=True, unique=True)
email = db.Column(db.String(120), index=True, unique=True)
password_hash = db.Column(db.String(128))
todos = db.relationship('Todo', backref='owner', lazy='dynamic')
def __repr__(self):
return '<models.py {}>'.format(self.username)
def set_password(self, password):
self.password_hash = generate_password_hash(password)
def check_password(self, password):
return check_password_hash(self.password_hash, password)
#login.user_loader
def load_user(id):
return User.query.get(int(id))
class Todo(db.Model):
id = db.Column(db.Integer, primary_key=True)
body = db.Column(db.String(140))
timestamp = db.Column(db.DateTime, index=True, default=datetime.utcnow)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
def __repr__(self):
return '<Todo {}>'.format(self.body)
As your error indicated, it is expecting an instances of an object, and you instead passed it a class. I think the issue is the first line in the clear_data function:
db.session.delete(User)
It was expecting an instances of a User record to delete, and doesn't know how to delete the whole table using just the model.
Check out this answer on how to delete all rows in a table. There are a few ways to do this, but this may be the least change for you:
db.session.query(User).delete()
In this case you are adding the step of SELECTing all the records in the table User maps to, then deleting them.
P.S.: as mentioned in the linked answer, you need to .commit() your session, otherwise it won't stick, and will rollback after you close the connection.
db.session.commit()
Code snippet:
db.session.query(model_name).delete()
db.session.commit()
Related
I am creating my first larger Python Flask app and one of the modules I will be using is Flask_User. To make all a bit more manageable I wanted to impost a blueprint and factory app.
The structure is simple:
app.py
config.py
auth/
__init__.py
models.py
So the goal is to have all authentication stuff in auth section. However, it comes down to where to initialize UserManager not to have errors, circular references, etc.
user_manager = UserManager(app, db, User)
How code looks like - app.py
from flask import Flask
# Configuration
from config import Config, HomeDevelopmentConfig
# Extensions
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
# INITIATE DB
db = SQLAlchemy()
migrate = Migrate() # this will run SQLAlchemy as well
from flask_user import UserManager
from auth.models import User
user_manager = UserManager()
# APPLICATION FACTORY
def create_app(config_class=Config):
# Create app object
app = Flask(__name__)
# Load configuration
app.config.from_object(config_class)
# Register extensions
db.init_app(app)
migrate.init_app(app, db)
user_manager = UserManager(app, db, User) # !!! this will not work - circular reference to User as User needs db
# Close session with DB at the app shut down
#app.teardown_request
def shutdown_session(exception=None):
db.session.remove()
with app.app_context():
# Register blueprints
# Main flask app
from main import bp_main
app.register_blueprint(bp_main)
# Authentication (flask-user)
from auth import bp_auth
app.register_blueprint(bp_auth)
return app
# RUN APPLICATION
if __name__ == '__main__':
app = create_app(HomeDevelopmentConfig)
app.run()
models.py (should contain data structure for authentication)
from app import db
from flask_user import UserMixin #, UserManager
class User(db.Model, UserMixin):
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)
email = db.Column(db.String(100), nullable=False, unique=True)
password = db.Column(db.String(255), nullable=False, server_default='')
first_name = db.Column(db.String(100), nullable=False, server_default='')
last_name = db.Column(db.String(100), nullable=False, server_default='')
email_confirmed_at = db.Column(db.DateTime())
active = db.Column('is_active', db.Boolean(), nullable=False, server_default='0')
# Define the relationship to Role via UserRoles
roles = db.relationship('Role', secondary='user_roles')
class Role(db.Model):
__tablename__ = 'roles'
id = db.Column(db.Integer(), primary_key=True)
name = db.Column(db.String(50), unique=True)
class UserRoles(db.Model, UserMixin):
__tablename__ = 'user_roles'
id = db.Column(db.Integer(), primary_key=True)
user_id = db.Column(db.Integer(), db.ForeignKey('users.id', ondelete='CASCADE'))
role_id = db.Column(db.Integer(), db.ForeignKey('roles.id', ondelete='CASCADE'))
and finally init.py for authentication:
from flask import blueprints
bp_auth = blueprints.Blueprint('auth', __name__)
from auth import models
Looks like mission impossible, but maybe you have some good hints. What works is to put all the code into app.py, but that make
I am new to flask been working on a project for a month. I have already separated all the code into file and blueprints and I want to delete all my tables by doing db.drop_all() in cmd. So i can start my database entries from scratch, but I get RuntimeError: No application found. Either work inside a view function or push an application context.
Here are part of my codes
the whole __init__.py:
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_bcrypt import Bcrypt
from flask_login import LoginManager
from flask_mail import Mail
from testapp.config import Config
db=SQLAlchemy()
bcrypt = Bcrypt()
login_manager = LoginManager()
login_manager.login_view = 'users.login'
login_manager.login_message_category = 'info'
mail = Mail()
def create_app(config_class=Config):
app = Flask(__name__)
app.config.from_object(Config)
db.init_app(app)
bcrypt.init_app(app)
l login_manager.init_app(app)
mail.init_app(app)
from testapp.users.routes import users
from testapp.posts.routes import posts
app.register_blueprint(users)
app.register_blueprint(posts)
return app
My config.py:
import os
class Config:
SECRET_KEY = os.environ.get('SECRET_KEY')
SQLALCHEMY_DATABASE_URI = os.environ.get('SQLALCHEMY_DATABASE_URI')
MAIL_SERVER = 'smtp.gmail.com'
MAIL_PORT = 587
MAIL_USE_TLS = True
MAIL_USERNAME = os.environ.get('EMAIL_USERNAME')
MAIL_PASSWORD = os.environ.get('EMAIL_PASSWORD')
All my tables in models.py:
from datetime import datetime
from itsdangerous import TimedJSONWebSignatureSerializer as Serializer
from flask import current_app
from testapp 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)
posts = db.relationship('Post', backref='author', lazy=True)
def __repr__(self):
return f"User('{self.username}', '{self.email}', '{self.image_file}')"
class Post(db.Model, UserMixin):
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(100), nullable=False)
date_posted = db.Column(db.DateTime, default=datetime.utcnow, nullable=False)
content = db.Column(db.Text, nullable=False)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
def __repr__(self):
return f"User('{self.title}', '{self.date_posted}')"
Just to follow up on that question, i was just dumbly running db.drop_all() function without in the command line inserting the app context all i had to do was import and add the app_context() function inside db.drop_all().
And here i thought something in my code was wrong, So for the people with same question dont refer my code, but just this answer.
Thank you #Adrian Klaver for the help.
I am trying to filter data by username in Python and sql alchemy but somehow the filter is not working., i am able to filter by ID and add users., in below code, i just showed the method for filter by username. not sure what wrong i am doing
from flask import Flask, request, jsonify
from flask_sqlalchemy import SQLAlchemy
from flask_marshmallow import Marshmallow
import os
app = Flask(__name__)
basedir = os.path.abspath(os.path.dirname(__file__))
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///' + os.path.join(basedir,
'datas.sqlite')
db = SQLAlchemy(app)
ma = Marshmallow(app)
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(80), unique=True)
email = db.Column(db.String(120), unique=True)
def __init__(self, username, email):
self.username = username
self.email = email
class UserSchema(ma.Schema):
class Meta:
# Fields to expose
fields = ('username', 'email')
user_schema = UserSchema()
users_schema = UserSchema(many=True)
#endpointtogetuserdetailbyusername
#app.route("/username/<username>",methods=["GET"])
def user_detailbyusername(username):
user=User.query.filter(User.username=='Tim')
# i tried user=User.query.filter_by(User.username=='Tim') but no luck
return user_schema.jsonify(user)
if __name__ == '__main__':
app.run(debug=True)
I think you should add some method to really get back the records, something like User.query.filter(User.username=='Tim').all() or User.query.filter(User.username=='Tim').first(), as can be seen in the examples at http://flask-sqlalchemy.pocoo.org/2.3/queries/#querying-records
I have read quite a bit of documentation and I can't see what is wrong with these lines
update_this = User.query.filter_by(email=email).first()
update_this.emailconfirmed = True
db.session.commit()
...and yet when I deploy the boolean column 'emailconfirmed' never is update to True. I have confirmed with print statements that update_this.emailconfirmed is False at the exact point in the code shown above... I just can't seem to update that value. Does anybody know what tests I can do, what imports I should check etc. etc.
Right now this is the top of my main .py file where the above code appears
from flask import Flask, render_template, request, session, redirect, url_for, make_response
# the following line imports from models.py
from models import db, User
# the following line imports SignupForm from forms.py
from forms import SignupForm, LoginForm
from flask_mail import Mail, Message
from itsdangerous import URLSafeTimedSerializer
# Production (causes Heroku to redirect to SSL)
from flask_sslify import SSLify
from flask_sqlalchemy import SQLAlchemy
import os
app = Flask(__name__)
sslify = SSLify(app)
sslify = SSLify(app, subdomains=True)
app.config.from_pyfile('config_file.cfg')
app.config['SQLALCHEMY_DATABASE_URI'] = os.environ['DATABASE_URL']
db = SQLAlchemy(app)
mail = Mail(app)
ts = URLSafeTimedSerializer(app.config['SECRET_KEY'], salt=app.config['SALT'])
and this is my models.py file
from flask_sqlalchemy import SQLAlchemy
from werkzeug import generate_password_hash, check_password_hash
db = SQLAlchemy()
class User(db.Model):
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)
firstname = db.Column(db.String(100))
lastname = db.Column(db.String(100))
role = db.Column(db.String(20))
roleapproved = db.Column(db.Boolean)
school = db.Column(db.String(100))
email = db.Column(db.String(120), unique=True)
emailconfirmed = db.Column(db.Boolean)
pwdhash = db.Column(db.String(100))
def __init__(self, firstname, lastname, role, school, email, password):
self.firstname = firstname.title()
self.lastname = lastname.title()
self.role = role.lower()
if role.lower() == 'student':
self.roleapproved = True
if role.lower() == 'teacher':
self.roleapproved = False
self.school = school.title()
self.email = email.lower()
self.set_password(password)
self.emailconfirmed = False
def set_password(self, password):
self.pwdhash = generate_password_hash(password)
def check_password(self, password):
return check_password_hash(self.pwdhash, password)
def __repr__(self):
return '<User {0}>'.format(self.email)
Any help on doing the update I mentioned above would be greatly appreciated!!
Ideally you want to maintain a single session throughout your application lifecycle. This way it makes it easy to reason about and you avoid binding sessions to individual models.
Thanks #Ilja Everila
In main.py instead of initializing SQLAlchemy
you should write,
db.init_app(app)
Define a save instance method for your User model.
def save(self):
"""Saves model object instance
"""
db.session.add(self)
db.session.commit()
You can call this method to save the instance as
update_this.save()
Another way to update the entity is to get the specific object session before committing
from sqlachemy.orm import session
...
session = session.object_session(update_this)
session.commit()
The two modules server.py and models.py are in the same directory. I import the models before running the create_all but the User table is never created. I have also tried changing import models to from models import User with the same result. Please help me understand what is going on here.
Server.py:
from flask import Flask
from fileserver.filebutler import butler
from flask.ext.sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.register_blueprint(butler)
app.config['SQLALCHEMY_DATABASE_URI'] = 'postgresql://working_url'
db = SQLAlchemy(app)
def initialize_db():
import models
db.create_all()
if __name__ == '__main__':
initialize_db()
app.run(debug=True)
models.py:
from sqlalchemy import Column, Integer, String
from downloadr import db
class User(db.Model):
__tablename__ = 'users'
id = Column(Integer, primary_key=True)
name = Column(String(50), unique=True)
email = Column(String(120), unique=True)
def __init__(self, name=None, email=None):
self.name = name
self.email = email
def __repr__(self):
return '<User %r>' % (self.name)
Try:
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(50), unique=True)
email = db.Column(db.String(120), unique=True)
Use:
from models import *
It works fine in my case (i'm not using a function to init though, just 2 lines of code in the if clause).