YE YUE
YE YUE

Reputation: 23

flask_sqlalchemy: why instance created by Model.query.filter_by().first() doesn't run __init__() in model

I have a User model like this:

class User(UserMixin, db.Model):
    id = db.Column(db.Integer, primary_key = True)
    username = db.Column(db.String(64), unique=True, index=True
    # ...

    def __init__(self, **kwargs):
        pass

When the instance is created like:user = User(username='john'), the constructor is called successfully.

But if the instance is returned by user = User.query.filter_by(username=john).first(), the constructor won't be called anyway.

And I have checked that the instance returned by the query is an instance of User, so the constructor should be called when the query is done. Because to my understanding, the constructor is called every time when an instance is created.

The question is: why this happens and what to do to call __init__ when the user is returned by the query.

Upvotes: 1

Views: 384

Answers (1)

augustomen
augustomen

Reputation: 9739

I just ran accross this same problem (constructor not being called), and reading through SQLAlchemy documentation, that's exactly how it's supposed to behave. When an instance is being created from a Query object, it doesn't call the __init__ method. Instead, it calls methods marked as reconstructor (decorator).

To mark a method as reconstructor, declare a parameterless method and decorate it with the method from your SQLAlchemy instance:

class User(UserMixin, db.Model):

    @db.reconstructor
    def reload(self):
        # Will be run when loading from a Query object

From the documentation:

Any method may be tagged as the reconstructor(), even the __init__ method. SQLAlchemy will call the reconstructor method with no arguments.

So, if your class has initialization code, or if you call super(), you can call your reconstructor from your __init__:

class User(UserMixin, db.Model):

    def __init__(self, **kwargs):
        super(User, self).__init__(**kwargs)
        self.reload()

    @db.reconstructor
    def reload(self):
        # Process data from model

That way, reload() will be called upon custom initialization and when queried.

Upvotes: 3

Related Questions