I am using Flask-Migrate (3.1.0) to handle my db(Postgres) migration for a flask application.
There's few answers on the internet but they are targeting models built using SQLAlchemy, the problem is I am building my models using Flask-SQLAlchemy, though I know there is no huge difference between them and Flask-SQLAlchemy is built on top of SQLAlchemy.
def class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(64), index=True)
email = db.Column(db.String(60), index=True)
date_of_birth = db.Column(db.DateTime, nullable=True)
team_member = db.Column(db.String(30), nullable = False)
is_monitored = db.Column(db.Boolean, default=False, nullable=False)
lifecycle = db.Column(db.Integer, default= 1, nullable=False)
shows=db.relationship('UserHistory', backref='active_state', lazy=True)`
Using flask db migrate to generate the migration script, I found no trace of the default value.
def upgrade():
# ### commands auto generated by Alembic - please adjust! ###
op.add_column('user', sa.Column('lifecycle', sa.Integer(), nullable=False))
op.add_column('user_history', sa.Column('lifecycle', sa.Integer(), nullable=False))
# ### end Alembic commands ###
As I define a column constraint to be not null, flask db upgrade fails which makes sense.
Is there any 'Flask-SQLAlchemy' solution to this issue rather than pure 'SQLAlchemy'? I don't know if mixing Flask-SQLAlchemy and SQLAlchemy is good idea .
Thanks
Related
Thank you in advance! I am working on a client relationship management app and am trying to configure the service level agreement models for meetings and calls. Both of these will share a many-to-many relationship with the model Month which is just the months of the year. Below is my code and the errors I am getting below that.
months = db.Table(
"months",
db.Column("month_id", db.Integer, db.ForeignKey("month.id"), primary_key=True),
db.Column("slacall_id", db.Integer, db.ForeignKey("sla_call.id"), primary_key=True),
db.Column(
"slameeting_id", db.Integer, db.ForeignKey("sla_meeting.id"), primary_key=True
),
)
class SLACall(db.Model):
id = db.Column(db.Integer, primary_key=True)
per_year = db.Column(db.Integer, nullable=False)
months = db.relationship(
"Month",
secondary=months,
lazy="subquery",
backref=db.backref("slacalls", lazy=True),
)
relationship_id = db.relationship("Relationship", backref="sla_call", lazy=True)
class SLAMeeting(db.Model):
id = db.Column(db.Integer, primary_key=True)
per_year = db.Column(db.Integer, nullable=False)
months = db.relationship(
"Month",
secondary=months,
lazy="subquery",
backref=db.backref("slameetings", lazy=True),
)
relationship_id = db.relationship("Relationship", backref="sla_meeting", lazy=True)
class Month(db.Model):
id = db.Column(db.Integer, primary_key=True)
month_name = db.Column(db.String(9), unique=True, nullable=False)
And I am getting the following error when I boot up flask for both SLA models (only one of the errors shown):
/home/kendall/Python/pm_crm/.venv/lib/python3.9/site-packages/flask_sqlalchemy/init.py:550: SAWarning: relationship 'Month.slameetings' will copy column month.id to column months.month_id, which conflicts with relationship(s): 'Month.slacalls' (copies month.id to months.month_id), 'SLACall.months' (copies month.id to months.month_id). If this is not the intention, consider if these relationships should be linked with back_populates, or if viewonly=True should be applied to one or more if they are read-only. For the less common case that foreign key constraints are partially overlapping, the orm.foreign() annotation can be used to isolate the columns that should be written towards. To silence this warning, add the parameter 'overlaps="months,slacalls"' to the 'Month.slameetings' relationship. (Background on this error at: https://sqlalche.me/e/14/qzyx)
Is this not the correct way to set this up? Or are multiple many-to-many relationships to a single model not possible?
Thank you again,
Kendall
I have this model:
class Task(db.Model):
...
created = db.Column(db.DateTime, index=False, unique=False, nullable=False)
active = db.Column(db.Boolean, index=False, unique=False, default=False)
...
In my Flask endpoint I would like to use order_by first on the created date. And then afterwards based on the active state of the Task. So if active = True it should be sorted on that as well. Is this possible?
I tried this:
#app.route('/tasks', methods=['GET'])
def get_tasks():
all_tasks = Task.query.order_by(Task.created.desc(), Task.active).all()
return jsonify(tasks_schema.dump(all_tasks))
However that is not working. Any ideas?
Maybe not answer to your question, as you want to use order_by, but I'm using this code to order by boolean:
self.items.sort(key=lambda x: (-x.active, x.name))
So, before adding a few columns I had username, password and email and it was working fine. But when I tried to add a few more things such as age, gender, phone and address I got this error:
sqlalchemy.exc.OperationalError: (sqlite3.OperationalError) no such table: user [SQL: 'SELECT user.id AS user_id, user.username AS user_username, user.email AS user_email, user.password AS user_password, user.age AS user_age, user.gender AS user_gender, user.phone AS user_phone, user.address AS user_address \nFROM user \nWHERE user.username = ?\n LIMIT ? OFFSET ?'] [parameters: ('agam-kashyap', 1, 0)] (Background on this error at: http://sqlalche.me/e/e3q8)
The error seems to be in these lines:
#app.route("/register",methods = ['GET','POST'])
def register():
form = RegistrationForm()
if form.validate_on_submit():
user = User(username=form.username.data, email = form.email.data, password = form.password.data, age = form.age.data, gender = form.gender.data, phone = form.phone.data, address = form.address.data)
db.create_all()
db.session.add(user)
db.session.commit()
return redirect(url_for('login'))
return render_template('register.html', title= 'Register' , form = form)
Also, here is my User class:
class User(db.Model):
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)
age = db.Column(db.String(3), nullable=False)
gender = db.Column(db.String(6), nullable=False)
phone = db.Column(db.String(10), nullable=False)
address = db.Column(db.String(200), nullable=False)
How can I solve this issue?
In keeping with Dave W. Smith's suggestion, the error is probably due to the fact that you did not perform any database migration operations for the new changes to take effect.
When a database model changes, as it does for you, the database must of course be updated. This is what you want to accomplish visibly.
So, you should note that the particularity with SQLAlchemy is that it creates tables from models only when they already exists.
It means that if you want to add new things to the database, modify or delete fields from your model, you have to destroy the old tables and recreate everything from scratch.
So the solution to work around this problem is to go through a database migration framework. Like a source code version control tool, a database migration framework tracks any changes that occur on the database schema. So it can apply incremental changes to the database.
For SQLAlchemy, there is an excellent database migration tool, I named: Alembic. But since you use Flask, you do not have to manipulate Alembic directly; There is a Flask extension that handles SQLAlchemy database migrations for Flask applications using Alembic: flask-migrate
As you are still a beginner with Flask, I recommend this excellent Miguel Grinberg's tutorial: the flask mega tutorial - part-iv :database. It will teach you the basics needed to work with Flask SQLAlchemy
I am writing a flask application for the first time from scratch. I have written several applications using django.
I am following tutorial where few options are specified on model fields. e.g.
email = db.Column(db.String(128), nullable=False,
unique=True)
date_created = db.Column(db.DateTime, default=db.func.current_timestamp())
date_modified = db.Column(db.DateTime, default=db.func.current_timestamp(),
onupdate=db.func.current_timestamp())
I want to find a list of all available options and their default behavior, like django does
While looking for constraints, I found column options here
name
type_
*args
autoincrement
default
doc
key
index
info
nullable
onupdate
primary_key
server_default
server_onupdate
quote
unique
system
comment
I already have an application using my SQLAlchemy model, and I'm now trying to add a flask admin website on top to manage some of the data in the DB. This model is already setup and working throughout the existing application so I know the relationships are configured correctly.
In my DB the data_package table has a FK to the supplier table, and this has been added as a relationship. However, when I create a flask model view to allow editing of the data_package I'm receiving the error:
Exception: Cannot find reverse relation for model
I've added a subset of the code below, there are a lot more columns and logic but this is the bare minimum I have to try and test a fix to the issue.The issue is happening because I am adding inline_models = (Supplier,) which I need as I want to have a select to allow the supplier to be changed for a data package.
The line of code throwing the exception is https://github.com/flask-admin/flask-admin/blob/master/flask_admin/contrib/sqla/form.py#L558. The reason for this is the relationship direction is defined as ONETOMANY, and there is a check to ensure the direction is MANYTOONE or MANYTOMANY
Base = declarative_base()
class Supplier(Base):
__tablename__ = 'supplier'
id = Column(Integer, primary_key=True)
name = Column(String(100), nullable=False)
class DataPackage(Base):
__tablename__ = 'data_package'
id = Column(Integer, primary_key=True)
name = Column(String(100), nullable=False)
supplier_id = Column(Integer, ForeignKey('supplier.id'), index=True, nullable=False)
supplier = relationship("Supplier", backref='packages')
class DataPackageAdminView(ModelViewOnly):
form_columns = ['name', 'supplier']
inline_models = (Supplier,)
def create_app():
settings.configure_orm()
app = Flask(__name__)
app.config.from_pyfile('config.py')
#app.route('/')
def index():
return 'Click me to get to Admin!'
admin = Admin(app, name='Test Editor', template_mode='bootstrap3')
admin.add_view(DataPackageAdminView(DataPackage, settings.Session, category='Data Sources'))
return app
if __name__ == '__main__':
app = create_app()
app.run(debug=True)