I want to make a column "immutable" or "unupdateable" in SQLAlchemy.
Right now I am using an event listener that raises an exception if that column is updated:
@event.listens_for(Person.email, 'set')
def receive_set_person_email(target, value, old_value, initiator):
if old_value != symbol('NEVER_SET') and old_value != symbol('NO_VALUE'):
raise AttributeError("Cannot change a Person's email")
But I wonder if something like this is already built-in, or if I could get to a prettier and more generic solution.
This was my solution:
from sqlalchemy import event
from sqlalchemy.util.langhelpers import symbol
class NonUpdateableColumnError(AttributeError):
def __init__(self, cls, column, old_value, new_value, message=None):
self.cls = cls
self.column = column
self.old_value = old_value
self.new_value = new_value
if message is None:
self.message = 'Cannot update column {} on model {} from {} to {}: column is non-updateable.'.format(
column, cls, old_value, new_value)
def make_nonupdateable(col):
@event.listens_for(col, 'set')
def unupdateable_column_set_listener(target, value, old_value, initiator):
if old_value != symbol('NEVER_SET') and old_value != symbol('NO_VALUE') and old_value != value:
raise NonUpdateableColumnError(col.class_.__name__, col.name, old_value, value)
class Person(Base):
email = Column(String)
make_nonupdateable(Person.email)
You could make email
an internal column that can't be written to with the ORM.
class Person(Base):
__tablename__ = 'person'
id = Column(Integer, primary_key=True)
_email = Column("email", String)
@hybrid_property
def email(self):
return self._email
Cannot write to it:
>>> p = Person(email="[email protected]")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<string>", line 4, in __init__
File "/Library/Python/2.7/site-packages/sqlalchemy/orm/state.py", line 306, in _initialize_instance
manager.dispatch.init_failure(self, args, kwargs)
File "/Library/Python/2.7/site-packages/sqlalchemy/util/langhelpers.py", line 60, in __exit__
compat.reraise(exc_type, exc_value, exc_tb)
File "/Library/Python/2.7/site-packages/sqlalchemy/orm/state.py", line 303, in _initialize_instance
return manager.original_init(*mixed[1:], **kwargs)
File "/Library/Python/2.7/site-packages/sqlalchemy/ext/declarative/base.py", line 649, in _declarative_constructor
setattr(self, k, kwargs[k])
AttributeError: can't set attribute
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With