Accessing a mixin member variable (column) from the child

别说谁变了你拦得住时间么 提交于 2019-12-02 18:13:30

问题


I'm having trouble accessing a parent class member variable id from the child class.

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

class User(db.Model, BaseModel):
    username = db.Column(db.String(35), nullable=False, unique=True)

    followed = db.relationship(
        'User',
        secondary=followers,
        primaryjoin=(followers.c.follower_id == BaseModel.id),
        secondaryjoin=(followers.c.followed_id == BaseModel.id),
        backref=db.backref('followers', lazy='dynamic'),
        lazy='dynamic')

Gives me this error:

sqlalchemy.exc.InvalidRequestError: One or more mappers failed to initialize - can't proceed with initialization of other mappers. Triggering mapper: 'Mapper|User|user'. Original exception was: Could not locate any simple equality expressions involving locally mapped foreign key columns for primary join condition 'followers.follower_id = "<name unknown>"' on relationship User.followed.  Ensure that referencing columns are associated with a ForeignKey or ForeignKeyConstraint, or are annotated in the join condition with the foreign() annotation. To allow comparison operators other than '==', the relationship can be marked as viewonly=True.

On the other hand

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

class User(db.Model, BaseModel):
    username = db.Column(db.String(35), nullable=False, unique=True)

    testing = BaseModel.id

Gives me this error:

sqlalchemy.exc.InvalidRequestError: Incorrect number of values in identifier to formulate primary key for query.get(); primary key columns are 'user.testing','user.id

回答1:


A class definition's body is its own scope and the names introduced will form the class' namespace. The problem (in your original question) with your model definition is that the name id has not been assigned to in the body of the class User, so the joins in the relationship definition refer to the builtin function id(). You also cannot use BaseModel.id as shown in this question, because the Declarative metaclass will create a copy of it to User, so it does not refer to the same column.

The solution is to use lazy evaluation: either pass a callable, or a Python-evaluable string as the join:

followed = db.relationship(
    'User',
    secondary=followers,
    primaryjoin='followers.c.follower_id == User.id',   # Note: the joins are
    secondaryjoin='followers.c.followed_id == User.id', # passed as strings 
    backref=db.backref('followers', lazy='dynamic'),
    lazy='dynamic')

This works because the inter-mapper relationships of mappers are configured after the mapped classes have been declared and are used for the first time, unless explicitly configured using configure_mappers().

Note that you cannot use plain id in the evaluable string either, but must instead refer to it using the User class, since it is evaluated later in a completely different scope from that of the class body, including names from the Declarative class registry, and metadata.

The last error is the result of code such as

User.query.get(some_id)

The assignment of BaseModel.id to another name – such as testing – in the class body results in your model having a composite primary key, formed from 2 integer columns, so Query.get() expected to receive a 2-tuple of integers, not just one integer.



来源:https://stackoverflow.com/questions/52414291/accessing-a-mixin-member-variable-column-from-the-child

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!