Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unable to create self referencing foreign key in flask-sqlalchemy

I have a model Region and each Region can have sub-regions. Each sub-region has a field parent_id which is the id of its parent region. Here is how my model looks like

class Region(db.Model):
    __tablename__ = 'regions'
    __table_args__ = {'schema': 'schema_name'}
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String(100))
    parent_id = db.Column(db.Integer, db.ForeignKey('regions.id'))
    parent = db.relationship('Region', primaryjoin=('Region.parent_id==Region.id'), backref='sub-regions')
    created_at = db.Column(db.DateTime, default=db.func.now())
    deleted_at = db.Column(db.DateTime)

Bu when i try to do db.create_all i get this error sqlalchemy.exc.NoReferencedTableError: Foreign key associated with column 'regions.parent_id' could not find table 'regions' with which to generate a foreign key to target column 'id'

Why cant it find regions when i am specifying it in __tablename__? I am using flask-sqlalchemy version 1.0

EDIT -- i removed the line

__table_args__ = {'schema': 'schema_name'}

from my code and it works. Beats the hell out of me.

like image 826
lovesh Avatar asked Dec 29 '13 21:12

lovesh


2 Answers

I had the same issue with the schema name argument. What I changed to get it to work was to reference the table class directly in ForeignKey and relationships instead of using a string. Example:

parent_id = Column(Integer, ForeignKey(Region.id), index=True)

parent = relationship(lambda: Region, remote_side=id, backref='sub_regions')
like image 109
braaksman Avatar answered Sep 28 '22 07:09

braaksman


You must tell SQLAlchemy what the "remote side" of the relationship is, to distinguish between the current row and the row being joined to. The relevant explanation is located partway into this section of the documentation on relationships.

The relationship might look like this:

parent = db.relationship('Region', remote_side=id, backref='sub_regions')

This is an example demonstrating a self-referential relationship:

from sqlalchemy import Column, Integer, String, ForeignKey
from sqlalchemy.engine import create_engine
from sqlalchemy.ext.declarative.api import declarative_base
from sqlalchemy.orm import sessionmaker, relationship

engine = create_engine('sqlite:///:memory:', echo=True)
Session = sessionmaker(engine)
Base = declarative_base(engine)

session = Session()


class Region(Base):
    __tablename__ = 'region'

    id = Column(Integer, primary_key=True)
    name = Column(String, nullable=False)
    parent_id = Column(Integer, ForeignKey('region.id'), index=True)

    parent = relationship(lambda: Region, remote_side=id, backref='sub_regions')


Base.metadata.create_all()

r1 = Region(name='United States of America')
r2 = Region(name='California', parent=r1)

session.add_all((r1, r2))
session.commit()

ca = session.query(Region).filter_by(name='California').first()
print ca.parent.name

There will be many lines of SQL output since echo is on, and the script will print 'United States of America' at the end.

like image 45
davidism Avatar answered Sep 28 '22 09:09

davidism