1

After failing to use vars to change db.Model(flask-sqlalchemy) value I solved it by using setattr instead, but after reading doc still don't get the difference between vars and setattr.

Here is what I have tried

Failed in vars

Code

from flask_sqlalchemy import SQLAlchemy

db = SQLAlchemy()

def set_option_parameters(var, option_keys, options):
    for option_key in options:
        if option_key in option_keys and options[option_key] is not None:
            vars(var)[option_key] = options[option_key]
            # setattr(var, option_key, options[option_key])

class Application(db.Model):
    id = db.Column(db.BigInteger(), primary_key=True, autoincrement=True)
    name = db.Column(db.VARCHAR(20), nullable=False)
    ...
    def __init__(self, options):
        option_keys = set(["name"])
        set_option_parameters(self, option_keys, options)

    def modification(self, options):
        modifiable = set(["name"])
        set_option_parameters(self, modifiable, options)

vars(var)[option_key] = options[option_key] works fine in initing a Application object , but failed in modification(name didn't change).

Log/Test

And I tried to print the application.__dict__ before db.session.commit(), it did be modified!

application = Application.query.filter_by(id=args["id"]).first()
# args["name"] is not None
app.logger.info(f"Before: {application.__dict__}")
application.modification(args)
app.logger.info(f"After: {application.__dict__}")
db.session.commit()

output

[2019-02-27 11:22:59,209] INFO in equipmentbaseapplicationhandler: Before:{'_sa_instance_state': <sqlalchemy.orm.state.InstanceState object at 0x7f718ac2b588>, 'id': 9, 'name': 'old_name'}
[2019-02-27 11:22:59,209] INFO in equipmentbaseapplicationhandler: After:{'_sa_instance_state': <sqlalchemy.orm.state.InstanceState object at 0x7f718ac2b588>, 'id': 9, 'name': 'new_name'}

BUT When I checked in mysql, it did not work

Successed in setattr

Then I changed to setattr(var, option_key, options[option_key]) in function set_option_parameters.

it works good in both init and modification, and the output of application.__dict__ is the same!

Aries_is_there
  • 386
  • 2
  • 13

1 Answers1

1

From the docs for vars:

Objects such as modules and instances have an updateable dict attribute; however, other objects may have write restrictions on their dict attributes (for example, classes use a types.MappingProxyType to prevent direct dictionary updates).

Looking through the SQLAlchemy source code (for instance, here), it is a common pattern to return a copy of some_model.__dict__ when accessed, rather than returning the __dict__ itself. So you are likely updating a copy rather than the original. Otherwise, it's possible that the dict is getting overwritten by the attributes themselves, even if the dict changes.

setattrs is the correct pattern, and is more explicit.

thejohnbackes
  • 1,255
  • 11
  • 21