I have a problem here.
I want to populate a table in the database with a record when the migration is executed, but actually it doesn't work. I'm using SQLAlchemy for the DB and the Manager object to create the DB.
In the DB I have a table called 'skills' which contains objects of type 'Skill'.
I just want to fill the table with a record during the initialization phase.
Please help me in finding the solution.
manage.py
#!/usr/bin/env python
import os
from app import create_app, db
from app.models import Skill
from flask_script import Manager, Shell
from flask_migrate import Migrate, MigrateCommand
app = create_app(os.getenv('FLASK_CONFIG') or 'default')
manager = Manager(app)
migrate = Migrate(app, db)
def make_shell_context():
return dict(app=app, db=db, Skill=Skill)
manager.add_command("shell", Shell(make_context=make_shell_context))
manager.add_command('db', MigrateCommand)
#manager.command
def create_skill():
db.session.add(Skill(name='ciaociao', in_use=False))
db.session.commit()
if __name__ == '__main__':
manager.run()
init.py
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_login import LoginManager
from config import config
db = SQLAlchemy()
login_manager = LoginManager()
login_manager.session_protection = 'strong'
login_manager.login_view = 'auth.login'
def create_app(config_name):
app = Flask(__name__)
app.config.from_object(config[config_name])
config[config_name].init_app(app)
db.init_app(app)
login_manager.init_app(app)
return app
models.py
class Skill(db.Model):
__tablename__ = 'skills'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(64), index=True)
in_use = db.Column(db.Boolean, default=False)
Related
I am new comer of flask api. I am doing a flask restful api project which purpose is to search stores by store name that is kept in existing database online.
Now is is connect to database using psycopg2.
I would like to improve it by using flask-sqlalchemy. I have searched on the web and the solution seems to use Automap, that I learned from the tutorial. The problem I am facing now is how to integrate those codes into my existing project, like where should I put the settings like automap_base() into my existing codes. Here is my current file structure:
app.py
∟ models
∟ family_mart.py
∟ resources
∟ family_mart.py
app.py:
from flask import Flask
from flask_restful import Api
from resources.family_mart import FamilyMart
app = Flask(__name__)
app.secret_key = 'apple'
api = Api(app)
api.add_resource(FamilyMart, '/familymart/<string:name>')
if __name__ == '__main__':
app.run(port=5000, debug=True)
family_mart.py in models folder:
import psycopg2
from config import config
class FamilyMartModel:
#classmethod
def find_by_name(cls, name):
conn = None
try:
params = config()
conn = psycopg2.connect(**params)
cur = conn.cursor()
query = f"""
SELECT extract_date
, store_name
, address
FROM cnvnt_str_fm
WHERE store_name = '{name}'
"""
cur.execute(query)
rows = cur.fetchall()
cur.close()
if rows:
result = {'store':[]}
for row in rows:
append_dict = {'extract_date':row[0].strftime('%Y-%m-%d'),
'store_name':row[1],
'address':row[2]}
result['store'].append(append_dict)
return result
except (Exception, psycopg2.DatabaseError) as error:
print(error)
finally:
if conn is not None:
conn.close()
print('Database connection closed.')
family_mart.py in resources folder:
from flask_restful import Resource
from models.family_mart import FamilyMartModel
class FamilyMart(Resource):
def get(self, name):
item = FamilyMartModel.find_by_name(name)
if item:
return item
return {'message':'Store not found.'}, 404
After study the documentation, I restructured my project and integrate with sqlalchemy successfully. I removed model folder and added db.py to configure the existing database, here is the detail :
New file structure:
app.py
∟ resources
∟ family_mart.py
db.py
app.py:
from flask import Flask
from flask_restful import Api
from config import config
from db import db
from resources.family_mart import FamilyMart
params = config()
DB_FULL_URL = f"postgresql+psycopg2://{params['user']}:{params['password']}#{params['host']}/{params['database']}"
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = DB_FULL_URL
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
app.secret_key = 'apple'
api = Api(app)
api.add_resource(FamilyMart, '/familymart/<string:name>')
if __name__ == '__main__':
db.init_app(app)
app.run(port=5000, debug=True)
db.py:
from flask_sqlalchemy import SQLAlchemy
from sqlalchemy import MetaData
from sqlalchemy import Table, Column, Integer, String, DateTime
db = SQLAlchemy()
metadata_obj = MetaData()
fm_table = Table('cnvnt_str_fm', metadata_obj,
Column('extract_date', String),
Column('store_name', String),
Column('address', String)
)
family_mart.py:
from flask_restful import Resource
from db import db
from db import fm_table
class FamilyMart(Resource):
def get(self, name):
results = db.session.query(fm_table).filter_by(store_name=name).\
order_by(fm_table.c.extract_date.desc()).limit(3)
output = {'store':[]}
for row in results:
extract_date = str(row[0])
store_name = row[1]
address = row[2]
output['store'].append({'extract_date':extract_date,
'store_name':store_name,
'address':address})
return output
Local everything works fine but when I go to pythoneverywhere I get error: Cannot import name "User" from "Data.Domain.User", without Flask-Migrate db works just fine, also, I tried to copy the content of Data.Domain.User module to app.py and it works, the problem appears just when I want to move it on another folder ( location ).
This is what I have in app.py:
from flask import Flask
from flask_bcrypt import Bcrypt
from flask_sqlalchemy import SQLAlchemy
from flask_script import Manager, Server
from flask_migrate import Migrate, MigrateCommand
from flask_login import LoginManager
from flask_mail import Mail, Message
app = Flask(__name__)
app.config['SECRET_KEY'] = 'blablablastrongsecretkey'
app.config['SQLALCHEMY_DATABASE_URI'] = 'connstring'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db = SQLAlchemy(app)
migrate = Migrate(app, db)
manager = Manager(app)
manager.add_command('db', MigrateCommand)
login_manager = LoginManager(app)
from Data.Domain.User import User
from Presentation import App
if __name__ == '__main__':
manager.run()
Module Data.Domain.User is this:
from app 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)
uuid = db.Column(db.String(50), unique=True)
username = db.Column(db.String(40), unique=True)
def __repr__(self):
return "User(\n\tID: " + str(self.uuid) + "\n\tUsername: " + self.username + "\n)"
I solved the problem renaming and moving the app.py file in another folder, and then I just imported app from it
from Presentation.Server import app
This is the import from Data.Domain.User file
new to web developing. I have three files. database_setup.py with my table classes. restaurant.py used to populate tables and script.py for flask app
database_setup.py
import os
import sys
from sqlalchemy import Column, ForeignKey, Integer, String
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import relationship
from sqlalchemy import create_engine
Base = declarative_base()
class Restaurant(Base):
__tablename__ = 'restaurant'
id = Column(Integer, primary_key=True)
name = Column(String(250), nullable=False)
engine = create_engine('sqlite:///restaurantmenu.db')
Base.metadata.create_all(engine)
restaurant.py
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from database_setup import Restaurant, Base, MenuItem
engine = create_engine('sqlite:///restaurantmenu.db')
Base.metadata.bind = engine
DBSession = sessionmaker(bind=engine)
session = DBSession()
restaurant = Restaurant(name= "Kasra")
session.add(restaurant)
session.commit()
script.py
from flask import Flask
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker,scoped_session
from database_setup import Base, Restaurant
app = Flask(__name__)
engine = create_engine('sqlite:///restaurantmenu.db')
Base.metadata.bind = engine
DBSession = scoped_session(sessionmaker(bind=engine))
session = DBSession()
#app.route('/')
#app.route('/hello')
def HelloWorld():
restaurant = session.query(Restaurant).first()
return restaurant.name
if __name__ == "__main__":
app.run(debug=True)
What I want to do is import the database that was created in restarant.py to script.py. When I run the app it creates a new and empty database because I'm using "engine = "create_engine()" in script.py and I need to use it in order to bind with engine and use session.
The error I get when running app "AttributeError: 'NoneType' object has no attribute 'name'"
Here is what I do to operate with Flask and SQLAlchemy:
In a database.py file:
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker
from models import Base
engine = create_engine(Config.SQLALCHEMY_DATABASE_URI)
session_factory = sessionmaker(bind=engine)
def init_db():
Base.metadata.create_all(bind=engine)
All model classes are located in a models.py file, and are also derived from the declarative base:
Base = declarative_base()
Then the start-up python script contains (key parts only):
app = Flask(__name__)
session = flask_scoped_session(session_factory, app)
if __name__ == "__main__":
database.init_db()
app.run()
I am creating a flask application where I am using the app factory method. I have a file in the application folder __init__.py which has the create_app function with the following code
def create_app(test_config=None):
app = Flask(__name__,instance_relative_config=True)
app.config.from_mapping(
SECRET_KEY='dev',
)
app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql+pymysql://root:#localhost/database'
db = SQLAlchemy(app)
if test_config == None:
app.config.from_pyfile('config.py',silent=True)
else:
app.config.form_mapping(test_config)
from flaskr import models
try:
os.makedirs(app.instance_path)
except OSError:
pass
class User(db.Model):
id = db.Column(db.Integer,primary_key=True)
uname = db.Column(db.String(50))
#app.route('/hello')
def hello():
return json.dumps({'message':'hello','status':True})
#app.route('/getusers')
def getusers():
u = User.query.get(1)
return json.dumps({'uname':u.uname})
return app
What I want is to define the models in a seperate file. How can I do this?
I have tried defining in a seperate file and importing it. But the problem is the model inherits the db.Model which is then not available in the imported file.
Leave the creation of db object outside create_app without passing any app instance and use the SQLAlchemy.init_app method to configure and init. your db object, this way you can import it from any file:
db = SQLAlchemy()
#...
def create_app(test_config=None):
app = Flask(__name__,instance_relative_config=True)
#...
db.init_app(app)
More on this topic can be found at flask's documentation
I've fought with this problem a few hours too. Couldn't fix it until I came to the realization, that I have to return the app inside the app.app_context()
model.py:
from flask_user import UserMixin
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
class User(db.Model, UserMixin):
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)
email = db.Column(db.String(255, collation='NOCASE'), nullable=False,
unique=True)
# ...
__init__.py:
from flask import Flask
from flask_migrate import Migrate
def create_app(test_config=None):
app = Flask(__name__, instance_relative_config=True)
app.config.from_pyfile('config.py', silent=True)
migrate = Migrate()
from appname.model import db
db.init_app(app)
migrate.init_app(app, db)
with app.app_context():
# Create all database tables
db.create_all()
import appname.routes
# Apply the blueprints to the app
from appname import bp1, bp2
appname.register_blueprint(bp1.bp)
appname.register_blueprint(bp2.bp)
return app
Run with:
cd ~/Folder/Webapp
. appname/bin/activate
export FLASK_APP=appname
export FLASK_ENV=development
flask run
(in Linux terminal)
or create this file and run it in python shell:
from appname import create_app
if __name__ == '__main__':
app = create_app()
app.run(debug=True)
I know this is probably a circular import error but I cannot figure out a solution. The application, so far, is very vanilla. It is the flask-security tutorial with a models.py added. This is in preparation to add blueprints to the project.
from app import db
ImportError: cannot import name 'db'
config.py
run.py
---app
------__init__py
------models.py
run.py
from app import app
if __name__ == "__main__":
app.run()
init.py
from config import Config
import app.models
from flask import Flask, render_template
from flask_sqlalchemy import SQLAlchemy
from flask_security import Security, SQLAlchemyUserDatastore, \
login_required
db = SQLAlchemy()
# Create app
def create_app():
app = Flask(__name__)
app.config.from_object(Config)
app.config['DEBUG'] = True
db.init_app(app)
user_datastore = SQLAlchemyUserDatastore(db, models.Users,
models.usersRoleNames)
security = Security(app, user_datastore)
return app
# Views
#app.route('/')
#login_required
def home():
return render_template('index.html')
models.py
from app import db
from flask_security import UserMixin, RoleMixin
users_roles = db.Table('users_roles',
db.Column('user_id', db.Integer(), db.ForeignKey('users.id')),
db.Column('role_id', db.Integer(), db.ForeignKey('users_role_names.id')))
class usersRoleNames(db.Model, RoleMixin):
id = db.Column(db.Integer(), primary_key=True)
name = db.Column(db.String(80), unique=True)
description = db.Column(db.String(255))
class Users(db.Model, UserMixin):
id = db.Column(db.Integer, primary_key=True)
email = db.Column(db.String(255), unique=True)
username = db.Column(db.String(255))
password = db.Column(db.String(255))
last_login_at = db.Column(db.DateTime())
current_login_at = db.Column(db.DateTime())
last_login_ip = db.Column(db.String(100))
current_login_ip = db.Column(db.String(100))
login_count = db.Column(db.Integer)
active = db.Column(db.Boolean())
confirmed_at = db.Column(db.DateTime())
roles = db.relationship('usersRoleNames', secondary=users_roles,
backref=db.backref('users',
lazy='dynamic'))
Try declaring your db object in models.py and import it in app/__init_.py
models.py
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
...
app/__init__.py
def create_app():
app = Flask(__name__)
app.config.from_object(Config)
app.config['DEBUG'] = True
from app.models import db, Users, usersRoleNames
db.init_app(app)
user_datastore = SQLAlchemyUserDatastore(db, Users,
usersRoleNames)
security = Security(app, user_datastore)
return app
I have found a simple solution to the circular import problem with flask-security. This solution is particularly useful when the models are declared across different files.
1. Create a folder named models:
All our models will live inside this folder.
Sample folder structure could look like this
models/
├── _db.py
├── __init__.py
├── tickets.py
└── users.py
0 directories, 4 files
2. Create a file named _db.py:
Declare the db object in this file.
The file name could be anything but starting the file name with an underscore ensures that the db object is always imported first when the import statements are sorted.
('VS Code provided an option to automatically sort the imports, so using that doesn't cause any issues.')
_db.py
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
3. Import db object from _db.py file:
Now we can import the db object from the _db.py file created above:
users.py
from ._db import db
from flask_security import RoleMixin, UserMixin
users_roles = db.Table(
# Table definition
)
class usersRoleNames(db.Model, RoleMixin):
# Table definition
...
class Users(db.Model, UserMixin):
# Table definition
...
4. Import everything in __init__.py file (optional):
Create a init.py file and import all the models and the db object inside it.
__init__.py
from ._db import db
from .tickets import Tickets
from .users import Users, users_roles, usersRoleNames
'''
Below part is optional
but I like to do it anyways
'''
__all__ = [
'db',
'Tickets',
'Users',
'users_roles',
'usersRoleNames',
]
By importing everything inside the __init__.py file we can import from models folder directly.
So we can do
from .models import db, Tickets, Users
Instead of this
from .models._db import _db
from .models.tickets import Tickets
from .models.users import Users
Just a thought: I am not sure if it's a bad practice to start a python file name with an 'underscore'. If you have an opinion please share your thoughts.