Schedule SQLAlchemy to clear all rows from a table - python

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

Flask_User and Blueprint - how to structure the app?

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

Trying to drop all tables in flask, RuntimeError: No application found. Either work inside a view function or push an application context

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.

SQL Alchemy filtering

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

update state with flask sqlalchemy with postgres will not commit to database

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

flask create_all not creating imported tables

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

Categories

Resources