Sto memorizzando JSON come blob/testo in una colonna utilizzando MySQL. C'è un modo semplice per convertire questo in un dict usando python/SQLAlchemy?SQLAlchemy JSON come blob/testo
risposta
si può facilmente create your own type con SQLAlchemy
Per le versioni SQLAlchemy> = 0,7, controlla Yogesh's answer seguito
import jsonpickle
import sqlalchemy.types as types
class JsonType(types.MutableType, types.TypeDecorator):
impl = types.Unicode
def process_bind_param(self, value, engine):
return unicode(jsonpickle.encode(value))
def process_result_value(self, value, engine):
if value:
return jsonpickle.decode(value)
else:
# default can also be a list
return {}
Questo può essere utilizzato quando si sta definendo le tue tabelle (esempio usa elisir):
from elixir import *
class MyTable(Entity):
using_options(tablename='my_table')
foo = Field(String, primary_key=True)
content = Field(JsonType())
active = Field(Boolean, default=True)
È anche possibile utilizzare un serializzatore json diverso per jsonpickle.
Che ne dici di json.loads()?
>>> d= {"foo":1, "bar":[2,3]}
>>> s='{"foo":1, "bar":[2,3]}'
>>> import json
>>> json.loads(s) == d
True
grazie, c'è un modo per farlo automaticamente? simile a un trigger in sqlalchemy. – Timmy
ritengo l'esempio JSON dalla documentazione SQLAlchemy anche la pena menzionare:
http://www.sqlalchemy.org/docs/core/types.html#marshal-json-strings
Ritengo tuttavia può essere migliorato per essere meno severe per quanto riguarda NULL e le stringhe vuote:
class JSONEncodedDict(TypeDecorator):
impl = VARCHAR
def process_bind_param(self, value, dialect):
if value is None:
return None
return json.dumps(value, use_decimal=True)
def process_result_value(self, value, dialect):
if not value:
return None
return json.loads(value, use_decimal=True)
Questa risposta ha funzionato senza toccare types.py. –
NB: Funzionerà solo se si considera il valore come immutabile. Quindi, assegni all'attributo oggetto un pieno 'dict'. Se si tenta di modificare solo gli elementi del 'dict', sqlalchemy non registrerà le modifiche e non verranno salvate in flush. Vedi 'sqlalchemy.ext.mutable.Mutable' su come cambiarlo. –
Questo è quello che mi è venuto in mente in base alle due risposte sopra.
import json
class JsonType(types.TypeDecorator):
impl = types.Unicode
def process_bind_param(self, value, dialect):
if value :
return unicode(json.dumps(value))
else:
return {}
def process_result_value(self, value, dialect):
if value:
return json.loads(value)
else:
return {}
Si è verificato un problema con il salvataggio risolto utilizzando un dizionario mutabile. http://docs.sqlalchemy.org/en/rel_0_8/orm/extensions/mutable.html –
sqlalchemy.types.MutableType
è sconsigliata (v0.7 in poi), il documentation recommends utilizzando sqlalchemy.ext.mutable
invece.
Ho trovato un Git gist di dbarnett che ho testato per il mio utilizzo. Ha funzionato bene finora, sia per il dizionario che per gli elenchi.
incolla di seguito per i posteri:
import simplejson
import sqlalchemy
from sqlalchemy import String
from sqlalchemy.ext.mutable import Mutable
class JSONEncodedObj(sqlalchemy.types.TypeDecorator):
"""Represents an immutable structure as a json-encoded string."""
impl = String
def process_bind_param(self, value, dialect):
if value is not None:
value = simplejson.dumps(value)
return value
def process_result_value(self, value, dialect):
if value is not None:
value = simplejson.loads(value)
return value
class MutationObj(Mutable):
@classmethod
def coerce(cls, key, value):
if isinstance(value, dict) and not isinstance(value, MutationDict):
return MutationDict.coerce(key, value)
if isinstance(value, list) and not isinstance(value, MutationList):
return MutationList.coerce(key, value)
return value
@classmethod
def _listen_on_attribute(cls, attribute, coerce, parent_cls):
key = attribute.key
if parent_cls is not attribute.class_:
return
# rely on "propagate" here
parent_cls = attribute.class_
def load(state, *args):
val = state.dict.get(key, None)
if coerce:
val = cls.coerce(key, val)
state.dict[key] = val
if isinstance(val, cls):
val._parents[state.obj()] = key
def set(target, value, oldvalue, initiator):
if not isinstance(value, cls):
value = cls.coerce(key, value)
if isinstance(value, cls):
value._parents[target.obj()] = key
if isinstance(oldvalue, cls):
oldvalue._parents.pop(target.obj(), None)
return value
def pickle(state, state_dict):
val = state.dict.get(key, None)
if isinstance(val, cls):
if 'ext.mutable.values' not in state_dict:
state_dict['ext.mutable.values'] = []
state_dict['ext.mutable.values'].append(val)
def unpickle(state, state_dict):
if 'ext.mutable.values' in state_dict:
for val in state_dict['ext.mutable.values']:
val._parents[state.obj()] = key
sqlalchemy.event.listen(parent_cls, 'load', load, raw=True, propagate=True)
sqlalchemy.event.listen(parent_cls, 'refresh', load, raw=True, propagate=True)
sqlalchemy.event.listen(attribute, 'set', set, raw=True, retval=True, propagate=True)
sqlalchemy.event.listen(parent_cls, 'pickle', pickle, raw=True, propagate=True)
sqlalchemy.event.listen(parent_cls, 'unpickle', unpickle, raw=True, propagate=True)
class MutationDict(MutationObj, dict):
@classmethod
def coerce(cls, key, value):
"""Convert plain dictionary to MutationDict"""
self = MutationDict((k,MutationObj.coerce(key,v)) for (k,v) in value.items())
self._key = key
return self
def __setitem__(self, key, value):
dict.__setitem__(self, key, MutationObj.coerce(self._key, value))
self.changed()
def __delitem__(self, key):
dict.__delitem__(self, key)
self.changed()
class MutationList(MutationObj, list):
@classmethod
def coerce(cls, key, value):
"""Convert plain list to MutationList"""
self = MutationList((MutationObj.coerce(key, v) for v in value))
self._key = key
return self
def __setitem__(self, idx, value):
list.__setitem__(self, idx, MutationObj.coerce(self._key, value))
self.changed()
def __setslice__(self, start, stop, values):
list.__setslice__(self, start, stop, (MutationObj.coerce(self._key, v) for v in values))
self.changed()
def __delitem__(self, idx):
list.__delitem__(self, idx)
self.changed()
def __delslice__(self, start, stop):
list.__delslice__(self, start, stop)
self.changed()
def append(self, value):
list.append(self, MutationObj.coerce(self._key, value))
self.changed()
def insert(self, idx, value):
list.insert(self, idx, MutationObj.coerce(self._key, value))
self.changed()
def extend(self, values):
list.extend(self, (MutationObj.coerce(self._key, v) for v in values))
self.changed()
def pop(self, *args, **kw):
value = list.pop(self, *args, **kw)
self.changed()
return value
def remove(self, value):
list.remove(self, value)
self.changed()
def JSONAlchemy(sqltype):
"""A type to encode/decode JSON on the fly
sqltype is the string type for the underlying DB column.
You can use it like:
Column(JSONAlchemy(Text(600)))
"""
class _JSONEncodedObj(JSONEncodedObj):
impl = sqltype
return MutationObj.as_mutable(_JSONEncodedObj)
in base alla risposta @snapshoe e di rispondere @ commento di Timmy:
È possibile farlo utilizzando le proprietà. Ecco un esempio di una tabella:
class Providers(Base):
__tablename__ = "providers"
id = Column(
Integer,
Sequence('providers_id', optional=True),
primary_key=True
)
name = Column(Unicode(40), index=True)
_config = Column("config", Unicode(2048))
@property
def config(self):
if not self._config:
return {}
return json.loads(self._config)
@config.setter
def config(self, value):
self._config = json.dumps(value)
def set_config(self, field, value):
config = self.config
config[field] = value
self.config = config
def get_config(self):
if not self._config:
return {}
return json.loads(self._config)
def unset_config(self, field):
config = self.get_config()
if field in config:
del config[field]
self.config = config
Ora è possibile utilizzarlo su un oggetto Providers()
:
>>> p = Providers()
>>> p.set_config("foo", "bar")
>>> p.get_config()
{"foo": "bar"}
>>> a.config
{u'foo': u'bar'}
So che questa è una vecchia questione forse anche morto, ma spero che questo potrebbe aiutare qualcuno .
C'è una ricetta per questo nella official documentation:
from sqlalchemy.types import TypeDecorator, VARCHAR
import json
class JSONEncodedDict(TypeDecorator):
"""Represents an immutable structure as a json-encoded string.
Usage::
JSONEncodedDict(255)
"""
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
come aggiornamento per le risposte precedenti, che abbiamo usato con successo finora. A partire da MySQL 5.7 e SQLAlchemy 1.1 è possibile utilizzare native MySQL JSON data type, che offre prestazioni migliori e un'intero range of operators gratuitamente.
Consente di creare anche virtual secondary indexes su elementi JSON.
Ma ovviamente ti bloccherai a eseguire l'app su MySQL solo quando sposti la logica nel database stesso.
- 1. SqlAlchemy: interrogazione del campo json di lunghezza con un array
- 2. Selezionare come in sqlalchemy
- 3. SQLAlchemy + Tornado: come creare un scopefunc per ScopedSession di SQLAlchemy?
- 4. Come usare flask-sqlalchemy con il modello sqlalchemy esistente?
- 5. Flask-Sqlalchemy + Sqlalchemy: elenco vuoto restituibile ricercabile
- 6. Come rendere SQLAlchemy utilizzare Memcached?
- 7. SQLAlchemy consentire null come ForeignKey
- 8. Come creare ENUM in SQLAlchemy?
- 9. Come integrare Redis con SQLAlchemy
- 10. Devo utilizzare SQLObject, SQLAlchemy o SQLAlchemy + Elixir?
- 11. SQLAlchemy & PassLib
- 12. SQLAlchemy non __init__ esecuzione
- 13. Ereditarietà SQLAlchemy
- 14. in SQLAlchemy
- 15. SQLAlchemy colonna sinonimo con diversi tipi
- 16. sqlalchemy filter multiple columns
- 17. stored procedure con sqlAlchemy
- 18. Utilizzo di sqlalchemy per creare un indice su una chiave json (indice di espressione)
- 19. SQLAlchemy INSERT IGNORE
- 20. SQLAlchemy 0.5.8 Funzione Max
- 21. SQLAlchemy + PostgreSQL + PG regex
- 22. come creare dinamicamente le colonne SQLAlchemy
- 23. come contare SQLAlchemy query in unit test
- 24. Come utilizzare "ordine composto da" in sqlalchemy
- 25. Come posso utilizzare gli UUID in SQLAlchemy?
- 26. Come generare questa query in sqlalchemy?
- 27. Come posso profilare un'applicazione alimentata con SQLAlchemy?
- 28. Come si modifica il database di SQLAlchemy?
- 29. Come utilizzare intervallo numerico postgres con SQLAlchemy
- 30. Come filtrare per tabella joinloaded in SqlAlchemy?
Questo non ha funzionato per me. All'interno della classe MutableType (oggetto): def copy_value genera un'eccezione. def copy_value (self, value): "" "Non implementato." "" raise NotImplementedError() –
Ho cambiato la fonte e ha funzionato, ma non mi sentivo a mio agio per i problemi di manutenzione che ciò avrebbe causato. –
Ottima risposta ... Si noti inoltre che PostgreSQL supporta un tipo JSON. Questo sembra promettente - è come il tuo esempio, ma utilizzerà il tipo JSON PostgreSQL se disponibile. [sqlalchemy-utils JSONType] (http://sqlalchemy-utils.readthedocs.org/en/latest/_modules/sqlalchemy_utils/types/json.html) – hangtwenty