分享个 Sqlalchemy 的自定义字段类型, Mutation Tracking 问题

2018-11-09 18:28:41 +08:00
 fanhaipeng0403

JsonEncodeDict-demo-1.py

import json

# https://segmentfault.com/a/1190000004288061
from sqlalchemy import Column, Integer, String
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.ext.mutable import Mutable
from sqlalchemy.orm import sessionmaker
from sqlalchemy.types import TypeDecorator, VARCHAR

# ***************************
engine = create_engine('sqlite:///./cnblogblog.db', echo=False)
Base = declarative_base()
DBSession = sessionmaker(bind=engine)


class JSONEncodedDict(TypeDecorator):
    "Represents an immutable structure as a json-encoded string."

    impl = VARCHAR

    def process_bind_param(self, value, dialect):
        if value is not None:
            value = json.dumps(value)
        return value

    def process_result_value(self, value, dialect):
        if value is not None:
            value = json.loads(value)
        return value


class MutableDict(Mutable, dict):
    @classmethod
    def coerce(cls, key, value):
        "Convert plain dictionaries to MutableDict."

        if not isinstance(value, MutableDict):
            if isinstance(value, dict):
                return MutableDict(value)

            # this call will raise ValueError
            return Mutable.coerce(key, value)
        else:
            return value

    def __setitem__(self, key, value):
        "Detect dictionary set events and emit change events."

        dict.__setitem__(self, key, value)
        self.changed()

    def __delitem__(self, key):
        "Detect dictionary del events and emit change events."

        dict.__delitem__(self, key)
        self.changed()


class MyDataClass1(Base):
    __tablename__ = 'my_data1'
    id = Column(Integer, primary_key=True)
    data = Column(MutableDict.as_mutable(JSONEncodedDict))
    name = Column(String(50))




if __name__ == '__main__':

    Base.metadata.create_all(engine)
    session = DBSession()

    m1 = MyDataClass1(data={'value1': 'foo1'}, name='xiaohong')
    session.add(m1)
    session.commit()

    #######session 提交后,data 可以关联到 query

    m1.name = 'xiaolang'
    
    m1.data['value1'] = 'bar'#数据库的值,将再改变
    
    # assert m1 in session.dirty
    session.commit()

    # my_data= session.query(MyDataClass).filter_by(id=1).one()
    # a= my_data.data
    # print (type(a))
    # print (a)
    # print (a["value1"])
    #
    # my_data.data["value1"] = "foo2"
    #
    # session.commit()

JsonEncodeDict-demo-2.py

import json

from sqlalchemy import Column, Integer, String
# https://segmentfault.com/a/1190000004288061
from sqlalchemy import create_engine
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker
from sqlalchemy.types import TypeDecorator, VARCHAR

# ***************************
engine = create_engine('sqlite:///./cnblogblog.db', echo=False)
Base = declarative_base()
DBSession = sessionmaker(bind=engine)


class JSONEncodedDict(TypeDecorator):
    "Represents an immutable structure as a json-encoded string."

    impl = VARCHAR

    def process_bind_param(self, value, dialect):
        if value is not None:
            value = json.dumps(value)
        return value

    def process_result_value(self, value, dialect):
        if value is not None:
            value = json.loads(value)
        return value


#
# class MutableDict(Mutable, dict):
#     @classmethod
#     def coerce(cls, key, value):
#         "Convert plain dictionaries to MutableDict."
#
#         if not isinstance(value, MutableDict):
#             if isinstance(value, dict):
#                 return MutableDict(value)
#
#             # this call will raise ValueError
#             return Mutable.coerce(key, value)
#         else:
#             return value
#
#     def __setitem__(self, key, value):
#         "Detect dictionary set events and emit change events."
#
#         dict.__setitem__(self, key, value)
#         self.changed()
#
#     def __delitem__(self, key):
#         "Detect dictionary del events and emit change events."
#
#         dict.__delitem__(self, key)
#         self.changed()


class MyDataClass2(Base):
    __tablename__ = 'my_data2'
    id = Column(Integer, primary_key=True)
    data = Column(JSONEncodedDict)
    name = Column(String(50))


if __name__ == '__main__':
    Base.metadata.create_all(engine)
    session = DBSession()

    m1 = MyDataClass2(data={'value1': 'foo1'}, name='xiaohong')
    session.add(m1)
    session.commit()

    m1.name = 'xiaolang'
    #######session 提交后,name 关联到了 query

    # assert m1 in session.dirty

    #######session 提交后,data 将不再关联到 query(解决方法,按着 JsonEncodeDict-demo-1.py 来)

    m1.data['value1'] = 'bar'#数据库的值,不再改变
    session.commit()
    # assert m1 in session.dirty

    # my_data= session.query(MyDataClass).filter_by(id=1).one()
    # a= my_data.data
    # print (type(a))
    # print (a)
    # print (a["value1"])
    #
    # my_data.data["value1"] = "foo2"
    #
    # session.commit()
    ```
2377 次点击
所在节点    Python
0 条回复

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/506236

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX