Flask-Migrate Alter Table Error - python

i've been scratching my head for a bit of time now with this error and I can't seem to figure out what's wrong. Maybe you can help?
Using Flask-Migrate i've modified my models and am attempting to migrate the database accordingly.
The error i'm running into seems to be between two specific models User and Transaction.
After running
$ python run.py db init
$ python run.py db migrate
$ python run.py db upgrade
I recieve this error:
sqlalchemy.exc.OperationalError: (_mysql_exceptions.OperationalError) (1825, "Failed to add the foreign key constraint on table 'transaction_table'. Incorrect options in FOREIGN KEY constraint 'my_database/transaction_table_ibfk_1'") [SQL: 'ALTER TABLE transaction_table ADD FOREIGN KEY(user_id) REFERENCES users (id)']
Here is how i've set up my models note the tablename override:
class User(db.Model):
__tablename__ = 'users'
id = db.Column('id', db.Integer, primary_key=True)
email = db.Column(db.String(120), unique=True, index=True)
password = db.Column('password', db.String(10))
role = db.Column('role', db.String(3))
registered_on = db.Column('registered_on', db.DateTime)
transactions = db.relationship('transaction_table', backref=db.backref('user', lazy='joined'), lazy='dynamic')
class Transaction(db.Model):
__tablename__ = 'transaction_table'
id = db.Column(db.Integer, primary_key=True)
user_id = db.Column(db.Integer, db.ForeignKey('users.id'))
transaction_date = db.Column('transaction_date', db.DateTime)
requested_donor_id = db.Column(db.Integer, db.ForeignKey('donor_db.id'))
I've used this guide for setting up my models with Foreign Keys and relationships.
I've used this Blog Post to help diagnose my problems to no avail. Might you know what i'm missing?
Thanks in advance.

Related

flask sqlalchemy does not insert a foreign key using mysql

I'm using Flask sqlalchemy to create some tables for a database using mySQL. I have a problem when setting some foreign keys,
the tables are:
class Specific(db.Model):
code = db.Column(db.String(30), primary_key=True, index=True)
serial_number = db.Column(db.String(30), primary_key=True, index=True)
equipment_id = db.Column(db.String(30), db.ForeignKey('general.id'), index=True)
#setting relationship fields
tickets_equip_code = db.relationship('Ticket', backref='specific_code', lazy='dynamic')
tickets_serial_number = db.relationship('Ticket', backref='specific_serial, lazy='dynamic')
class Ticket(db.Model):
id = db.Column(db.String(30), primary_key=True)
solicitant = db.Column(db.String(30), db.ForeignKey('user.id'), index=True, unique=False)
#setting the foreign keys
equipment_code = db.Column(db.String(30), db.ForeignKey('specific.code'), nullable=False)
equipment_serial_number = db.Column(db.String(30), db.ForeignKey('specific.serial_number'), nullable=False)
the problem comes when I try to create all the tables using db.create_all() in the console, I get the following error:
pymysql.err.OperationalError 1822 "Failed to add the foreign key constraint. Missing index for constraint 'fk_ticket_serial_number' in the referenced table 'specific'"
I have other db Models with FKs but they seem to work well these two models are the ones causing the trouble but I can't get it. I've even tried to run the script using only one of the two FKs in the mentioned models and it runs fine; I have also tried setting both the primary keys in the Specific model using __table_args__, also I tried setting manually the FK constraints in Tickets using __table_args__ (that's why the name 'fk_ticket_serial_number' appears) but it had no effect.
Do you guys have any ideas what might be going wrong?

How to recreate database in SQLAlchemy from Flask?

I'm using SQLAlchemy ORM framework from a Flask project. I wanted to add another column to one of my models. After I did, I used db.session.drop_all() to drop all current entries in the database, and then I tried recreating a new instance with the new field.
Whenever I tried I get 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.image_file AS user_image_file
FROM user]
I think I might need to reconstruct the database in some way, but I'm not sure how. I looked into the documentation, but could not find anything useful.
__init__.py
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///site.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
models.py
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(40), unique=True, nullable=False) #newly added field
password = db.Column(db.String(60), nullable=False)
image_file = db.Column(db.String(20), nullable=False, default='default.jpg')
messages = db.relationship('Message', backref='user', lazy=True)
When you used db.drop_all() you dropped all tables so now you can't insert data because there is no table. You need create the table again with db.create_all() as #SuperShoot mentioned.
You should use something for migrations like alembic or flask-sqlalchemy.
This way you could add new column to your ORM. Run flask db migrate -m 'new column', this will detect changes. Then run flask db upgrade head to apply those changes to your database.

Is there an ORM structure which allows me to have tables which result in Catalog.Schema.Table?

I am currently working on a project with a pre-existing database. The Server is a clustered server with multiple Catalogs (database), and in each Catalog there are multiple Schemas with Tables. The table name format for the traditional SQL query would be [Catalog].[Schema].[Table]. This structure works for the traditional SQL.
The problem comes in when I try to flask db migrate to an sqlite database for testing. I get a number of errors depending on what I try.
I am using
Python 3.7
Flask 1.0.2
Flask-SQLAlchemy 2.4.0
Flask-Migrate 2.4.0
Windows 10 (not ideal, but its what I have)
I have tried the following with different results:
Schema only method:
class User(db.Model):
__tablename__ = 'user'
__table_args__ = (
db.PrimaryKeyConstraint('userid')
, db.ForeignKeyConstraint(('manageruserid',), ['CatalogA.SchemaA.userid'])
, {'schema': 'CatalogA.SchemaA'}
)
manager_user_id = db.Column('manageruserid', db.Integer())
user_id = db.Column('userid', db.Integer(), nullable=False)
class Tool(db.Model):
__tablename__ = 'tool'
__table_args__ = (
db.PrimaryKeyConstraint('toolid')
, db.ForeignKeyConstraint(('ownerid',), ['CatalogA.SchemaA.user.userid'])
, {'schema': 'CatalogB.SchemaB'}
)
tool_id = db.Column('toolid', db.Integer())
owner_id = db.Column('ownerid', db.Integer(), nullable=False)
when trying to upgrade it creates an error:
"sqlalchemy.exc.OperationalError: (sqlite3.OperationalError) unknown database "CatalogA.SchemaA" [SQL: CREATE TABLE "CatalogA.SchemaA".user (
manageruserid INTEGER,
userid INTEGER NOT NULL,
PRIMARY KEY (userid),
FOREIGN KEY(manageruserid) REFERENCES user (userid) )"
Bind With Schema (binds are setup correctly)
class User(db.Model):
__bind_key__ = 'CatalogA'
__tablename__ = 'user'
__table_args__ = (
db.PrimaryKeyConstraint('userid')
, db.ForeignKeyConstraint(('manageruserid',), ['CatalogA.SchemaA.user.userid'])
, {'schema': 'SchemaA'}
)
manager_user_id = db.Column('manageruserid', db.Integer())
user_id = db.Column('userid', db.Integer(), nullable=False)
class Tool(db.Model):
__bind_key__ = 'CatalogB'
__tablename__ = 'tool'
__table_args__ = (
db.PrimaryKeyConstraint('toolid')
, db.ForeignKeyConstraint(('ownerid',), ['CatalogA.SchemaA.user.userid'])
, {'schema': 'SchemaB'}
)
tool_id = db.Column('toolid', db.Integer())
owner_id = db.Column('ownerid', db.Integer(), nullable=False)
when trying to migrate it creates an error:
"sqlalchemy.exc.NoReferencedTableError: Foreign key associated with column 'user.manageruserid' could not find table 'CatalogA.SchemaA.user' with which to generate a foreign key to target column 'userid'"
If I do it the Schema only method way then I can run queries on the database, but it doesn't correctly setup my test-db.
I looked for multiple hours trying to find a solution, and would love someone to help me find the way forward (if you find a link to another solution, please tell me what you searched as well to increase my google-fu).
Main questions are:
What is the right way to have a model for this situation?
Was/Is there something in the documentation which I missed for this scenario?

Flask-Migration with one-to-many relationship

I have a working blog system. I want to add it to comment system. I completed migrations with post model with id, title and body.
Now I add comments and create new model named Comment. When I run migrations:
INFO [alembic.runtime.migration] Context impl MySQLImpl.
INFO [alembic.runtime.migration] Will assume non-transactional DDL.
INFO [alembic.env] No changes in schema detected.
from run import db
class Post(db.Model):
__tablename__ = 'blog.post'
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String, nullable=False)
body = db.Column(db.Text, nullable=False)
comments = db.relationship('Comment', backref='blog.post')
from run import db
class Comment(db.Model):
__tablename__ = 'blog.comment'
id = db.Column(db.Integer, primary_key=True)
body = db.Column(db.Text, nullable=False)
post_id = db.Column(db.Integer, db.ForeignKey('blog.post.id'), nullable=False)
I dont know what is wrong with my code. I get relationship from documentation and edit it. There aren't any comment table in db before.
EDIT 1:
I call comment inside run like below:
from model.comment import Comment
After that I can create migration but migration got error like below:
sqlalchemy.exc.InternalError: (pymysql.err.InternalError) (1005, 'Can\'t create table blog_db.blog.comment (errno: 150 "Foreign key constraint is incorrectly formed")') [SQL: '\nCREATE TABLE blog.comment (\n\tid INTEGER NOT NULL AUTO_INCREMENT, \n\tname VARCHAR(255) NOT NULL, \n\tbody TEXT NOT NULL, \n\tcreated DATETIME DEFAULT now(), \n\tstatus INTEGER NOT NULL, \n\tpost_id INTEGER NOT NULL, \n\tPRIMARY KEY (id), \n\tFOREIGN KEY(post_id) REFERENCES blog.post (id)\n)\n\n'] (Background on this error at: http://sqlalche.me/e/2j85)
The error Foreign key constraint is incorrectly formed happens because the primary key and the foreign key have different types.
In the Post model you defined the id column as:
id = db.Column(db.Integer, primary_key=True)
But the post_id foreign key that you added in the Comment model is defined differently:
post_id = db.Column(db.Integer, db.ForeignKey('blog.post.id'), nullable=False)
I think if you remove the nullable clause from the foreign key you'll get the migration accepted.

Error adding columns to SQLITE

I am working on Flask and sqlite as database. The requirements keep increasing for the project, so I have to add columns but I am not able to do it. I have searched on google but no answer found.
The part of code from model.py
class Role(db.Model):
__tablename__ = 'roles'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(64), unique=True)
default = db.Column(db.Boolean, default=False, index=True)
permissions = db.Column(db.Integer)
users = db.relationship('User', backref='role', lazy='dynamic')
#staticmethod
def insert_roles():
roles = {
'User': (Permission.FOLLOW |
Permission.COMMENT |
Permission.WRITE_ARTICLES, True),
'Moderator': (Permission.FOLLOW |
Permission.COMMENT |
Permission.WRITE_ARTICLES |
Permission.MODERATE_COMMENTS, False),
'Administrator': (0xff, False)
}
for r in roles:
role = Role.query.filter_by(name=r).first()
if role is None:
role = Role(name=r)
role.permissions = roles[r][0]
role.default = roles[r][1]
db.session.add(role)
db.session.commit()
def __repr__(self):
return '<Role %r>' % self.name
class User(UserMixin, db.Model):
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)
email = db.Column(db.String(64), unique=True, index=True)
username = db.Column(db.String(64), unique=True, index=True)
role_id = db.Column(db.Integer, db.ForeignKey('roles.id'))
password_hash = db.Column(db.String(128))
confirmed = db.Column(db.Boolean, default=False)
name = db.Column(db.String(64))
age = db.Column(db.Integer)
location = db.Column(db.String(64))
about_me = db.Column(db.Text())
member_since = db.Column(db.DateTime(), default=datetime.utcnow)
last_seen = db.Column(db.DateTime(), default=datetime.utcnow)
avatar_hash = db.Column(db.String(32))
posts = db.relationship('Post', backref='author', lazy='dynamic')
Whenever I try to add an column by running command
python manage.py db upgrade to it; it throws the following error:
sqlalchemy.exc.OperationalError: (sqlite3.OperationalError) table
roles already exists [SQL: u'\nCREATE TABLE roles (\n\tid INTEGER NOT
NULL, \n\tname VARCHAR(64), \n\tPRIMARY KEY (id), \n\tUNIQUE
(name)\n)\n\n']
How can I add column to my database?
After db migrate
run python manage.py db upgrade.
The error message is "table roles already exists". Therefore, you need to drop your db first and try to run python manage.py db upgrade again.
You must have created the database by running db.create_all();
I meet the same problem and you may try my solution as follow:
delete the database file (.sqlite);
delete the migrations folder;
run ... db init to create a new migrations;
run ... db migrate and ... db upgrade;
There may be some repeated operations between db.create_all() and the init migration. So just skip creating the tables manually.
Run the following three commands:
flask db stamp head
flask db migrate
flask db upgrade

Categories

Resources