V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
waterlaw
V2EX  ›  Python

postgresql 9.3.5 数据库存储 字典型数据遇到的一个问题

  •  
  •   waterlaw ·
    water-law · 2017-12-19 19:27:44 +08:00 · 1933 次点击
    这是一个创建于 2526 天前的主题,其中的信息可能已经有所发展或是发生改变。

    本人是 python 初级程序员, 今天在公司测试服务器遇到一个数据库存储的问题, 原本存储在数据库中的字段值 '{"first": ["", ""], "second": ["", ""]}' 变成了 '{{first, "", ""}, {second, "", ""}}',first 没有引号!!!, 而且突然有了 {{ 和 }} 是什么意思? 这里主要设计 django 的两个 model, 定义如下:

    class A(models.Model):
    	params = JSONField(verbose_name="A",default=dict)
    
    class B(models.Model):
    	template = models.ForeignKey(A, verbose_name='B')
    

    存储 A 中一条记录的语句:

    d = dict()
    d["first"] = ["", ""]
    d["second"] = ["", ""]
    A.objects.create(params=d)
    

    我在使用 A 的增删改查操作时 数据库记录都是正确的, 但是服务器第二天就报错了, 初步排查, 代码中只有一处对 A 表进行操作, 应该不是存储格式错误的原因,在服务器中使用 psql 查询 A 表时发现存储的数据 由原来的 {"first": ["", ""], "second": ["", ""]} 变成 {{first, "", ""}, {second, "", ""}},这是 postgresql 数据库的一个 bug 吗? 因为 9.3 版本不支持 jsonb, 所以自定义了一个 JSONField , 定义如下:

    class JSONField(Field):
        empty_strings_allowed = True
        default_error_messages = {
            'invalid': "Value must be valid JSON.",
        }
        description = 'A JSON object'
        _encoder_class = {
            "indent": None,
            "sort_keys": True,
            "separators": (',', ':'),
            "skipkeys": True,
        }
        _decoder_kwargs = {
            "object_hook": hook_handler
        }
    
        def __init__(self, *args, **kwargs):
            if not kwargs.get('null', False):
                kwargs['default'] = kwargs.get('default', dict)
            self.encoder_kwargs = dict(
                kwargs.pop(
                    'encoder_kwargs', self._encoder_class))
    
            self.decoder_kwargs = dict(
                kwargs.pop(
                    'decoder_kwargs', self._decoder_kwargs))
            super(JSONField, self).__init__(*args, **kwargs)
    
        def formfield(self, **kwargs):
            defaults = {
                'form_class': JSONFormField,
                'widget': JSONWidget,
            }
            defaults.update(**kwargs)
            return super(JSONField, self).formfield(**defaults)
    
        def validate(self, value, model_instance):
            super(JSONField, self).validate(value, model_instance)
            try:
                json.dumps(value)
            except TypeError:
                raise ValidationError(
                    self.error_messages['invalid'],
                    code='invalid',
                    params={'value': value},
                )
    
        def get_internal_type(self):
            return 'TextField'
    
        def db_type(self, connection):
            # if connection.vendor == 'postgresql' and connection.pg_version >= 90400:
            #     raise ValueError("使用 django.contrib.postgres.fields.JSONField")
            return 'text'
    
        def from_db_value(self, value, expression, connection, context):
            if value is None:
                return None
            return json.loads(value, **self.decoder_kwargs)
    
        def get_prep_value(self, value):
            if value is None:
                if self.null and self.blank:
                    return None
                value =  ""
            return json.dumps(value, **self.encoder_kwargs)
    
        def value_to_string(self, obj):
            return self.value_from_object(obj)
    
    siteshen
        1
    siteshen  
       2017-12-19 22:43:17 +08:00
    看起来像是函数 `get_prep_value` 的问题,不知道你的 JSONField 怎么写出来的。
    我的话,会在 Django 自带的 JSONField 上调整,楼主可以试试。

    ```
    from django.contrib.postgres.fields.jsonb import JSONField as JSONBField

    class JSONField(JSONBField):
    def db_type(self, connection):
    return 'json'
    ```
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   3398 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 20ms · UTC 12:01 · PVG 20:01 · LAX 04:01 · JFK 07:01
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.