【问题标题】:How do we update an HSTORE field with Flask-Admin?我们如何使用 Flask-Admin 更新 HSTORE 字段?
【发布时间】:2013-09-22 17:24:26
【问题描述】:

如何使用 Flask-Admin 更新 HSTORE 字段?

常规的ModelView 不会在编辑视图中显示HSTORE 字段。它什么也没显示。完全没有控制力。在列表视图中,它以 JSON 表示法显示包含数据的列。这对我来说很好。

使用自定义ModelView,我可以将HSTORE 字段更改为TextAreaField。这将在编辑视图中以 JSON 表示法向我显示 HSTORE 字段。但我无法编辑/更新它。在列表视图中,它仍然以 JSON 表示法向我显示对象。在我看来很好。

class MyView(ModelView):
    form_overrides = dict(attributes=fields.TextAreaField)

当我尝试保存/编辑 JSON 时,收到此错误:

sqlalchemy.exc.InternalError
InternalError: (InternalError) Unexpected end of string
LINE 1: UPDATE mytable SET attributes='{}' WHERE mytable.id = ...
                                         ^
 'UPDATE mytable SET attributes=%(attributes)s WHERE mytable.id = %(mytable_id)s' {'attributes': u'{}', 'mytable_id': 14L}

现在——使用代码,我可以将一些东西保存到 HSTORE 字段中:

class MyView(ModelView):
    form_overrides = dict(attributes=fields.TextAreaField)
    def on_model_change(self, form, model, is_created):
        model.attributes = {"a": "1"}
        return

这基本上覆盖了模型并将这个对象放入其中。然后我可以在列表视图和编辑视图中看到该对象。仍然不够好——我想保存/编辑用户输入的对象。

我尝试将表单中的内容解析并保存到 JSON 中并退出。这不起作用:

class MyView(ModelView):
    form_overrides = dict(attributes=fields.TextAreaField)

    def on_model_change(self, form, model, is_created):
        x = form.data['attributes']
        y = json.loads(x)
        model.attributes = y
        return

json.loads(x) 是这样说的:

ValueError ValueError:期望属性名称:第 1 行第 1 列(字符 1)

以下是一些失败的示例输入:

{u's': u'ff'}
{'s':'ff'}

但是,此输入有效:

{}

空白也可以

这是我的 SQL 表:

CREATE TABLE mytable (
    id BIGSERIAL UNIQUE PRIMARY KEY,
    attributes hstore
);

这是我的 SQA 模型:

class MyTable(Base):
    __tablename__ = u'mytable'

    id = Column(BigInteger, primary_key=True)
    attributes = Column(HSTORE)

这是我将视图添加到管理对象的方式

admin.add_view(ModelView(models.MyTable, db.session))

使用自定义模型视图添加视图

admin.add_view(MyView(models.MyTable, db.session))

但我不会同时执行这些视图 -- 我收到蓝图名称冲突错误 -- 单独的问题)

我还尝试使用表单字段转换器。我无法让它真正命中代码。

class MyModelConverter(AdminModelConverter):
    def post_process(self, form_class, info):
        raise Exception('here I am') #but it never hits this
        return form_class

class MyView(ModelView):
    form_overrides = dict(attributes=fields.TextAreaField)

【问题讨论】:

    标签: json postgresql flask hstore flask-admin


    【解决方案1】:

    答案比问的多一点

    首先它“扩展”了 hstore 以便能够实际存储 JSON,而不仅仅是键值 所以这个结构也OK:

    {"key":{"inner_object_key":{"Another_key":"Done!","list":["no","problem"]}}}
    

    所以,首先你的 ModelView 应该使用自定义转换器

    class ExtendedModelView(ModelView):
       model_form_converter=CustomAdminConverter
    

    转换器本身应该知道如何使用hstore方言:

    class CustomAdminConverter(AdminModelConverter):
        @converts('sqlalchemy.dialects.postgresql.hstore.HSTORE')
        def conv_HSTORE(self, field_args, **extra):
            return DictToHstoreField(**field_args)
    

    如您所见,这个使用自定义 WTForms 字段,可双向转换数据:

    class DictToHstoreField(TextAreaField):
        def process_data(self, value):
            if value is None:
                value = {}
            else:
                for key,obj in value.iteritems():
                    if (obj.startswith("{") and obj.endswith("}")) or (obj.startswith("[") and obj.endswith("]")):
                        try:
                            value[key]=json.loads(obj)
                        except:
                            pass #
    
            self.data=json.dumps(value)
    
        def process_formdata(self, valuelist):
            if valuelist:
                self.data = json.loads(valuelist[0])
                for key,obj in self.data.iteritems():
                    if isinstance(obj,dict) or isinstance(obj,list):
                        self.data[key]=json.dumps(obj)
                    if isinstance(obj,int):
                        self.data[key]=str(obj)
    

    最后一步是在应用程序中实际使用这些数据

    我并没有为 SQLalchemy 提供一种通用的好方法,因为它与 flask-restful 一起使用,所以我只在一个方向上采用了 flask-restful,但我认为从这里很容易得到这个想法并做休息。

    如果你的情况是简单的键值存储,所以不需要做任何额外的事情,就按原样使用它。 但是如果你想在代码中的某处解包JSON,只要你使用它就很简单,只需在函数中进行包装

     if (value.startswith("{") and value.endswith("}")) or (value.startswith("[") and value.endswith("]")):
            value=json.loads(value)
    

    也可以通过扩展 FormField 并添加一些用于添加/删除字段的 javascript 来为实际的非 JSON 数据编辑方式创建动态字段,但这是完全不同的故事,在我的情况下,我需要实际的 json 存储,有二十一点和名单:)

    【讨论】:

    【解决方案2】:

    正在处理 postgres JSON 数据类型。上述解决方案在稍作修改后效果很好。

    试过

    'sqlalchemy.dialects.postgresql.json.JSON',
    'sqlalchemy.dialects.postgresql.JSON',
    'dialects.postgresql.json.JSON',
    'dialects.postgresql.JSON'
    

    以上版本无效。

    以下更改终于奏效了

    @converts('JSON')
    

    并将 class DictToHstoreField 更改为以下内容:

    class DictToJSONField(fields.TextAreaField):
        def process_data(self, value):
            if value is None:
                value = {}
    
            self.data = json.dumps(value)
    
        def process_formdata(self, valuelist):
            if valuelist:
                self.data = json.loads(valuelist[0])
            else:
                self.data = '{}'
    

    【讨论】:

      【解决方案3】:

      虽然这可能不是您问题的答案,但默认情况下 SQLAlchemy 的 ORM 不会检测到对 HSTORE 字段值的就地更改。不过还好有解决办法:SQLAlchemy的MutableDict类型:

      from sqlalchemy.ext.mutable import MutableDict
      
      class MyClass(Base):
          __tablename__ = 'mytable'
      
          id = Column(Integer, primary_key=True)
          attributes = Column(MutableDict.as_mutable(HSTORE))
      

      现在,当您就地更改某些内容时:

      my_object.attributes.['some_key'] = 'some value'
      

      hstore 字段将在session.commit() 之后更新。

      【讨论】:

      • 对了,忘了这个也需要:)
      猜你喜欢
      • 2015-03-07
      • 2022-08-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-06-05
      • 2015-09-16
      相关资源
      最近更新 更多