如何使用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列(char 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)
答案 0 :(得分:6)
答案会让你多一点,然后问
所有它的拳头“扩展”hstore能够实际存储JSON,而不仅仅是键值 所以这个结构也没关系:
{"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做出共同的好方法,因为它与烧瓶一起使用,所以我只采用了一个方向的烧瓶,但是我觉得很容易从这里得到这个想法并且做到了其余部分。
如果您的案例是简单的键值存储,那么就不应该另外添加,只需按原样使用即可。 但是如果你想在代码中的某个地方展开JSON,那么只要你使用它就像这样简单,只需包装在函数中
if (value.startswith("{") and value.endswith("}")) or (value.startswith("[") and value.endswith("]")):
value=json.loads(value)
通过扩展FormField
并添加一些javascript来添加/删除字段,也可以为实际的非JSON方式创建动态字段来编辑数据,但这是完全不同的故事,在我的情况下我需要实际的json存储,二十一点和列表:)
答案 1 :(得分:4)
正在使用postgres JSON数据类型。上述解决方案只需稍加修改即可实现。
尝试
'sqlalchemy.dialects.postgresql.json.JSON',
'sqlalchemy.dialects.postgresql.JSON',
'dialects.postgresql.json.JSON',
'dialects.postgresql.JSON'
上述版本无效。
最后,以下更改有效
@converts('JSON')
将类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 = '{}'
答案 2 :(得分: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()
之后更新。