我有一个声明性映射:
class User(base):
username = Column(Unicode(30), unique=True)
Run Code Online (Sandbox Code Playgroud)
我怎么能告诉 sqlalchemy 这个属性不能被修改?我想出的解决方法有点笨拙:
from werkzeug.utils import cached_property
# regular @property works, too
class User(base):
_username = Column('username', Unicode(30), unique=True)
@cached_property
def username(self):
return self._username
def __init__(self, username, **kw):
super(User,self).__init__(**kw)
self._username=username
Run Code Online (Sandbox Code Playgroud)
在数据库列权限级别上执行此操作将不起作用,因为并非所有数据库都支持该操作。
您可以使用validatesSQLAlchemy 功能。
from sqlalchemy.orm import validates
...
class User(base):
...
@validates('username')
def validates_username(self, key, value):
if self.username: # Field already exists
raise ValueError('Username cannot be modified.')
return value
Run Code Online (Sandbox Code Playgroud)
参考:https://docs.sqlalchemy.org/en/13/orm/mapped_attributes.html#simple-validators
我可以建议以下方法来保护列免受修改:
首先是在设置任何属性时使用钩子:
如果以上Base声明性表中的所有列都将被挂钩,因此您需要以某种方式存储有关列是否可以修改的信息。例如,您可以继承 sqlalchemy.Column 类以向其添加一些属性,然后检查挂钩中的属性。
class Column(sqlalchemy.Column):
def __init__(self, *args, **kwargs):
self.readonly = kwargs.pop("readonly", False)
super(Column, self).__init__(*args, **kwargs)
# noinspection PyUnusedLocal
@event.listens_for(Base, 'attribute_instrument')
def configure_listener(class_, key, inst):
"""This event is called whenever an attribute on a class is instrumented"""
if not hasattr(inst.property, 'columns'):
return
# noinspection PyUnusedLocal
@event.listens_for(inst, "set", retval=True)
def set_column_value(instance, value, oldvalue, initiator):
"""This event is called whenever a "set" occurs on that instrumented attribute"""
logging.info("%s: %s -> %s" % (inst.property.columns[0], oldvalue, value))
column = inst.property.columns[0]
# CHECK HERE ON CAN COLUMN BE MODIFIED IF NO RAISE ERROR
if not column.readonly:
raise RuntimeError("Column %s can't be changed!" % column.name)
return value
Run Code Online (Sandbox Code Playgroud)
要挂钩具体属性,您可以执行以下操作(不需要将属性添加到列):
# standard decorator style
@event.listens_for(SomeClass.some_attribute, 'set')
def receive_set(target, value, oldvalue, initiator):
"listen for the 'set' event"
# ... (event handling logic) ...
Run Code Online (Sandbox Code Playgroud)
这是有关 SQLAlchemy 事件的指南。
我建议的第二种方法是使用标准 Python 属性或 SQLAlchemy hybrid_property,正如您在问题中所示的那样,但使用这种方法会导致代码增长。
PS 我认为紧凑的方式是将属性添加到列并挂钩所有设置事件。
| 归档时间: |
|
| 查看次数: |
2331 次 |
| 最近记录: |