null オブジェクトは直接、データストアに入らない

Python では Null という便利なクラスが提唱されています。

#
# null.py
#
class Null(object):
    """ Null objects always and reliably "do nothing." """
    # optional optimization: ensure only one instance per subclass
    # (essentially just to save memory, no functional difference)
    def __new__(cls, *args, **kwargs):
        if '_inst' not in vars(cls):
            cls._inst = super(Null, cls).__new__(cls, *args, **kwargs)
        return cls._inst
    def __init__(self, *args, **kwargs): pass
    def __call__(self, *args, **kwargs): return self
    def __repr__(self): return "Null( )"
    def __nonzero__(self): return False
    def __getattr__(self, name): return self
    def __setattr__(self, name, value): return self
    def __delattr__(self, name): return self


AppEngine のデータストアは、null オブジェクトを None として扱ってくれません
従って、以下のプログラムでは BadValueError: Unsupported type for property name: が発生するはずです。

class TestModel(db.Model):
    name = db.StringProperty()

from null import Null
def store(data):
    data = data or Null()
    entity = TestModel(name=data.name)
    entity.put()
  • 解決策 1 : or None を使う
def store(data):
    data = data or Null()
    entity = TestModel(name=data.name or None)
    entity.put()
  • 解決策 2 : Property クラスの validator で対処する

StringProperty のコンストラクタ引数で別の validator をセットするやり方もありますが、型チェックは継承したいので、StringProperty を継承したクラスを適当に作って、valudate メソッドをオーバーライドします。

def validate_null(value):
    # 引数 |value| が Null なら None を返す
    if isinstance(value, Null):  # Null -> None
        return None
    return value

class StringProperty(db.StringProperty):
    ''' Null オブジェクトを None として扱う StringProperty クラス '''
    def validate(self, value):
        v = validate_null(value)
        return super(StringProperty, self).validate(v)

class TestModel(db.Model):
    name = NullStringProperty()

from null import Null
def store(data):
    data = data or Null()
    entity = TestModel(name=data.name)
    entity.put()

validate(value)
プロパティ用の完全な検証ルーチン。value が有効な場合、そのままの値、または必要な型に適合させた値を返します。それ以外の場合、対応する例外を返します。

  • 注)ListProperty の validate() は None でなく、空のリスト [] を返すようにする。
class ListProperty(db.ListProperty):
    def validate(self, value):
        v = value if value else []
        return super(ListProperty, self).validate(v)