Flask-SQLAlchemy's create_all does not create tables - python

I'm working on a flask application where I'm trying to isolate my unit tests. I'm using flask-sqlalchemy, and I'm trying to use the create_all and drop_all methods to clean my database after running a test.
However, it appears my create_all and drop_all methods do not actually create/drop the tables as the documentation states. I have my models imported in the application before calling create_all, like most other answers say.
This is the error I'm getting with the code below:
psycopg2.ProgrammingError: relation "tasks" does not exist
Here's my relevant code
/app.py
import os
import configparser
from flask import Flask
from src.router import router
from flask_sqlalchemy import SQLAlchemy
from flask_migrate import Migrate
app = Flask(__name__)
if not os.path.exists(os.path.join(app.root_path, 'config.ini')):
raise Exception(f'config.ini not found in the {app.root_path}')
config = configparser.ConfigParser()
config.read('config.ini')
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.config['SQLALCHEMY_DATABASE_URI'] = config[os.environ['APP_ENV']]['DATABASE_URI']
app.register_blueprint(router)
db = SQLAlchemy(app)
migrate = Migrate(app, db)
if __name__ == "__main__":
app.run()
/tests/test_router.py
from unittest import TestCase
from flask import Flask
from app import app, db
from src.models import Task
class TestRouter(TestCase):
def setUp(self):
db.create_all()
def tearDown(self):
db.drop_all()
def test_adds_task(self):
task = Task(task_id='task_1', name='my task')
db.session.add(task)
db.session.commit()

I think I was a little quick to post the question, but I hope this might help others come up with other ideas on how to troubleshoot a similar issue.
In my src/models.py file where I keep my models, you must make sure that your models are defined correctly. Since Flask-SQLAlchemy is a wrapper around the SQLAlchemy you must use the data types under the db object.
Essentially, I had my models defined as such:
class Task(db.Model):
__tablename__ = 'tasks'
id = Column(Integer, primary_key=True)
task_id = Column(String)
name = Column(String)
created_at = Column(DateTime, default=datetime.datetime.now)
As you can see, I was inheriting from db.Model instead of the return value of declarative_base(). I also needed to add the db. in front of all the data types, including Column, Integer, String, Float, DateTime, relationship, and ForeignKey.
So, I was able to fix my issue by changing my model to something like:
class Task(db.Model):
__tablename__ = 'tasks'
id = db.Column(db.Integer, primary_key=True)
task_id = db.Column(db.String)
name = db.Column(db.String)
created_at = db.Column(db.DateTime, default=datetime.datetime.now)
See: Documentation on declaring Flask-SQLAlchemy models

Related

Flask-Migrate is just detecting the unique constraints not the whole model

When I try to use flask-migrate with my model and a Postgres database, it does not work. Even if the database is created at startup. Every time migrate only detects the following:
INFO [alembic.autogenerate.compare] Detected added unique constraint 'None' on '['Auth_ID']'
INFO [alembic.autogenerate.compare] Detected added unique constraint 'None' on '['ClosedCourse_ID']'
My Model (much larger but is poorly designed like this)
class SKAuthentication(db.Model):
__tablename__ = 'sk-authentication'
Auth_ID = db.Column(UUID(as_uuid=True), primary_key=True, unique=True)
Basic_Auth = db.Column(Text, nullable=False)
Basic_User = db.Column(Text, nullable=False)
Teacher_ID = db.Column(UUID(as_uuid=True), db.ForeignKey('sk-teacher.Teacher_ID'), nullable=False)
Model = db.Column(Text, nullable=True)
Phone_ID = db.Column(Text, nullable=True)
Brand = db.Column(Text, nullable=True)
VersionInstalled = db.Column(Text, nullable=False, default='0.0.0')
def __init__(self, teacher_id):
chars = string.digits + string.ascii_letters + string.punctuation
self.Basic_Password = ''.join(secrets.choice(chars) for _ in range(256))
self.Auth_ID = create_ID(teacher_id, token_1, auth_crypt)
self.Basic_Auth = sha512_crypt.encrypt(self.Basic_Password)
self.Basic_User = create_ID(self.Basic_Auth, self.Auth_Secret)
self.Teacher_ID = teacher_id
class SKDayilyClosedCourse(db.Model):
__tablename__ = 'sk-daily-closed-course'
ClosedCourse_ID = db.Column(UUID(as_uuid=True), primary_key=True, unique=True)
Teachers_Group_ID = db.Column(UUID(as_uuid=True), db.ForeignKey('sk-teachers-groups.Row_ID'), nullable=False)
Course_Date = db.Column(Date, nullable=False)
Closed = db.Column(BOOLEAN, nullable=False, default=False)
Reminded = db.Column(BOOLEAN, nullable=False, default=False)
def __init__(self, teachers_group_id, course_date, reminded):
self.ClosedCourse_ID = create_ID(teachers_group_id, False, False, course_date)
self.Teachers_Group_ID = teachers_group_id
self.Course_Date = course_date
self.Reminded = reminded
My run.py looks like this:
from flask import Flask, session
from flask_sqlalchemy import SQLAlchemy
from modules.extensions import csrf, migr, cors
from config import DevelopmentConfig
from flask_migrate import upgrade, migrate
db = SQLAlchemy()
migr = Migrate()
def create_app():
app = Flask(__name__)
csrf.init_app(app)
app.config.from_object(DevelopmentConfig)
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db.init_app(app)
cors.init_app(app, resources={r"/mobile/v1/*": {"origins": "GET, POST, OPTIONS"}})
migr.init_app(app, db, render_as_batch=False)
with app.app_context():
import modules.database.model
db.create_all()
db.session.commit()
migrate()
upgrade()
from application import application
from application import application_ticket
from application import application_mobile
from application import application_bookings
app.register_blueprint(application.application)
app.register_blueprint(application_ticket.ticket, url_prefix="/ticket")
app.register_blueprint(application_mobile.mobile, url_prefix="/mobile/v1")
app.register_blueprint(application_bookings.bookings, url_prefix="/bookings/v1")
#app.before_request
def before_request():
session.permanent = True
app.permanent_session_lifetime = timedelta(minutes=1)
return app
This is the important part form my extensions.py
from flask_wtf import CSRFProtect
from flask_migrate import Migrate
from flask_cors import CORS
csrf = CSRFProtect()
migr = Migrate()
cors = CORS()
...
And that's my wsgi.py:
from run import create_app
app = create_app()
Once it worked, but I don't know how. I didn't change anything, so I tried to reproduce it and I encountered the same problem I mentioned before. I am not sure how to proceed with this problem.
I tried to create the tables inside the models.py and try to migrate everything from there, but still just the unique constraints.
Another way, I figured out was to import the models, which I also needed to create the tables with Flask-SQLAlchemy, but that changed nothing.
Next solution I've found was, to run everything from console. Nothing changed.
Other solution: Drop Table "alembic_version" -> didn't changed anything.
Hopefully someone can help me soon!
I am expecting to get following output:
INFO [alembic.autogenerate.compare] Detected added table '[<Tablename>]'
...
Thank you all!

How to create separate file to generate different table of database Flask-SqlAlchemy?

I using Flask-Restplus and SqlAlchemy to create my table and Api.The problem I facing is like below:
At first I have this user.py which having 2 table inside:
User.py
class User(db.Model):
__tablename__ = "users_info"
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
name = db.Column(db.Integer, unique=True, nullable=True)
device = db.relationship('Device', backref='user')
# .. some other field here
class Device(db.Model):
__tablename__ = "user_device"
device_id = db.Column(db.Integer, primary_key=True, autoincrement=True)
id = db.Column(db.Integer, db.ForeignKey(User.id))
# .. some other field here
At this point,I using this 2 command below with manage.py to create the database,and have no problem.
python manage.py db migrate
python manage.py db upgrade
manage.py
import os
import unittest
from flask_migrate import Migrate, MigrateCommand
from flask_script import Manager
from app import blueprint
from app.main import create_app, db
app = create_app(os.getenv('My_App') or 'dev')
app.register_blueprint(blueprint)
app.app_context().push()
manager = Manager(app)
migrate = Migrate(app, db)
manager.add_command('db', MigrateCommand)
#manager.command
def run():
app.run()
if __name__ == '__main__':
manager.run()
Now I want to create another file called product.py,which is a new table for the same database like below:
Product.py
from .. import db
from .model.user import User
class Product(db.Model):
__tablename__ = "product"
product_id = db.Column(db.Integer, primary_key=True, autoincrement=True)
product_name = db.Column(db.String)
# plan here what you want in this table
user_id = db.Column(db.Integer, db.ForeignKey(User.user_id))
As you can see Product class is having relationship with User with the foreignKey.
Result:
But I use the same command above to migrate again,the result is look like this :
INFO [alembic.runtime.migration] Context impl SQLiteImpl.
INFO [alembic.runtime.migration] Will assume non-transactional DDL.
INFO [alembic.env] No changes in schema detected.
It seems like didn't detect the new table from my new file.
My Question:
How to create separate file to generate different table of database?
I think the problem is that the models aren't being imported, which means that SQLAlchemy and Alembic do not know about them.
This is counterintuitive to some people, but SQLAlchemy uses introspection to find out what models are defined, so in many cases the solution to your problem is to just import the models in your main script, even if you don't need to use them. Importing them will make them visible to SQLAlchemy and Alembic, and from there everything will work well.
In your case, you just need to add something like the following in manage.py:
from model.user import User
from model.product import Product
If this results in import errors due to circular dependencies, you may want to move the imports down below all other imports. If you define your db instance in the same script, then you want these imports below the line that defines the database, since obviously the database needs to be defined first.

AttributeError: 'BaseQuery' object has no attribute 'whoosh_search'

I am using Flask (python), creating whooshe search, I have followed all steps from this link, also tried from other links too, but every time at end I came to this error:
results = BlogPost.query.whoosh_search('cool')
AttributeError:'BaseQuery' object has no attribute 'whoosh_search'
Here is my model code:
class BlogPost(db.Model):
__tablename__ = 'blogpost'
__searchable__ = ['title', 'content']
__analyzer__ = StemmingAnalyzer()
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.Unicode) # Indexed fields are either String,
content = db.Column(db.Text) # Unicode, or Text
created = db.Column(db.DateTime, default=datetime.utcnow)
I am having error on this:
#app.route('/search')
def search():
results = BlogPost.query.whoosh_search('cool')
return results
I have also seen this error.Then I found the error is in models.py.You should add the whooshalchemy.whoosh_index(app, BlogPost) at the end of the model code.
I ran in to a similar issue if anyone falls down thsi rabbit hole.
In my case, i found that in my models.py i was importing from sqlalchemy and using the declarative_base as a base for each model.
ie:
from sqlalchemy import Column, Integer, DateTime, Text, Boolean
from sqlalchemy.ext.declarative import declarative_base
Base = declarative_base()
class Book(Base):
while in my routes.py I was importing flask-sqlalchemy where you use db.Model as the base:
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
db.init_app(app)
class Book(db.Model):
There have been some similar answers on some other posts as well but it looks like when using flask-whooshalchemy or its similar variants (whooshalchemy3, whooshalchemyplus) using db.Model as the base is the correct way to init the model to have the appropriate attributes.
hopefully that helps someone down the road.

unresolved attribute "Column" in class "SQLAlchemy"

i use pycharm 5.0 and python3.5.And i download all the liarbry by the build-in function of pycharm(setting-project-project interpreter-"+").other libraries appear well,but some problems happens to flask-SQLAlchemy.
i import flask-SQLAlchemy successfully.however,pycharm remind me that "unresolved attribute reference 'Column' in class'SQLAlchemy'"."unresolved attribute reference 'relationship' in class 'SQLAlchemy'" and so on.
I have try some ways ,but they didn't work.for example:1.restart 2.remove and redownload 3.refresh the cache.which mention in PyCharm shows unresolved references error for valid code
code:
from flask import Flask, redirect, render_template, session, url_for, flash
from flask_sqlalchemy import SQLAlchemy
from flask_bootstrap import Bootstrap
from flask_wtf import Form
from wtforms import StringField, SubmitField
import os
from wtforms.validators import data_required
basedir = os.path.abspath(os.path.dirname(__file__))
app = Flask(__name__)
app.config['SECRET_KEY'] = 'hard to guess string'
app.config['SQLALCHEMY_DATABASE_URI'] =\
'sqlite:///' + os.path.join(basedir, 'data.sqlite')
app.config['SQLALCHEMY_COMMIT_ON_TEARDOWN'] = True
bootstrap = Bootstrap(app)
db = SQLAlchemy(app)
class Role(db.Model):
__tablename__ = 'roles'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(64), unique=True)
users = db.relationship('User', backref='role', lazy='dynamic')
def __repr__(self):
return '<Role %r>' % self.name
class User(db.Model):
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(64), unique=True, index=True)
role_id = db.Column(db.Integer, db.ForeignKey('roles.id'))
def __repr__(self):
return '<User %r>' % self.username
how can i solve this problem?
The constructor of the flask_sqlalchemy.SQLAlchemy class calls _include_sqlalchemy, which attaches all attributes from sqlalchemy and sqlalchemy.orm to its instances.
This is only done at runtime and not detected by PyCharm's code inspection.
It would require flask_sqlalchemy to use a more standard way of importing those attributes, like from sqlalchemy import *. But this would import the attributes into the flask_sqlalchemy module instead of each instance of SQLAlchemy and thus change the way they're accessed.
I'm not a Python or SQLAlchemy expert and won't judge whether this is good design or not but you could open an issue on https://github.com/pallets/flask-sqlalchemy and discuss it there.
here is what I do.
from flask_sqlalchemy import SQLAlchemy
from typing import Callable
class MySQLAlchemy(SQLAlchemy): # Or you can add the below code on the SQLAlchemy directly if you think to modify the package code is acceptable.
Column: Callable # Use the typing to tell the IDE what the type is.
String: Callable
Integer: Callable
db = MySQLAlchemy(app)
class User(db.Model, UserMixin):
__tablename__ = "users"
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(20)) # The message will not show: Unresolved attribute reference 'Column' for class 'SQLAlchemy'
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
def create_test_data():
db.create_all()
test_user = User(name='Frank') # I add __init__, so it will not show you ``Unexpected argument``
db.session.add(test_user)
db.session.commit()
I've ran into the same problem just now.
In short if I just hit Run the code runs with exit code 0 even is PyCharm shows Unresolved attribute reference
but for anyone who might make the same mistake as I did before simply hitting Run:
This is the code that I was writing and it showed 'Unresolved attribute reference 'Column' for class 'SQLAlchemy'' also for eg. 'Unresolved attribute reference 'Integer' for class 'SQLAlchemy' '.
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///new-books-collection.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] =
False
db = SQLAlchemy(app)
class Books(db.Model):
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(250), unique=True, nullable=False)
author = db.Column(db.String(250), unique=True, nullable=False)
rating = db.Column(db.Float, nullable=False)
db.create_all()
My problem was that I hovered over Column and Integer and clicked Add method Column() to class SQLAlchemy. Also the same with Integer.
From that moment TypeErrors came up for me because it created these empty methods in __init__.py of SQLAlchemy.
To solve this I used pip install flask_sqlalchemy --upgrade and pip install flask_sqlalchemy. And did not Add methods again. It still showed Unresolved attribute reference, but the code ran with exit code 0 and the database was created, with the right data inside.
Hope that helps!
Check the version of flask_sqlalchemy in your pycharm or environment.
i had same problem ,the easiest solution
pip install flask_sqlalchemy --upgrade
100% its gone work.

creating tables from different models with sqlalchemy

I have different models e.g. model1.py, model2.py etc. Some how tables are being created following pocoo link, which required to be invoked from terminal.
But
def init_db():
import model.model1
import model.model2
Base.metadata.create_all(bind=engine)
This is not working, rather requires to be invoked from terminal.
>> from database import init_db
>> init_db() #works
database.py
from sqlalchemy import create_engine
from sqlalchemy.orm import scoped_session, sessionmaker
from sqlalchemy.ext.declarative import declarative_base
engine = create_engine('sqlite:///xyz.sqlite', echo=True)
db_session = scoped_session(sessionmaker(autocommit=False,
autoflush=False,
bind=engine))
Base = declarative_base()
Base.query = db_session.query_property()
def init_db():
import model.admin # from model.admin import User doesnt help either
import model.role
Base.metadata.create_all(bind=engine)
if __name__ == '__main__':
init_db()
admin.py
from sqlalchemy import Column, Integer, String
from database import Base
class User(Base):
__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)
There are no errors although an empty db file is generated.
How can database be created from multiple models?
I'm not sure why invoking directly from command line triggers table creation for you but I've always structured my Flask apps ala Digital Ocean's guide. Something that wasn't noted explicitly in the quide is the fact that you need to initialize your blueprints first before create_all is able to build the database tables for you.
(Your code as it is, lacks blueprints. Maybe try to create some first then try again?)

Categories

Resources