How do I update a database table column? FastAPI Python - python

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.

Related

How to disable SQLAlchemy´s lazy loading?

I´m developing a python app which uses Flask-SQLAlchemy and it´s ORM Mapper.
I´ve got two tables with a Many To One relation. The main problem is that I want to load the content of both Objects with one join query and not later, when I access the object of the child table.
I already tried to get these behavior by using the joinedload option like this:
Event.query.filter(Event.timestamp == day)
.join(Event.user)
.options(joinedload(Event.user))
.all()
Also tried to set the lazy="joined" attribute in the entity-class for the relationship, which caused no difference.
The SQL Query looks right and the join is correctly generated, but if I access the user attribute later, another join query is sent. With other strategies it´s also not working, like the contains_eager option.
So the expected behavior would be, to save all information on the first load and don´t execute a query later on.
All SQLAlchemy options are default, except the DATABASE_URI. Is there any global option to disable this behavior or to override the default?
The Entities are the following:
class Event(db.Model):
__tablename__ = "event"
__table_args__ = (
db.ForeignKeyConstraint(
["username", "userfirstname"], ["users.name", "users.firstname"]
),
)
timestamp = db.Column(db.Date, primary_key=True, index=True)
username= db.Column(db.String, primary_key=True)
userfirstname= db.Column(db.String, primary_key=True)
...
user = db.relationship("UserEntity")
class UserEntity(db.Model):
__tablename__ = "users"
name= db.Column(db.String, primary_key=True)
firstname= db.Column(db.String, primary_key=True)
...
try this
class Event(db.Model):
__tablename__ = "event"
__table_args__ = (
db.ForeignKeyConstraint(
["username", "userfirstname"], ["users.name", "users.firstname"]
),
)
timestamp = db.Column(db.Date, primary_key=True, index=True)
username= db.Column(db.String, primary_key=True)
userfirstname= db.Column(db.String, primary_key=True)
user = db.relationship("UserEntity", back_populates="events")
user_id = Column(Integer, ForeignKey('user.id'))
class UserEntity(db.Model):
__tablename__ = "users"
name= db.Column(db.String, primary_key=True)
firstname= db.Column(db.String, primary_key=True)
events = relationship(MyOtherClass, lazy='joined')
from sqlalchemy docs:
joined applies a JOIN to the given SELECT statement so that related rows are loaded in the same result set. Joined eager loading is detailed at Joined Eager Loading.
I´ve found the mistake. The basic idea of disabling lazyloading was right and worked fine. I just messed up the object refs on the validation step and destroyed the SQLAlchemy background logic.

Is it possible to update the properties of a table with Flask + SQLAlchemy?

I created a table model that has a 'unique=true' property (flask code)
class Books(db.Model):
id = db.Column(db.Integer, unique=True, primary_key=True)
name = db.Column(db.String(255), unique=True, nullable=False)
author = db.Column(db.String(255), unique=True, nullable=False)
This was a mistake and I want to change the unique property to false (or just remove it..).
class Books(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(255), nullable=False)
author = db.Column(db.String(255), nullable=False)
How do I re-create the table via flask-python code?
Offcourse, I can drop the table and rebuild it with db.create_all(), or do the same via SQL directly on the database, or change it with an SQL command.
Is it possible doing it with the python-sqlalchemy way? (I really like using the python command line) - Preferably just with a small edit without dropping the data in the table.
yes it's possible.
you need to learn migration:
Flask Migration Example

FastAPI column products.category_id does not exist

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/

How to prevent this circular reference in Flask/SQLAlchemy Models?

I have the following four model classes in my Flask/SQLAlchemy application (a fifth class MyClassA is not shown here but is reference):
class MyClassF(db.Model):
valid_fs = ['F1', 'F2']
f_id = db.Column(db.Integer, nullable=False, primary_key=True)
name = db.Column(db.Enum(*valid_fs, name='f_enum'), default='F1', nullable=False)
class MyClassD(db.Model):
d_id = db.Column(db.Integer, nullable=False, primary_key=True)
name = db.Column(db.String(128))
v_collection = db.relationship('MyClassV', lazy='dynamic', backref=db.backref('d'), cascade='all, delete-orphan')
class MyClassV(db.Model):
v_id = db.Column(db.Integer, nullable=False, primary_key=True)
d_id = db.Column(db.Integer, db.ForeignKey(MyClassD.d_id), nullable=False)
c_collection = db.relationship('MyClassC', lazy='dynamic', backref=db.backref('v'), cascade='all, delete-orphan')
a_collection = db.relationship('MyClassA', lazy='dynamic', backref=db.backref('v'), cascade='all, delete-orphan')
class MyClassC(db.Model):
c_id = db.Column(db.Integer, nullable=False, primary_key=True)
v_id = db.Column(db.Integer, db.ForeignKey(MyClassV.v_id), nullable=False)
f_id = db.Column(
db.Integer,
db.ForeignKey(MyClassF.f_id),
nullable=False,
#default=MyClassF.query.filter(MyClassF.name == "F1").one().f_id
)
Creating this schema using the Flask-Migrate command db init, db migrate and db upgrade works just fine.
However, when I un-comment the line the definition of MyClassC.f_id and try it again (after removing the migrations directory), I get the following circular dependency error:
sqlalchemy.exc.InvalidRequestError: When initializing mapper
Mapper|MyClassV|my_classV, expression 'MyClassC' failed to locate a
name ("name 'MyClassC' is not defined"). If this is a class name,
consider adding this relationship() to the
class after both dependent classes have been defined.
All I'm trying to do is ensure that the default value of MyClassC.f_id is set by querying the MyClassF table. This check should occur at insertion time -- not when the database is being created. So I don't understand why I am getting this error now.
How can I use db.relationship() (or any other technique) to get around this circular dependency error while enforcing the database integrity rule I'm trying to implement?
I believe the default parameter is translated to the SQL DEFAULT constraint. AFAICT this constraint is not evaluated at insertion time, but when creating the table and the value is then used as a default (which means the default value is set once and forall at creation of the table and used for all rows that lack that column, it cannot dynamically change according to the contents of an other table).
However the documentation of SQLAlchemy mentions the fact that you can pass in a python function as default value and it will be called for each insert to obtain the default value to use, so you can do:
class MyClassC(db.Model):
c_id = db.Column(db.Integer, nullable=False, primary_key=True)
v_id = db.Column(db.Integer, db.ForeignKey(MyClassV.v_id), nullable=False)
f_id = db.Column(
db.Integer,
db.ForeignKey(MyClassF.f_id),
nullable=False,
default=lambda: MyClassF.query.filter(MyClassF.name == "F1").one().f_id
)
Note however that this will not use any database facility to provide the default value, it's SQLAlchemy first obtaining the default value and then manually inserting it in your queries.

How do I write a query to get sqlalchemy objects from relationship?

I am learning python and using the framework pyramid with sqlalchemy as the orm. I can not figure out how relationships work. I have 2 tables, offices and users. the foreign key is on the users table 'offices_id'. I am trying to do a query that will return to me what office a user is a part of.
This is how I have my models set up.
DBSession = scoped_session(sessionmaker(extension=ZopeTransactionExtension()))
Base = declarative_base()
class User(Base):
__tablename__ = 'users'
id = Column(Integer, primary_key=True)
name = Column(Unicode(255), unique=True, nullable=False)
trec_number = Column(Unicode(255), unique=True, nullable=False)
office_id = Column(Integer, ForeignKey('offices.id'))
class Office(Base):
__tablename__ = 'offices'
id = Column(Integer, primary_key=True)
name = Column(Unicode(255), unique=True, nullable=False)
address = Column(Unicode(255), unique=True, nullable=False)
members = relationship("User", backref='offices')
In my view how would I write a query that would return the the office information for a given user?
I am trying this:
for user in DBSession.query(User).join(Office).all():
print user.address
but I think I am misunderstanding how the queries work because I keep getting errors
AttributeError: 'User' object has no attribute 'address'
when I do this:
for user in DBSession.query(User).join(Office).all():
print user.name
it prints out the users name fine since name is an attribute of the User class.
I also can not get the inverse to work
for offices in DBSession.query(Office).join(User).all():
print offices.users.name
You need to use the name you used in the backref argument to access the Office model. Try user.offices.address

Categories