sqlalchemy:如何阻止特定列的更新

时间:2014-05-04 11:09:11

标签: sqlalchemy

我有一个声明性映射:

class User(base):
    username = Column(Unicode(30), unique=True)

如何判断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

在数据库列权限级别执行此操作将不起作用,因为并非所有数据库都支持该功能。

3 个答案:

答案 0 :(得分:1)

我可以建议以下方法保护列不被修改:

首先在设置任何属性时使用钩子

如果上面的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

要挂钩具体属性,您可以采用下一种方式(不需要向列中添加属性):

# 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) ...

Here是关于SQLAlchemy事件的指南。

我建议的第二种方法是使用您在问题中显示的标准Python属性或SQLAlchemy hybrid_property,但使用此方法会导致代码增长。

P.S。我认为紧凑的方法是将属性添加到列并挂钩所有设置事件。

答案 1 :(得分:1)

您可以使用validates SQLAlchemy功能。

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

参考:https://docs.sqlalchemy.org/en/13/orm/mapped_attributes.html#simple-validators

答案 2 :(得分:0)

对@AlexQueue 的回答稍作修正。

#include <iostream>
#include <SFML/Graphics.hpp>

int WIDTH = 600;
int HEIGHT = 600;

int main(){
    sf::RenderWindow window(sf::VideoMode(WIDTH, HEIGHT), "Pokemon");
    sf::Event event;

    sf::Font font;
    font.loadFromFile("arial.ttf");

    sf::Text start("Hello! What?", font, 300);
    start.setPosition(100, 100);

    while(window.isOpen()){
        window.setFramerateLimit(30);

        while (window.pollEvent(event)){
            if (event.type == sf::Event::Closed){
                window.close();
            }
        }

        window.clear(sf::Color::Blue);

        window.draw(start);
        window.display();
    }

    return EXIT_SUCCESS;
}