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 :)
Related
I am new to this and I have a little test database which looks like this:
class Company(Base):
__tablename__ = 'company'
building_id = Column(Integer, primary_key=True, nullable=False, index=True)
name = Column(String, nullable=False)
buildings = relationship("Building", back_populates="company")
class Building(Base):
__tablename__ = 'building'
building_id = Column(Integer, primary_key=True, nullable=False, index=True)
name = Column(String, nullable=False)
ip_address = Column(String, nullable=True)
company_id = Column(Integer, ForeignKey('company.company_id'),nullable=False)
company = relationship("Company", back_populates="buildings")
As you may have noticed I have messed up the name for the company id, naming it "building_id".
I have changed this in the model, but it won't update the table with the error message
"(sqlite3.OperationalError) no such column: company.company_id".
How do I update it?
When you've actually launched your product you use a tool to handle database migrations for you, such as Alembic. Alembic can generate a migration script for you that runs the necessary ALTER statements to bring your database up to date with your model.
However while developing, it might be easier to just delete the .sqlite file and call create_all() again to get a fresh db created according to the schema.
I'm using FastAPI and I am stuck in this error while adding columns to a Model class
Here are my models
class Category(Base):
__tablename__ = 'categories'
id = Column(Integer, primary_key=True, index=True)
name = Column(String, unique=True)
description = Column(String)
products = relationship("Product", back_populates="category")
class Product(Base):
__tablename__ = 'products'
id = Column(Integer, primary_key=True, index=True)
category_id = Column(Integer, ForeignKey('categories.id'))
title = Column(String, unique=True)
price = Column(Float)
cost = Column(Float)
category = relationship("Category", back_populates="products")
when the API is running I got this error: sqlalchemy.exc.ProgrammingError: (psycopg2.errors.UndefinedColumn) column products.category_id does not exist
And the problem go on, if I try to add a new attribute to Porduct model there are no changes in my database columns
There is something like run migrations in FastAPI? What I am missing?
you will need to use something like Alembic to manage DB migrations. FastAPI does not manage this for you. Here's an example project from FastAPI that shows how to set up Alembic migrations.
https://fastapi.tiangolo.com/tutorial/sql-databases/#alembic-note
https://github.com/tiangolo/full-stack-fastapi-postgresql/tree/master/%7B%7Bcookiecutter.project_slug%7D%7D/backend/app/alembic/
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.
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').
I am working in a Python Eve based RESTful service with a SQLAlcemy backend. I have two models with a one to many relationship:
class User(CommonColumns):
"""Model of an user in the database"""
__tablename__ = "user"
id = Column(Integer, primary_key=True)
username = Column(String, unique=True)
email = Column(EmailType, unique=True)
folders = relationship('Folder', backref='user')
def __unicode__(self):
return self.username
class Folder(CommonColumns):
"""Model of an user in the database"""
__tablename__ = "folder"
id = Column(Integer, primary_key=True)
name = Column(String, unique=True)
user_id = Column(Integer, ForeignKey('user.id'), nullable=False)
def __unicode__(self):
return "{}/{}".format(self.user.username, self.name)
CommonColumnsis defined like here
This works great when inserting, updating and deleting users. However, I can't get inserting right for folders:
newfolder = {
'name':'FOLDER',
'user_id': 1,
}
response = requests.post("http://localhost:8080/api/v1.0/folders",data=newfolder)
print response.json()
{u'_error': {u'code': 422,
u'message': u'Insertion failure: 1 document(s) contain(s) error(s)'},
u'_issues': {u'exception': u"'user'"},
u'_status': u'ERR'}
Which is a rather cryptic error message. I've been reading Python Eve's documentation and I can't really understand what I am doing wrong. The only difference I see between user and folder insertions is that one has foreign keys.
Any idea why this is happening?
Well, it seemed that the problem was that I didn't have the same name for the resource and the table name ("users" for resource, "user" for table name). This seems to raise problems with foreign keys in eve, but does not fail with entities without relationships.
Changing the table names to match the resource names solved the problem.
I hope this will be useful to anyone.