Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Flask-admin: catch changes on inline_models

I have a simple application, full code presented here. It uses flask-admin

the models:

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


class UserNote(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    body = db.Column(db.String, nullable=False)
    user_id = db.Column(db.Integer, db.ForeignKey('users.id'))
    author_id = db.Column(db.Integer, db.ForeignKey('users.id'))
    user = db.relationship(User,
                           foreign_keys='UserNote.user_id',
                           backref=db.backref('user_notes', order_by=id))
    author = db.relationship(User,
                             foreign_keys='UserNote.author_id',
                             backref=db.backref('author_user_notes',
                                                order_by=id))

and the main app.py:

app = Flask(__name__)

class ModelFormWithAuthorship(InlineFormAdmin):
    def on_model_change(self, form, model):
        user = User.query.first()
        print('++++++')
        print(user)
        print(model)
        print('++++++')
        model.author = user

class UserModelView(ModelView):
    inline_models = (ModelFormWithAuthorship(UserNote),)


admin = Admin(app, url='/admin', name='MyAdmin', template_mode='bootstrap3')
admin.add_view(UserModelView(User, db.session))

I want to automatically set the first user from the query as author of the changed UserNote. It's simplification for a current logged in user.

But the flask admin passes all inline models to this method.

Here is steps for reporduce

  1. Start application
  2. Create user with name user1 (this user might be the first())
  3. Create user with name admin1
  4. Create user with name admin2
  5. Create 2 inline notes, it will have the user1 as author
  6. Connect to the database directly and change author of one of the note to admin1 and the other note to the admin2
  7. Go to the Flask-Admin and make sure, that you have 2 notes from 2 users
  8. Edit body of first note and click save

Expected result for these actions for me was, that first note will have user1 as author and author of the second note will remain the same. But Actual result is following: author for both notes set to the user1.

I've tried to debug, and found some interesting attributes:

# Not changed note
model._sa_instance_state._attached = {bool} True
model._sa_instance_state.deleted = {bool} False
model._sa_instance_state.detached = {bool} False
model._sa_instance_state.expired = {bool} False
model._sa_instance_state.has_identity = {bool} True
model._sa_instance_state.pending = {bool} False
model._sa_instance_state.persistent = {bool} True
model._sa_instance_state.transient = {bool} False
model.body = {str} 'test1'
model._sa_instance_state.modified = {bool} True

# Changed note
model._sa_instance_state._attached = {bool} True
model._sa_instance_state.deleted = {bool} False
model._sa_instance_state.detached = {bool} False
model._sa_instance_state.expired = {bool} False
model._sa_instance_state.has_identity = {bool} True
model._sa_instance_state.pending = {bool} False
model._sa_instance_state.persistent = {bool} True
model._sa_instance_state.transient = {bool} False
model.body = {str} 'test2A'
model._sa_instance_state.modified = {bool} True

But they're indistinguishable from each other.

Is there way to catch only changed models?

like image 805
Nikolai Golub Avatar asked Nov 04 '25 08:11

Nikolai Golub


1 Answers

You can use WTForms' Field.object_data attribute:

This is the data passed from an object or from kwargs to the field, stored unmodified. This can be used by templates, widgets, validators as needed (for comparison, for example)

So you can compare UserNote.body old and new values, then change the author if needed like this:

class ModelFormWithAuthorship(InlineFormAdmin):
    def on_model_change(self, form, model):    
        # Check whether note's body has changed
        if form.body.object_data != form.body.data:
            model.author = User.query.first()
like image 88
Dauros Avatar answered Nov 07 '25 02:11

Dauros



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!