client side column default value processing for special values e.g. JSON.NULL

Issue #3870 resolved
Adrian created an issue

Is this behavior intentional? If I set the default value of a column to be a JSON null, which should be represented by a Python None, I'd expect a new object to have that value after flushing. This is especially an issue when using the value e.g. a a dict key since it obviously differs from an actual None.

from __future__ import print_function
from sqlalchemy import *
from sqlalchemy.orm import *
from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()


class Test(Base):
    __tablename__ = 'test'

    id = Column(Integer, primary_key=True)
    data = Column(JSON, nullable=False, default=JSON.NULL)

    def __repr__(self):
        return '<Test({})>'.format(self.id)


e = create_engine('postgresql:///test')
Base.metadata.create_all(e)
s = Session(e)


t = Test()
s.add(t)
print('data before flush', repr(t.data), type(t.data))
s.flush()
print('data after flush', repr(t.data), type(t.data))
foo = {None: 'bar'}
print(foo[None])
print(foo[t.data])
data before flush None <type 'NoneType'>
data after flush symbol('JSON_NULL') <class 'sqlalchemy.util.langhelpers.symbol'>
bar
Traceback (most recent call last):
  File "satest2.py", line 31, in <module>
    print(foo[t.data])
KeyError: symbol('JSON_NULL')

Comments (3)

  1. Mike Bayer repo owner

    I don't even know what to do with that. You set the default as JSON.NULL, so it's potentially ambiguous what someone would want to see as the "value", None is probably the right answer but I can see it going badly either way. Not really sure. It would imply that type deserializers have to run for some client side default values which implies a total crapshow of new plumbing in Core.

    Here's how to make it work right now, please feel free to suggest where this might fit in the documentation (e.g. where you would have found it):

    data = Column(JSON, nullable=False, default=lambda: None)
    
  2. Mike Bayer repo owner

    I put the wrong issue number; here's the changelog:

    4d4ea792e7ea7945de9c9b1e005ce09b2feb45b5 Author: Mike Bayer mike_mp@zzzcomputing.com Date: Thu May 18 17:44:10 2017 -0400

    - Document the most transparent way to set JSON.NULL for
    a column default.
    
    The JSON.NULL value is special in that while it is a Python-side
    value, it represents "do this special behavior", and is not at all
    like any other Python-side value for which normally, that's exactly
    the type of data we want back.   So in this case, encourage the user
    to use a SQL expression that is exact, so in the ORM context, you get
    back what was actually persisted in the column.  There's some variants
    of this such as literal(JSON.NULL, JSON) but text("'null'") is simpler.
    
    Change-Id: I0339fafdc03e4b2f533d53970b2f74c774f2687b
    Fixes: #3870
    
  3. Log in to comment