One-To-Many database relationship Foreign Key error - SQLAlchemy - python

I am having trouble making a One-To-Many relationship between 'User' and 'GymObj' using a Foreign Key. In my code, a user can have only one gym objective however a gym objective can have many users. The code which I attempted seems to be correct as I followed a tutorial for it however a 'NoForeignKeysError' appears.
python code
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(200), nullable=False, unique=True)
birth_year = db.Column(db.Integer, nullable=False)
weight = db.Column(db.Integer, nullable = False)
date_added = db.Column(db.DateTime, default=datetime.utcnow)
user_id = db.Column(db.Integer, db.ForeignKey('gymobj.id'))
#Create a String
def __repr__(self):
return '<Username %r>' % self.username
class GymObj(db.Model):
id = db.Column(db.Integer, primary_key=True)
gym_obj = db.Column(db.String)
users = db.relationship('User', backref='user')
console
sqlalchemy.exc.NoForeignKeysError: Could not determine join condition between parent/child tables on relationship GymObj.users - 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 attempted to make a migration and push the migration in the Shell as well however the same error appeared. Any help would be much appreciated.

Related

How do I associate two foreign keys to separate instances of the same parent model in SQLAlchemy?

I am working on a user to user 'Challenge' model in SQLAlchemy that needs to be linked to both the sending player, as well as the receiving player. I.e one user sends a challenge to another user, and users can see all of their sent challenges along with their received challenges. I initially attempted to solve this using an association table with no luck. I have since realized that an association table is unnecessary, however, I am unable to join the two tables as desired without receiving this error: Could not determine join condition between parent/child tables on relationship User.sent_challenges - there are multiple foreign key paths linking the tables.
I have read through the documentation and all similar problems that I could find through these forums but none seem to fix my problem. Below is the current implementation of my code. It is far from the only attempt I have made, however, I believe that it most accurately portrays what I am attempting to accomplish.
Challenge model:
class Challenge(db.Model):
__tablename__ = 'challenges'
id = Column(Integer, primary_key=True)
time = Column(Integer)
player_1_score = Column(Integer, nullable=True)
player_2_score = Column(Integer, nullable=True)
sender_id = Column(Integer, ForeignKey('users.id'), nullable=False)
receiver_id = Column(Integer, ForeignKey('users.id'), nullable=False)
sender = relationship("User", back_populates="sent_challenges")
receiver = relationship("User", back_populates="received_challenges")
User model:
class User(db.Model):
__tablename__ = 'users'
id = Column(Integer, primary_key=True)
user_name = Column(String(14), nullable=False)
level = Column(Integer)
high_score = Column(Integer)
points = Column(Integer)
sent_challenges = relationship("Challenge", back_populates="sender", cascade="all, delete")
received_challenges = relationship("Challenge", back_populates="receiver", cascade="all, delete")
Any insight would be greatly appreciated.
Add the foreign_keys param so sqlalchemy knows which foreign key belongs to which relationship:
sender = relationship("User", foreign_keys=[sender_id], back_populates="sent_challenges")
receiver = relationship("User", foreign_keys=[receiver_id], back_populates="received_challenges")
This is explained here with addresses that mirror your sender/receiver ambiguity:
handling-multiple-join-paths

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?

Flask-SQLAlchemy: One-To-Many between separate moduls (NoReferencedTableError)

I have two model classes in separate files, created a One-To-Many relationship between them.
user.py:
class User(db.Model):
__tablename__ = 'users'
id = db.Column(db.Integer, primary_key=True)
email = db.Column(db.String(100), index=True, unique=True, nullable=False)
password = db.Column(db.String(128), nullable=False)
projects = db.relationship('Project', backref='owner', lazy='dynamic')
project.py:
class Project(db.Model):
__tablename__ = 'projects'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(100), index=True, unique=True, nullable=False)
owner_id = db.Column(db.Integer, db.ForeignKey('User.id'))
This is how I create my app:
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = DB_URL
db = SQLAlchemy(app)
#app.before_first_request
def create_tables():
db.create_all()
app.run(debug=True)
However when I create a request I get the following error:
sqlalchemy.exc.NoReferencedTableError:
Foreign key associated with column 'projects.owner_id' could not find
table 'User' with which to generate a foreign key to target column 'id'
I understand this is a duplicate of this question asked before, however I tried that and did not work:
#app.before_first_request
def create_tables():
from projectx.models.auth.user import User
from projectx.models.private_data.project import Project
db.create_all()
I also tried giving the same __table_args__ = {"schema": "testdb"} args to both models (I manually created the db with this name) and refer to db.ForeignKey('testdb.User.id) to no avail, the error is the same.
What am I missing here?
It seems I misunderstood some concepts about how this relationship abstraction works.
The foreign key reference is to the tablename and not the class name naturally.
I should have written db.ForeignKey('users.id').

sqlalchemy.exc.InvalidRequestError: One or more mappers failed to initialize - can't proceed with initialization of other mappers

This error happened when I tried to get access to the page. I didn't get errors when I created the tables, but seems like there are problems still.
The models are like this:
class User(UserMixin, db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(64), index=True, unique=True)
sell_items = db.relationship('Item', backref='user')
class Item(db.Model):
id = db.Column(db.Integer, primary_key=True)
item_name = db.Column(db.String(64), index=True)
item_image = db.Column(db.String(200), index=True)
price = db.Column(db.Float(10), index=True)
user_id = db.Column(db.Integer, db.ForeignKey('user.id'))
user = db.relationship('User', backref='sell_items')
The whole error message is this
Triggering mapper: 'Mapper|User|user'. Original exception was: Error creating backref 'user' on relationship 'User.sell_items': property of that name exists on mapper 'Mapper|Item|item'
How can I fix this? What I want to do is to refer to username who sells the item, but I cannot. There is a problem with the relationships between the models.
When you use backref the backwards relationship is automatically created, so it should only be used in one side of the relationship. In your case, you can remove the sell_items in the User model and the User model will automatically get a relationship from Item.
To declare the relationshiop on both sides (in case you want to customize its name, for example, use back_populates='name_of_relationship_on_other_model'.
in your Item class, replace this line
user = db.relationship('User', backref='sell_items')
with this line
user_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
it should work that way, from there you can query like this item = Item.query.first(), then item.sell_items... to get the user who posted the item.
i hope it helps.

SQLAlchemy not cascade deleting multiple levels down

I'm a little new to SQLAlchemy. I've searched around for an answer to my question but I have found nothing that works for my situation.
In short, deleting a record in the Release model will delete all the records in the other models as long as there is no related records in TestResults. However, if there are related records in TestResult, then deleting a Release will not work. It almost seems as if deleting a parent will delete a child and the child's child but not the child's child's child. Here is some code to help highlight this:
class Release(db.Model):
__tablename__ = 'releases'
id = db.Column(db.Integer, primary_key=True)
platform_id=db.Column(db.Integer, db.ForeignKey('platforms.id'))
name = db.Column(db.String(20), unique=True)
builds = db.relationship('ReleaseBuilds', cascade='all,delete', lazy='dynamic', order_by="desc(ReleaseBuilds.date_created)")
class ReleaseBuilds(db.Model):
__tablename__='release_builds'
id = db.Column(db.Integer, primary_key=True)
release_id = db.Column(db.Integer, db.ForeignKey('releases.id'))
name = db.Column(db.String(150), nullable=False)
artifacts = db.relationship('ReleaseBuildArtifacts', cascade='all,delete', backref='builds', lazy='dynamic')
deployments = db.relationship('Deployments', cascade='all,delete', lazy='dynamic')
tests = db.relationship('Test', cascade='delete', lazy='dynamic')
class ReleaseBuildArtifacts(db.Model):
__tablename__='release_build_artifacts'
id = db.Column(db.Integer, primary_key=True)
release_build_id = db.Column(db.Integer, db.ForeignKey('release_builds.id'))
application_id = db.Column(db.Integer, db.ForeignKey('applications.id'))
rpm = db.Column(db.String(300))
build = db.relationship('ReleaseBuilds')
application = db.relationship('Application')
class Deployments(db.Model):
__tablename__ = 'deployments'
release_build_id = db.Column(db.Integer, db.ForeignKey('release_builds.id'), primary_key=True)
environment_id = db.Column(db.Integer, db.ForeignKey('environments.id'), primary_key=True)
date_deployed = db.Column(db.DateTime(timezone=False), default=datetime.datetime.utcnow)
environment = db.relationship('Environment', foreign_keys=[environment_id])
class TestType(db.Model):
__tablename__ = 'test_types'
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(50), unique=True)
class Test(db.Model):
__tablename__ = 'tests'
id = db.Column(db.Integer, primary_key=True)
release_build_id = db.Column(db.Integer, db.ForeignKey('release_builds.id'), nullable=False)
environment_id = db.Column(db.Integer, db.ForeignKey('environments.id'), nullable=False)
test_type_id = db.Column(db.Integer, db.ForeignKey('test_types.id'))
name = db.Column(db.String(300))
environments = db.relationship('Environment', foreign_keys=[environment_id])
results = db.relationship('TestResult', cascade='all,delete', lazy='dynamic')
__table_args__ = (
ForeignKeyConstraint(['release_build_id', 'environment_id'],['deployments.release_build_id', 'deployments.environment_id']),
)
class TestResult(db.Model):
__tablename__ = 'test_results'
id = db.Column(db.Integer, primary_key=True)
test_id = db.Column(db.Integer, db.ForeignKey('tests.id'), nullable=False)
name = db.Column(db.String(500))
passed = db.Column(db.Boolean)
Any suggestions as to why this cascade delete is not working?
I came across a similar issue in our project, where we define cascades on the ORM level and also use lazy=dynamic relationships. This caused the cascade not to run on the bottom-most children.
Dynamic loading causes the relationship to return a Query object when accessed.
Delete on queries is quite limited, in order to increase performance, as documented here:
https://docs.sqlalchemy.org/en/13/orm/query.html
The method does not offer in-Python cascading of relationships - it
is assumed that ON DELETE CASCADE/SET NULL/etc. is configured for any
foreign key references which require it, otherwise the database may
emit an integrity violation if foreign key references are being
enforced.
After the DELETE, dependent objects in the Session which were impacted
by an ON DELETE may not contain the current state, or may have been
deleted. This issue is resolved once the Session is expired, which
normally occurs upon Session.commit() or can be forced by using
Session.expire_all(). Accessing an expired object whose row has been
deleted
will invoke a SELECT to locate the row; when the row is not found,
an ObjectDeletedError is raised.
Therefore a solution for your problem could be either defining cascades on the database level, or using other types of relationships.
Related question was raised here: SQLAlchemy delete doesn't cascade
EDIT: (Solution I applied is changing the loading type on query level - in options)

Categories

Resources