2013-07-01 18 views
3

所有,SQLAlchemy的 - 的onupdate不會覆蓋

我工作的一個審計混入了SQLAlchemy的和不知道到底如何做到這一點的電流值。我的班級看起來是這樣的:

class AuditColumns(object): 

    created_dt = Column(DateTime, 
         default=datetime.utcnow(), 
         nullable=False) 

    created_by = Column(String(64), 
         default=current_user, 
         nullable=False) 

    updated_dt = Column(DateTime, 
         default=datetime.utcnow(), 
         nullable=False, 
         onupdate=datetime.utcnow()) 
    updated_by = Column(String(64), 
         default=current_user, 
         nullable=False, 
         onupdate=current_user) 

更新的是好的,因爲我只需要在表級別記錄最新的更新;任何重要的審計將在一個單獨的表中詳細更新/刪除等。

我的問題是;我不希望更新created_dt/by列。我知道,在我的代碼中,我可以在更新對象時簡單地忽略它們;但另一個編碼者可能;所以我確實希望在每次更新之前確保它會覆蓋自身的值,或者如果有人試圖更改它(如果有人希望更改後者),則會引發錯誤。

我的SQLAlchemy技能還在開發中,會不會是事件發生的地方;或者有什麼可以通過覆蓋一些通用的聲明性函數,如save()或before_save()或任何可能存在的東西來完成?

我會繼續尋找答案 - 但幫助找到解決方案(我不希望被授予代碼)是可取的。

回答

4

[編輯] 我正在使用flask.g--但我意識到它不是持久性的,除非硬編碼到某個地方;因此我已經轉移到我的真實實施中的用戶會話

確定球員...希望這會幫助某人。我想我已經整理出來的解決方案,測試和足夠安全爲我的項目(儘管它並不完美,我很想一些反饋):

這是審計的Mixin:

from datetime import datetime 
from flask import g 
from sqlalchemy import Column, DateTime, String 
from sqlalchemy.orm import MapperExtension 


class AuditColumns(object): 

    created_dt = Column(DateTime, 
         default=datetime.utcnow(), 
         nullable=False) 

    created_by = Column(String(64), 
         nullable=False) 

    updated_dt = Column(DateTime, 
         default=datetime.utcnow(), 
         nullable=False, 
         onupdate=datetime.utcnow()) 

    updated_by = Column(String(64), 
         nullable=False) 


class AuditExtension(MapperExtension): 

    def before_insert(self, mapper, connection, instance): 
     """ Make sure the audit fields are set correctly """ 
     instance.created_dt = datetime.utcnow() 
     instance.created_by = g.username 

     instance.updated_dt = datetime.utcnow() 
     instance.updated_by = g.username 

    def before_update(self, mapper, connection, instance): 
     """ Make sure when we update this record the created fields stay unchanged! """ 
     instance.created_dt = instance.created_dt 
     instance.created_by = instance.created_by 

     instance.updated_dt = datetime.utcnow() 
     instance.updated_by = g.username 

然後我簡單地釘的延伸和鹼基延伸到任何車型需要他們:

class Roles(db.Model, AuditColumns): 

    id = Column(BigInteger, primary_key=True) 
    username = Column(String(64), nullable=False, unique=True) 
    password = Column(String(255), nullable=False) 

    __mapper_args__ = { 
     'extension': AuditExtension()} 

    def __repr__(self): 
     return self.username 

現在,有2個告誡我發現這種方法: - g.username被硬編碼 - 在這個階段,我不知道我如何通過額外的參數在Mapper中使用SQLAlchemy,所以現在;這將不得不做。 - 數據庫級別的操作仍然可以完成...在數據庫上運行原始SQL不會阻止更新這些列。

這些警告中的第二個是有問題的 - 但我認爲定義模型級觸發器可能有助於防止任何db交互式小提琴。

可能是另一種方法的任何想法?我想我已經鎖定任何基於SQLalchemy的小提琴...

2

你的問題是你沒有使用可調用的「默認」和「onupdate」。 It's documented here

的日期應該是(注意不存在括號的):

default=datetime.datetime.utcnow 
onupdate=datetime.datetime.utcnow 

或用戶名:

default=lambda: current_user.username 

後者的例子應該是一個函數,而不是一個拉姆達,進行各種對current_user進行安全檢查(例如,如果匿名呢?)