Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SQLAlchemy: foreign key to multiple tables

Let's consider 3 tables:

  • books
  • American authors
  • British authors

Each book has a foreign key to its author, which can either be in the American table, or the British one.

How can I implement such foreign key condition in SQLAlchemy?

I'd like to have a single column to handle the link.


My approach so far was to create an abstract class Author, from which both AmericanAuthor and BritishAuthor inherit, and have the foreign key of Book point to the parent.

class Author(Model):
    __abstract__ = True
    id = db.Column(db.Integer, primary_key=True)
    name = db.Column(db.String)

class AmericanAuthor(Author):
    __tablename__ = 'american_author'
    # some other stuff

class BritishAuthor(Author):
    __tablename__ = 'british_author'
    # some other stuff

class Book(Model):
    __tablename__ = 'book'
    title = db.Column(db.String)
    author_id = db.Column(db.Integer, db.ForeignKey("author.id"))

It fails with the error:

sqlalchemy.exc.NoReferencedTableError: Foreign key associated with column 'books.author_id' could not find table 'author' with which to generate a foreign key to target column 'id'

Which completely makes sense, considering author is abstract...

like image 469
3kt Avatar asked Jul 15 '19 13:07

3kt


People also ask

Can a foreign key reference multiple tables?

The FOREIGN KEY constraint is a key used to link two tables together.

What is foreign key in SQLAlchemy?

A foreign key in SQL is a table-level construct that constrains one or more columns in that table to only allow values that are present in a different set of columns, typically but not always located on a different table.

How to create a table with multiple foreign keys in SQL?

How to Create a Table With Multiple Foreign Keys in SQL? When a non-prime attribute column in one table references the primary key and has the same column as the column of the table which is prime attribute is called a foreign key. It lays the relation between the two tables which majorly helps in the normalization of the tables.

What is a foreign key in SQLAlchemy?

The foreign key is the “joint” that connects together pairs of rows which have a relationship with each other, and SQLAlchemy assigns very deep importance to this concept in virtually every area of its operation.

What is foreignkeyconstraintand Index in SQL alchemy?

In SQLAlchemy the key classes include ForeignKeyConstraintand Index. Defining Foreign Keys¶ A foreign keyin SQL is a table-level construct that constrains one or more columns in that table to only allow values that are present in a different set of columns, typically but not always located on a different table.

What is SQLAlchemy noforeignkeyserror?

sqlalchemy.exc.NoForeignKeysError: Could not determine join condition between parent/child tables on relationship Author.articles - there are no foreign keys linking these tables via secondary table ‘authorarticle’.


2 Answers

I think you can't build a relationship with two different tables using the same column.

Try creating two different columns ("american_author_id" and "british_author_id") then make a @property "author" that returns the author that isn't NULL.

This way you can get the author using: mybook.author

like image 159
lorenzo_campanile Avatar answered Oct 01 '22 14:10

lorenzo_campanile


Although the @property decorator will work in the application it might be better to use the @hybrid_property from the sqlalchemy.ext.hybrid package. In that way you will be able to filter on that property just like any normal attribute.

Your Book class would then look like:

class Book(Model):
    __tablename__ = 'book'
    title = db.Column(db.String)
    american_author_id = db.Column(db.Integer, db.ForeignKey("american_author.id"), nullable=True)
    british_author_id = db.Column(db.Integer, db.ForeignKey("british_author.id"), nullable=True)

    @hybrid_property
    def author_id(self):
        return self.american_author_id or self.british_author_id
like image 35
Carl Onsjö Avatar answered Oct 01 '22 15:10

Carl Onsjö