My flask application has a single database(db1) before, now I bind a new database(db2) to it, both has 10 tables.
SQLALCHEMY_DATABASE_URI = 'mysql+pymysql://root#localhost:3306/db1'
SQLALCHEMY_BINDS = {
'test': 'mysql+pymysql://root#localhost:3306/db2'
}
db = SQLAlchemy()
class table1(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(80), unique=True)
.......
class table10(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(80), unique=True)
class table11(db.Model):
__bind_key__ = 'db2'
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(80), unique=True)
......
class table20(db.Model):
__bind_key__ = 'db2'
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(80), unique=True)
I want to use alembic autogenerate function to auto detecting and generating migrations for db 1 and db2 separately, but db.metadata will get all tables metadata, but how to just get bind db metadata?
Thanks #davidism help! I can use include_symbol to make it.
def include_symbol(tablename, schema):
return tablename in ('table1', 'table2'.......'table10') # for db1
# return tablename not in ('table1', 'table2'.......'table10') # for db2
with connectable.connect() as connection:
context.configure(
connection=connection,
target_metadata=target_metadata,
include_symbol=include_symbol
)
You can't because that feature doesn't exist yet. Currently, there is one metadata instance for all models across all binds. As long as all the models have unique names, this isn't a huge problem.
When we apply this patch and make a new release, each bind will have its own metadata. You will then be able to access it with db.get_metadata(bind='db2').
Related
I'm trying to connect my FastAPI project to 2 databases, one is gonna be local and the other one is located in an IP address. I think I managed to connect to both. However, when I try to create the local db, I get the file of the file, but is not pulling the models that I created for that db.
my code for database.py is the following:
SQLALCHEMY_DATABASE_URL = "credentials and address"
AUTH_DATABASE_URL = "sqlite:///./users.db"
engine = create_engine(SQLALCHEMY_DATABASE_URL)
engine2 = create_engine(AUTH_DATABASE_URL, connect_args={
"check_same_thread": False})
Base = declarative_base()
BaseB = declarative_base()
SessionLocal = sessionmaker(autocommit=False, autoflush=False)
SessionLocal.configure(binds={Base: engine, BaseB: engine2})
The models I created for that base are the following:
class User(BaseB):
__tablename__ = "users"
Id = Column(Integer, primary_key=True, index=True)
email = Column(String, unique=True, index=True)
hashed_password = Column(String)
##Constraints##
leads = relationship("Lead", back_populates="owner")
def verify_password(self, password: str):
return hash.bcrypt.verify(password, self.hashed_password)
class Lead(BaseB):
__tablename__ = "leads"
Id = Column(Integer, primary_key=True, index=True)
owner_id = Column(Integer, ForeignKey("users.Id"))
first_name = Column(String, index=True)
last_name = Column(String, index=True)
email = Column(String, index=True)
company = Column(String, index=True, default="")
notes = Column(String, default="")
date_created = Column(String, default=datetime.utcnow)
date_last_update = Column(String, default=datetime.utcnow)
##Constraints##
owner = relationship("User", back_populates="leads")
Also, I created a services.py file, which I use to run the code to create the database. It's creating the users.db file, but when I try to open it, I don't get the tables I created:
from database import BaseB, engine2, SessionLocal
def create_database():
return BaseB.metadata.create_all(bind=engine2)
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()
This is causing me problems, because I need to post data to the table users. However, when I try to do it, I get an error saying there is no such table created. I've tried even to run the code to generate the db automatically (without running services.py on python) but still I get the same error.
I would appreciate if someone can take a look at my code and check what I may be missing.
Thanks!!
Thank you!
I think I have figured out what was wrong. I post this solution in case anyone else might get trapped as I was. My error was that I wasn't importing the models, neither declaring it in the services.py route. I changed my code and now I can see the two tables when I create the database from python
from database import engine2, SessionLocal
import models
def create_database():
return models.BaseB.metadata.create_all(bind=engine2)
Hope it helps anyone having the same problem :)
I am using a Flask-Sqlalchemy postgres database model with the following style:
class User(db.Model) // Holds the users
id = db.Column(db.Integer, primary_key=True)
...
class Track(db.Model) // Holds racing tracks
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(255), nullable=False)
class Record(db.Model) // Hold users records on tracks
id = db.Column(db.Integer, primary_key=True)
user_id = db.Column(db.Integer, db.ForeignKey("user.id"), primary_key=True)
track_id = db.Column(db.Integer, db.ForeignKey("map.id"), primary_key=True)
time = db.Column(db.BigInteger, nullable=False)
My goal now would be to create a view which holds all Tracks completed by Users. In plain PostgresSQL it would look like this:
CREATE VIEW OR REPLACE user_tracks_finished AS
SELECT DISTINCT user_id, track_id
FROM record;
The User model would look something like this:
class User(db.Model) // Holds the users
id = db.Column(db.Integer, primary_key=True)
finished_tracks = db.relationship(...)
I can not any way to create views with the flask-sqlalchemy module. There is a sqlalchemy-views module, which I can not get to run in my flask app. Anybody got a good idea on how to model something like this? Any help ist appreciated!
I managed to get it done with SQLAlchemy-Utils: https://pypi.org/project/SQLAlchemy-Utils/
from sqlalchemy_utils import create_view
class UserFinishedMaps(db.Model):
__table__ = create_view(
name="user_finished_maps",
selectable=select([Record.user_id, Record.map_id]).group_by(
Record.user_id, Record.map_id
),
metadata=db.metadata,
)
I am working on a Flask app, using Flask-SQLAlchemy extension for database interactions. Since I have multiple apps writing on the same DB, I was getting concurrency issues with SQLite and I wanted to switch to PostgreSQL instead. I am able to create the tables on new database without a problem and pgAdmin displays the tables and columns.
# working
def createTables():
with app.app_context():
from models import User, Invoice
db.create_all()
But when it comes to adding a user, I am now getting an error: sqlalchemy.exc.NoForeignKeysError Although, I think, I declared one-to-many relationship in my models, based on the documentation, I get an error states that "there are no foreign keys linking these tables."
# not working
def create_test_user():
with app.app_context():
user = User(
username="Bob",
email="bob#email.com",
password="test"
)
db.session.add(user)
db.session.commit()
The full error message:
""" NoForeignKeysError: Could not determine join condition between parent/child tables on relationship User.invoices
- there are no foreign keys linking these tables.
Ensure that referencing columns are associated with a ForeignKey or ForeignKeyConstraint, or specify a 'primaryjoin' expression. """
I can't figure out what causes the error. What is missing with my models?
# models.py
class User(db.Model):
__tablename__ = "user"
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)
password = db.Column(db.String(60), nullable=False)
invoices = db.relationship('Invoice', backref='user', lazy=True)
class Invoice(db.Model):
__tablename__ = "invoice"
id = db.Column(db.Integer, primary_key=True)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
amount = db.Column(db.Integer, nullable=False)
Solved
Your code works for me. Maybe you need to re-create your tables or something similar. To be sure that we have the identical code: I have tested the following code:
class User(db.Model):
__tablename__ = "user"
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)
password = db.Column(db.String(60), nullable=False)
invoices = db.relationship('Invoice', backref='user', lazy=True)
class Invoice(db.Model):
__tablename__ = "invoice"
id = db.Column(db.Integer, primary_key=True)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
amount = db.Column(db.Integer, nullable=False)
In the route:
user = User(
username="Bob",
email="bob#email.com",
password="test"
)
db.session.add(user)
db.session.commit()
print(user)
I finally solved the problem and it was not where I was looking for. I was getting NoForeignKeysError due to importing a wrong model file during initializing the app. One of my imported modules was calling a wrong/old version of the model. It was causing the table relationship in the actual model to break I guess.
When I went through step by step create_test_user() I noticed that the error occurs actually during the class creation, before even it hits to db.session.add and I replicated the error even without a DB. I went through all my modules that are calling the models and caught wrong model import.
I am trying to configure a MySQL schema using Flask-SQLAlchemy. I have a schema called testdb and some tables. I will list one table, User.
This code, so far, creates all of the tables needed but only when testdb already exists. Is there a way to check and create testdb before I connect?
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql://root:password#localhost/testdb'
db = SQLAlchemy(app)
class User(db.Model):
userid = db.Column(db.Integer, primary_key=True, autoincrement=True)
username = db.Column(db.String(16), unique=True, nullable=False)
password = db.Column(db.String(16), unique=False, nullable=False)
email = db.Column(db.String(80), unique=True, nullable=False)
createdDate = db.Column(db.DateTime, default=datetime.utcnow, nullable=False)
lastUpdated = db.Column(db.DateTime, default=datetime.utcnow, nullable=False)
db.create_all()
Desired command:
CREATE SCHEMA IF NOT EXISTS `testdb` ;
I solved this thanks to #hygorxaraujo
See the code below:
import sqlachemy
engine = sqlalchemy.create_engine('mysql://root:password#localhost') # connect to server
engine.execute("CREATE SCHEMA IF NOT EXISTS `testdb`;") #create db
engine.execute("USE testdb;") # select new db
app.config['SQLALCHEMY_DATABASE_URI'] = 'mysql://root:mypassword#localhost/testdb'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False #Turn off annoying message
db = SQLAlchemy(app)
i have created a database for the Flask application,
class Post(db.Model, SearchableMixin):
__searchable__ = ['body']
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'))
language = db.Column(db.String(5))
# for the comment foreign key
comment = db.relationship('Comment', foreign_keys = 'Comment.post_id',
backref = 'post', lazy = 'dynamic')
photo = db.relationship('Photo', foreign_keys = 'Photo.post_idn',
backref = 'post', lazy = 'dynamic')
def __repr__(self):
return "<Post {}>".format(self.body)
class Photo(db.Model):
id = db.Column(db.Integer, primary_key = True)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
photo_url = db.Column(db.String(200))
upload_time = db.Column(db.DateTime, index=True, default=datetime.utcnow)
new_field = db.Column(db.String(100))
post_id = db.Column(db.Integer, db.ForeignKey('post.id'))
def __repr__(self):
return '<Photo {}>'.format(self.photo_url)
when i run flask db migrate and upgrade, it states that
[2018-11-25 21:07:03,439] INFO in init: Microblog startup INFO
[alembic.runtime.migration] Context impl SQLiteImpl. INFO
[alembic.runtime.migration] Will assume non-transactional DDL.
However, when I try to run Photo.query.all() in flask shell, it gives me the below errors ' No such column: post_id'
I believe this is a databse migration issue. Any one has any idea what i should do?
And i am willing to delete the whole database and re-migrate everything again. Can someone advice me how to do so. Thanks.
You have created post_id as foreign key. So you must create a Post model for post db table with a column named id.