How to set up a table with a recursive foreign key

2020-08-05 11:05发布

问题:

Suppose I have a table "nodes" where I store a tree. Each node has a primary key id and a column parent_id. Of course, I want to access a parent attribute of each node instance, that is, a relation. One might try:

import sqlalchemy, sqlalchemy.orm, sqlalchemy.ext.declarative
engine = sqlalchemy.create_engine('sqlite:///PATHTOMYDATABASE', echo=True)
Base = sqlalchemy.ext.declarative.declarative_base()
class Node(Base):
    __tablename__ = "nodes"
    id = sqlalchemy.Column(sqlalchemy.Integer, primary_key=True) 
    parent_id = sqlalchemy.Column(sqlalchemy.Integer, sqlalchemy.ForeignKey("nodes.id"))
    parent = sqlalchemy.orm.relationship("Node", backref=sqlalchemy.orm.backref("childs"))
Base.metadata.create_all(engine)

But when I do that, I get an error:

sqlalchemy.exc.InvalidRequestError: Table 'nodes' is already defined for this MetaData instance. Specify 'useexisting=True' to redefine options and columns on an existing Table object.

I don't understand at what point I could set that option 'useexisting=True'. Is this the right way?

EDIT: In fact, the error comes indirectly from another part of the original script. If one replaces the database path by the temporary database :memory:, it works without problems. Thanks to TokenMacGuy.

So the above example can be considered as a working example.

回答1:

For some reason, you already have a class registered for that table, or you have defined the table (possibly indirectly) twice. This happens to me most frequently when I'm experimenting on the python command line; sqlalchemy remembers the table definitions and class mappings longer than is immediately obvious (from the lifetime of the actual MetaData instance). Make sure you are defining the table only once (exit out of the python interpreter and restart it, if you are in one). Other causes why they might be already present is from table reflection, or something has called reload() without really clearing out sys.modules first.

If you are using table reflection, you pass the useexisting=True option to the declarative extension in the __table_args__ class variable:

class Node(Base):
    __tablename__ = "nodes"
    __table_args__ = {"useexisting": True}

But only do that if you are sure that you are defining the table intentionally before you define the python class.

If none of these seem to fix the problem, please reduce your module to a minimal example that shows the problem and then post the whole thing in your answer.