Dan Rubio
Dan Rubio

Reputation: 4907

NoForeignKey error on relationship even though SQLAlchemy model specifies foreign key

I am having some difficulty setting up a one to one relationship between two models in my flask application. I have two models Employeeand `Photo'. An employee has only one photo associated with it and vice-versa.

This is the code that I have in my models.py file:

class Employee(db.Model):
    __tablename__ = 'employees'
    id = db.Column(db.Integer, primary_key=True)
    photo = db.relationship("Photo", uselist=False, back_populates='employees')

class Photo(db.Model):
    __tablename__ = 'photos'
    id = db.Column(db.Integer, primary_key=True)
    employee_id = db.Column(db.Integer, db.ForeignKey('employees.id'))
    employee = db.relationship('Photo', back_populates='photo')

I've followed the instruction on the SQL Alchemy documentation found hereSQL Alchemy simple relationships. The error that I keep encountering is shown below:

sqlalchemy.exc.NoForeignKeysError: Could not determine join condition between parent/child tables on relationship Photo.employee
 - 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 clearly specify the foreign key right here employee_id = db.Column(db.Integer, db.ForeignKey('employees.id')) . I'm not sure what I'm doing wrong. Additionally, I was reading the documentation and it doesn't help that uselist, backref, and back_populates are so similar.

Can someone assist me with this? Help would be greatly appreciated.

One to One relationship stack overflow question

Upvotes: 2

Views: 2168

Answers (1)

davidism
davidism

Reputation: 127200

backref automatically adds the reverse relationship to the related model. You can pass a db.backref object to it to specify options to the relationship. back_populates tells SQLAlchemy to populate an existing reverse relationship, rather than creating it. uselist tells SQLAlchemy whether the relationship is a list or not, for cases where it can't determine that automatically.

In your example, you need one relationship, with one backref that is a single item.

You have two typos in your code. First, back_populates='employees' should refer to 'employee', which is what you called the property on the related model. Second, employee = relationship('Photo' is pointing at the wrong model, it should relate to Employee.

from flask import Flask
from flask_sqlalchemy import SQLAlchemy

app = Flask(__name__)
db = SQLAlchemy(app)
db.engine.echo = True

class Photo(db.Model):
    id = db.Column(db.Integer, primary_key=True)

class Employee(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    photo_id = db.Column(db.ForeignKey(Photo.id))
    photo = db.relationship(Photo, backref=db.backref('employee', uselist=False))

db.create_all()
db.session.add(Employee(photo=Photo()))
db.session.commit()
print(Employee.query.get(1).photo)

Upvotes: 2

Related Questions