0
votes

There are quite a few posts on this error but none of the answers apply to me, as far as I can see my code already conforms to the answers in the other posts.

I import my complete model and it has all classes, columns and relationships defined. In my code on the very first statement I execute I get this error:

InvalidRequestError: When initializing mapper mapped class Application->application, expression 'Version' failed to locate a name ("name 'Version' is not defined"). If this is a class name, consider adding this relationship() to the class after both dependent classes have been defined.

Interestingly the first statement I execute that "triggers" the error does not even involve the classes in the message. For the record it is user_mk = User.query.filter_by(user_name='mk').first().

The part of the model that errors is a simple 1-M relationship.

from flask import Flask
from flask_sqlalchemy import SQLAlchemy
...etc...
app = Flask(__name__)
app.config.from_object(TestConfig)  
db = SQLAlchemy(app)

class User(UserMixin, db.Model)
    __bind_key__ = 'app'
    __tablename__ = 'user'

...etc...

class Version(db.Model):
    __bind_key__ = 'app'
    __tablename__ = 'version'

    version_id = db.Column(db.Integer, primary_key=True)
    version_number = db.Column(db.Integer, nullable=False)
    release_number = db.Column(db.Integer, nullable=False)
    modification_number = db.Column(db.Integer, nullable=False, server_default=db.text("0"))
    test_status = db.Column(db.Integer, index=True)
    lifecycle = db.Column(db.Text, index=True, server_default=db.text("\"Current\""))
    timestamp = db.Column(db.DateTime, server_default=db.text("CURRENT_TIMESTAMP"))
    notes = db.Column(db.Text)

class Application(db.Model):
    __bind_key__ = 'app'
    __tablename__ = 'application'

    application_id = db.Column(db.Integer, primary_key=True)
    application_name = db.Column(db.Text)
    version_id = db.Column(db.ForeignKey('version.version_id'), index=True)
    description = db.Column(db.Text)
    notes = db.Column(db.Text)

    version = db.relationship('Version')

I cannot understand how SQLAlchemy cannot "see" the Version class from the Application class. What am I Doing wrong?

UPDATE Following the suggestion in the answer below a different error occurs.

sqlalchemy.exc.InvalidRequestError: One or more mappers failed to initialize - can't proceed with initialization of other mappers. Triggering mapper: 'mapped class Version->version'. Original exception was: Could not determine join condition between parent/child tables on relationship Version.applications - there are no foreign keys linking these tables. Ensure that referencing columns are associated with a ForeignKey or ForeignKeyConstraint, or specify a 'primaryjoin' expression.

The code I tried is

class Version(db.Model):
    __bind_key__ = 'app'
    __tablename__ = 'version'

    version_id = db.Column(db.Integer, primary_key=True)
    version_number = db.Column(db.Integer, nullable=False)
    release_number = db.Column(db.Integer, nullable=False)
    modification_number = db.Column(db.Integer, nullable=False, server_default=db.text("0"))
    test_status = db.Column(db.Integer, index=True)
    lifecycle = db.Column(db.Text, index=True, server_default=db.text("\"Current\""))
    timestamp = db.Column(db.DateTime, server_default=db.text("CURRENT_TIMESTAMP"))
    notes = db.Column(db.Text)

    applications = db.relationship('Application', 
                    backref='version',
                    primaryjoin='Version.version_id == Application.version_id',
                    )

class Application(db.Model):
    __bind_key__ = 'app'
    __tablename__ = 'application'

    application_id = db.Column(db.Integer, primary_key=True)
    application_name = db.Column(db.Text)
    version_id = db.Column(db.ForeignKey('version.version_id'), index=True)
    description = db.Column(db.Text)
    notes = db.Column(db.Text)

ANSWER Thanks @djnz. The answer that works is as follows, but it is not clear why. Observe that the primaryjoin clause under Version has been replaced with a foreign_keys clause.

class Version(db.Model):
    __bind_key__ = 'app'
    __tablename__ = 'version'

    version_id = db.Column(db.Integer, primary_key=True)
    version_number = db.Column(db.Integer, nullable=False)
    release_number = db.Column(db.Integer, nullable=False)
    modification_number = db.Column(db.Integer, nullable=False, server_default=db.text("0"))
    test_status = db.Column(db.Integer, index=True)
    lifecycle = db.Column(db.Text, index=True, server_default=db.text("\"Current\""))
    timestamp = db.Column(db.DateTime, server_default=db.text("CURRENT_TIMESTAMP"))
    notes = db.Column(db.Text)

    applications = db.relationship('Application', 
                    backref='version',
                    foreign_keys="Application.version_id",
                    )

class Application(db.Model):
    __bind_key__ = 'app'
    __tablename__ = 'application'

    application_id = db.Column(db.Integer, primary_key=True)
    application_name = db.Column(db.Text)
    version_id = db.Column(db.ForeignKey('version.version_id'), index=True)
    description = db.Column(db.Text)
    notes = db.Column(db.Text)
1
Are all models defined in a single file, or separate files? Do they all use the same db instance? Have you made sure that all models get imported/defined before first use?Ilja Everilä
Yes, all models in one file imported at the start of the script.Mark Kortink

1 Answers

1
votes

This is likely where backref or back_populates is needed. These are used by SQLAlchemy to map the relationship.

Using backref (on your Version model):

applications = db.relationship("Version", backref="version")

This is the same as writing:

# Version model:
applications = db.relationship("Application", back_populates="version")

# Application model:
version = db.relationship("Version", backref="applications")

This mapping then adds the parent/child reference to the model, like my_version.applications, or my_application.version