I cannot make one-to-one relationship in Flask SQL Alchemy. Right after running the command flask-init in the terminal I get the following error:
sqlalchemy.exc.NoForeignKeysError: Could not determine join condition
between parent/child tables on relationship TimeZones.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 have three (3) tables.
Languages
TimeZones
User
Table User have primary keys of Languages table and TimeZones table.
Following is the code for all three table:
1. Languages
from modules.user_management.user_model import *
class Languages(db.Model):
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
language_culture = db.Column(db.String(50), nullable=False)
language_rtl = db.Column(db.Integer, nullable=False)
language_code = db.Column(db.String(50))
is_published = db.Column(db.Integer)
language_resources = db.relationship('LanguageResources', backref=db.backref('languages', lazy=True))
users = db.relationship('User', backref=db.backref('languages', lazy=True))
2. TimeZones
from modules.user_management.user_model import *
class TimeZones(db.Model):
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
time_zone = db.Column(db.String(50), unique=True, nullable=False)
users = db.relationship('User', backref=db.backref('timezones', lazy=True))
3. User
from modules.setups.language_model import *
from modules.setups.time_zone_model import *
class User(db.Model):
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
language_id = db.Column(db.Integer, db.ForeignKey('languages.id'), nullable=False)
timezone_id = db.Column(db.Integer, db.ForeignKey('timezones.id'), nullable=False)
user_logs = db.relationship('UserLogs', backref=db.backref('user', lazy=True))
Following the tutorial available on the below link, I cannot find any problem.
http://flask-sqlalchemy.pocoo.org/2.3/models/
Related
##CONFIGURE TABLES
class Userdb(db.Model, UserMixin):
__tablename__ = "users"
id = db.Column(db.Integer, primary_key=True)
email = db.Column(db.String(100), unique=True)
password = db.Column(db.String(100))
name = db.Column(db.String(100))
# This will act like a List of BlogPost objects attached to each User.
# The "author" refers to the author property in the BlogPost class.
post = relationship("BlogPost", back_populates="author", lazy='dynamic')
class BlogPost(db.Model):
__tablename__ = "blog_posts"
id = db.Column(db.Integer, primary_key=True)
author_id = db.Column(db.Integer, db.ForeignKey("users.id"),nullable=False)
# Create reference to the User object, the "posts" refers to the posts protperty in the User class.
author = relationship("Userdb", back_populates="post", lazy='dynamic')
# Create Foreign Key, "users.id" the users refers to the tablename of User.
title = db.Column(db.String(250), unique=True, nullable=False)
subtitle = db.Column(db.String(250), nullable=False)
date = db.Column(db.String(250), nullable=False)
body = db.Column(db.String(10000), nullable=False)
img_url = db.Column(db.String(250), nullable=False)
sqlalchemy.exc.NoForeignKeysError
sqlalchemy.exc.NoForeignKeysError: Could not determine join condition between parent/child tables on relationship Userdb.post - there are no foreign keys linking these tables. Ensure that referencing columns are associated with a ForeignKey or ForeignKeyConstraint, or specify a 'primaryjoin' expression.
class Team(db.Model):
__tablename__ = "Teams"
id = db.Column(db.Integer, primary_key=True)
flashscore_id = db.Column(db.String(255), nullable=False, unique=True)
name = db.Column(db.String(255), nullable=False)
leagues = db.relationship("League",
secondary=league_teams_table)
standings = db.relationship('Standing', backref='teams', cascade="all,delete")
fixtures = db.relationship('Fixture', backref='teams', cascade="all,delete")
related_fixtures_table = db.Table('RelatedFixtures',
db.Column('fixture_id', db.Integer, db.ForeignKey('Fixtures.id')),
db.Column('related_fixture_id', db.Integer, db.ForeignKey('Fixtures.id')))
class Fixture(db.Model):
__tablename__ = "Fixtures"
id = db.Column(db.Integer, primary_key=True)
home_id = db.Column(db.Integer, db.ForeignKey('Teams.id'),
nullable=False)
away_id = db.Column(db.Integer, db.ForeignKey('Teams.id'),
nullable=False)
home_ref = db.relationship("Teams", backref="fixture", uselist=False, foreign_keys=[home_id])
away_ref = db.relationship("Teams", backref="fixture", uselist=False, foreign_keys=[away_id])
flashscore_id = db.Column(db.String(255), nullable=False, unique=True)
total_goals = db.Column(db.Integer, nullable=False)
total_fh_goals = db.Column(db.Integer, nullable=False, default=0)
total_sh_goals = db.Column(db.Integer, nullable=False, default=0)
total_home_goals = db.Column(db.Integer, nullable=False)
total_away_goals = db.Column(db.Integer, nullable=False)
total_home_fh_goals = db.Column(db.Integer, nullable=False, default=0)
total_home_sh_goals = db.Column(db.Integer, nullable=False, default=0)
total_away_fh_goals = db.Column(db.Integer, nullable=False, default=0)
total_away_sh_goals = db.Column(db.Integer, nullable=False, default=0)
related_fixtures = db.relationship("Fixture",
secondary=related_fixtures_table)
I wrote the above code trying to define multiple relations between Fixture & Team. When I run the application and execute an action I get the following error:
sqlalchemy.exc.AmbiguousForeignKeysError: Could not determine join condition between parent/child tables on relationship Team.fixtures - there are multiple foreign key paths linking the tables. Specify the 'foreign_keys' argument, providing a list of those columns which should be counted as containing a foreign key reference to the parent table.
I tried solving this by adding the following two lines:
home_ref = db.relationship("Teams", backref="fixture", uselist=False, foreign_keys=[home_id])
away_ref = db.relationship("Teams", backref="fixture", uselist=False, foreign_keys=[away_id])
But I still get the same error. Can anyone help me out?
You need to configure the fixtures relationship on the Team class, because that's what the error is telling you to do: Could not determine join condition ... on relationship Team.fixtures.
However, you can't fix this situation with foreign_keys; that expects a single (possibly compound) foreign key, and you have two, distinct foreign key relationships. You'd have to create two separate relationships, one for fixtures where this team is the home team, and another one for the away relationship.
If you want Team.fixtures to return a list of all Fixture rows that reference the team via one or both foreign keys, you need to create a custom primaryjoin condition, one that matches your Team.id against the home_id or the away_id column (and you probably want to add a DISTINCT condition to your query to avoid getting duplicate results for fixtures that connect to your team via both foreign keys).
You need to get rid of fixtures = db.relationship('Fixture', backref='teams', cascade="all,delete") in Team.
What you will have at the end is:
class Team(db.Model):
__tablename__ = "Teams"
id = db.Column(db.Integer, primary_key=True)
# ...
class Fixture(db.Model):
__tablename__ = "Fixtures"
id = db.Column(db.Integer, primary_key=True)
home_id = db.Column(db.Integer, db.ForeignKey('Teams.id'), nullable=False)
away_id = db.Column(db.Integer, db.ForeignKey('Teams.id'), nullable=False)
home_ref = db.relationship("Teams", backref="fixture", uselist=False, foreign_keys=[home_id])
away_ref = db.relationship("Teams", backref="fixture", uselist=False, foreign_keys=[away_id])
# ...
You have your foreign keys defined in home_id and away_id, then you specify the 'foreign_keys' argument, providing a list of those columns which should be counted as containing a foreign key reference to the parent table. Refer to this section in the SQLAlchemy documentation.
I am new to SQLAlchemy, I am trying to build a practice project using SQLAlchemy. I have created the database containing tables with the following relationship. Now my questions are :
How to INSERT data into the tables as they are interdependent?
Does this form a loop in database design?
Is the looping database design, a bad practice? How to resolve this if its a bad practice?
Department.manager_ssn ==> Employee.SSN
and
Employee.department_id ==> Department.deptid
database relationship diagram
and following is the current version of code creating this exact database.
# Department table
class Departments(Base):
__tablename__ = "Departments"
# Attricutes
Dname= Column(String(15), nullable=False)
Dnumber= Column(Integer, primary_key=True)
Mgr_SSN= Column(Integer, ForeignKey('Employees.ssn'), nullable=False)
employees = relationship("Employees")
# Employee table
class Employees(Base):
__tablename__ = "Employees"
# Attributes
Fname = Column(String(30), nullable=False)
Minit = Column(String(15), nullable=False)
Lname = Column(String(15), nullable=False)
SSN = Column(Integer, primary_key=True)
Bdate = Column(Date, nullable=False)
Address = Column(String(15), nullable=False)
Sex = Column(String(1), default='F', nullable=False)
Salary = Column(Integer, nullable=False)
Dno = Column(Integer, ForeignKey('Departments.Dnumber'), nullable=False)
departments = relationship("Departments")
Please provide the solution in SQLAlchemy only and not in flask-sqlalchemy or flask-migrate and I am using Python 3.6.
You can avoid such circular reference design altogether by
Declaring the foreign key constraint on just one side of the relationship
Use a boolean flag to denote if the employee is a manager
class Department(Base):
__tablename__ = 'departments'
department_id = Column(Integer, primary_key=True)
employees = relationship('Employee', lazy='dynamic', back_populates='department')
class Employee(Base):
__tablename__ = 'employees'
employee_id = Column(Integer, primary_key=True)
is_manager = Column(Boolean, nullable=False, default=False)
department_id = Column(Integer, ForeignKey('departments.department_id'), nullable=False)
department = relationship('Department', back_populates='employees')
You can find the manager of the department using
department = session.query(Department).get(..)
department.employees.filter(Employee.is_manager == True).one_or_none()
What are the proper way to define a deletion cascade on many-to-many relationship.
Trying to achieve a setup where a deletion of a user only deletes the the user and the relationship stored in 'user_favorited'.
The different scenarios led that the user + relationship and user favorited/favorited_by where deleted.
Adding to the complexity are following parameters which can be added to the relationship and backref.
single_parent
viewonly (stackoverflow)
Goal:
deletion of a first user should not lead to deletion of second user favorited/favorited by. Only First user and relationship(s) should be deleted.
class User(Base):
__tablename__ = 'user'
id = Column(Integer, primary_key=True)
username = Column(String(30), nullable=False, unique=True, index=True)
favorites = relationship("User", secondary='user_favorited',
primaryjoin=UserFavorited.user_id==id,
secondaryjoin=UserFavorited.favorite_user_id==id,
backref=backref('favorites_list', lazy='dynamic', cascade='all,delete-orphan'),
cascade="all",
order_by=UserFavorited.favorited_on.desc())
favorited_by = relationship("User", secondary='user_favorited',
primaryjoin=UserFavorited.favorite_user_id==id,
secondaryjoin=UserFavorited.user_id==id,
backref=backref('favorited_list', lazy='dynamic', cascade='all,delete-orphan'),
cascade="all",
order_by=UserFavorited.favorited_on.desc())
class UserFavorited(Base):
__tablename__ = 'user_favorited'
user_id = Column(Integer, ForeignKey('user.id'), primary_key=True)
favorite_user_id = Column(Integer, ForeignKey('user.id'), primary_key=True, index=True)
favorited_on = Column(DateTime(timezone=True), nullable=False)
I am trying to build a Many-to-Many relationship using Flask-SQLAlchemy using two primary keys from one model and one from another. My models are the following:
Service:
class Service(db.Model):
"""
Service model object
"""
name = db.Column(db.String(120), primary_key=True, nullable=False)
description = db.Column(db.Text)
ContactClosure:
class ContactClosure(db.Model):
module = db.Column(db.Integer, primary_key=True, nullable=False)
relay = db.Column(db.Integer, primary_key=True, nullable=False)
status = db.Column(db.Boolean)
description = db.Column(db.Text)
#Relationships
hostname = db.Column(db.String(120), db.ForeignKey('ip2cc.hostname'), primary_key=True, nullable=False)
device_name = db.Column(db.String(120), db.ForeignKey('device.name'), primary_key=True, nullable=False)
services = db.relationship('Service', secondary=cc_services, backref=db.backref('contact_closures', lazy='dynamic'))
This is the related table:
cc_services = db.Table('cc_services',
db.Column('service_name', db.String(120), db.ForeignKey('service.name')),
db.Column('hostname', db.String(120), db.ForeignKey('contact_closure.hostname')),
db.Column('device_name', db.String(120), db.ForeignKey('contact_closure.device_name')),
)
And this is the error I am getting:
"AmbiguousForeignKeysError: Could not determine join condition between
parent/child tables on relationship ContactClosure.services - there
are multiple foreign key paths linking the tables via secondary table
'cc_services'. Specify the 'foreign_keys' argument, providing a list
of those columns which should be counted as containing a foreign key
reference from the secondary table to each of the parent and child
tables."
If anybody can tell what is the problem here I will be highly thankful, I've been stuck on this for a while...
Ok, finally I found the solution to this problem:
cc_services = db.Table('cc_services',
db.Column('service_name', db.String(120),
db.ForeignKey('service.name')),
db.Column('cc_hostname', db.String(120)),
db.Column('cc_module', db.Integer),
db.Column('cc_relay', db.Integer),
ForeignKeyConstraint(
('cc_hostname', 'cc_module', 'cc_relay'),
('contact_closure.hostname', 'contact_closure.module', 'contact_closure.relay')
)
)
If a model has multiple keys, they must be declared on the helper table on a ForeignKeyConstraint statement.
You need to define 'foreign_keys' argument as the error text says, like this:
services = db.relationship('Service', secondary=cc_services, backref=db.backref('contact_closures', lazy='dynamic'), foreign_keys=[column_name])
If you're using an association table or fully declared table metadata, you can use the primary_key=True in both columns, as suggested here.
Association table example:
employee_role = db.Table(
"employee_role",
db.Column("role_id", db.Integer, db.ForeignKey("role.id"), primary_key=True),
db.Column("employee_id", db.Integer, db.ForeignKey("agent.id"), primary_key=True),
)
Metadata example:
# this is using SQLAlchemy
class EmployeeRole(Base):
__tablename__ = "employee_role"
role_id = Column(Integer, primary_key=True)
employee_id = Column(Integer, primary_key=True)
# this is using Flask-SQLAlchemy with factory pattern, db gives you access to all SQLAlchemy stuff
class EmployeeRole(db.Model):
__tablename__ = "employee_role"
role_id = db.Column(db.Integer, primary_key=True)
employee_id = db.Column(db.Integer, primary_key=True)
Alembic migration for it:
op.create_table(
'employee_role',
sa.Column('role_id', sa.Integer(), nullable=False),
sa.Column('employee_id', sa.Integer(), nullable=False),
sa.PrimaryKeyConstraint('role_id', 'employee_id')
)
SQL:
CREATE TABLE agent_role (
role_id INTEGER NOT NULL,
employee_id INTEGER NOT NULL,
PRIMARY KEY (role_id, employee_id)
);
In terms of relationship, declare it on one side (this should give you role.employees or employee.roles which should return a list):
# this is using Flask-SQLAlchemy with factory pattern, db gives you access to all SQLAlchemy stuff
class Employee(db.Model):
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
roles = db.relationship("Role", secondary=employee_role, backref="employee")
Your Role class can be:
# this is using Flask-SQLAlchemy with factory pattern, db gives you access to all SQLAlchemy stuff
class Role(db.Model):
__tablename__ = "role"
id = db.Column(db.Integer, primary_key=True, autoincrement=True)
name = db.Column(db.String(25), nullable=False, unique=True)