2009-09-04 6 views
21

Je voudrais charger/enregistrer un dict de/vers ma DB sqlite, mais j'ai quelques problèmes pour trouver un moyen simple de le faire. Je n'ai pas vraiment besoin d'être capable de filtrer, etc., en fonction du contenu, donc une simple conversion de/à partir de la chaîne est très bien.Python dicts dans sqlalchemy

La prochaine meilleure chose serait des clés étrangères. S'il vous plait, ne postez pas de liens vers d'énormes exemples, ma tête exploserait si jamais je jetais un œil dessus.

Répondre

4

Vous pouvez créer un type personnalisé par le sous-classement sqlalchemy.types.TypeDecorator pour gérer sérialisation et désérialisation au texte.

Une mise en œuvre pourrait ressembler à

import json 
import sqlalchemy 
from sqlalchemy.types import TypeDecorator 

SIZE = 256 

class TextPickleType(TypeDecorator): 

    impl = sqlalchemy.Text(SIZE) 

    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 

Exemple d'utilisation:

class SomeModel(Base): 
    __tablename__ = 'the_table' 
    id = Column(Integer, primary_key=True) 
    json_field = Column(TextPickleType()) 

s = SomeEntity(json_field={'baked': 'beans', 'spam': 'ham'}) 
session.add(s) 
session.commit() 

Ceci est décrit dans an example in the SQLAlchemy docs, qui montre aussi comment suivre les mutations de ce dictionnaire.

Cette approche devrait fonctionner pour toutes les versions de Python, alors tout simplement en passant json comme la valeur à l'argument pickler de PickleType ne fonctionnera pas correctement, comme AlexGrönholm points out dans son commentaire sur une autre réponse.

+0

Votre réponse n'est pas complète, donc ne peut pas accepter. Il a également des références à "la réponse acceptée" qui détourne toute tentative et changer cet état particulier sur n'importe quelle réponse. Veuillez commencer par une bonne réponse, et (si vous devez) inclure ce que les autres ont mal fait à la fin. –

+0

@ JonasByström assez juste, j'ai supprimé la référence à la réponse actuellement acceptée et étendu l'exemple un peu – karlson

+0

Cela a fonctionné quand je l'ai mis en place. Cependant après avoir ajouté un nouveau champ à mon modèle ('product_count = db.Column (db.Integer)') il me donne l'erreur suivante lors de la migration des modifications vers mysql: 'NameError: name 'TextPickleType' n'est pas défini' – iamlolz

3

Si vous devez mapper un 1-N relation et la carte comme dict plutôt que list, alors lisez Dictionary Based Collections

Mais si vous voulez dire un champ , alors ce que vous pouvez faire pour avoir une Champ DB de type string, qui est mappé à votre objet Python. Mais sur le même objet python, vous fournissez une propriété qui sera une sorte de proxy pour ce champ de chaîne mappé de type dict(). Exemple de code (non testé):

class MyObject(object): 
    # fields (mapped automatically by sqlalchemy using mapper(...) 
    MyFieldAsString = None 

    def _get_MyFieldAsDict(self): 
     if self.MyFieldAsString: 
      return eval(self.MyFieldAsString) 
     else: 
      return {} # be careful with None and empty dict 

    def _set_MyFieldAsDict(self, value): 
     if value: 
      self.MyFieldAsString = str(value) 
     else: 
      self.MyFieldAsString = None 

    MyFieldAsDict = property(_get_MyFieldAsDict, _set_MyFieldAsDict) 
+0

Ahhh! Splendide! :) –

+1

Notez que SQLAlchemy ne vous verra pas modifier le contenu de la dict, de sorte que les modifications que vous apportez à un dict une fois qu'il a été défini ne seront pas conservées. – Simon

48

Le SQLAlchemy PickleType signifie exactement pour cela.

class SomeEntity(Base): 
    __tablename__ = 'some_entity' 
    id = Column(Integer, primary_key=True) 
    attributes = Column(PickleType) 

# Just set the attribute to save it 
s = SomeEntity(attributes={'baked': 'beans', 'spam': 'ham'}) 
session.add(s) 
session.commit() 

# If mutable=True on PickleType (the default) SQLAlchemy automatically 
# notices modifications. 
s.attributes['parrot'] = 'dead' 
session.commit() 

Vous pouvez modifier le mécanisme de sérialisation en changeant le Pickler avec quelque chose d'autre qui a dumps() et loads() méthodes. Le mécanisme de stockage sous-jacent par le sous-classement PickleType et en remplaçant la attritbute de impl:

class TextPickleType(PickleType): 
    impl = Text 

import json 
class SomeOtherEntity(Base): 
    __tablename__ = 'some_other_entity' 
    id = Column(Integer, primary_key=True) 
    attributes = Column(TextPickleType(pickler=json)) 
+0

+1: très bien en effet – van

+0

Lovely! Je vais essayer le premier lundi. –

+0

Je fais probablement quelque chose de vraiment mal quand j'essaie self._properties [k] = v? Je reçois "TypeError: l'objet 'Column' ne supporte pas l'assignation d'objet". –

Questions connexes