2009-04-23 7 views
15

J'ai une question concernant le SQLAlchemy. Comment puis-je ajouter dans ma classe mappée l'attribut de type dictionnaire, qui mappe les clés de chaîne en valeurs de chaîne et qui seront stockées dans la base de données (dans la même table ou dans une autre table que l'objet mappé original). Je veux que cela ajoute le support pour les balises arbitraires de mes objets.SQLAlchemy - Dictionnaire de tags

Je trouve l'exemple suivant dans la documentation SQLAlchemy:

from sqlalchemy.orm.collections import column_mapped_collection, attribute_mapped_collection, mapped_collection 

mapper(Item, items_table, properties={ 
# key by column 
'notes': relation(Note, collection_class=column_mapped_collection(notes_table.c.keyword)), 
# or named attribute 
'notes2': relation(Note, collection_class=attribute_mapped_collection('keyword')), 
# or any callable 
'notes3': relation(Note, collection_class=mapped_collection(lambda entity: entity.a + entity.b)) 
}) 

item = Item() 
item.notes['color'] = Note('color', 'blue') 

Mais je veux le comportement suivant:

mapper(Item, items_table, properties={ 
# key by column 
'notes': relation(...), 
}) 

item = Item() 
item.notes['color'] = 'blue' 

Il est possible SQLAlchemy?

Merci

Répondre

21

La réponse simple est oui.

utiliser Juste un proxy d'association:

from sqlalchemy import Column, Integer, String, Table, create_engine 
from sqlalchemy import orm, MetaData, Column, ForeignKey 
from sqlalchemy.orm import relation, mapper, sessionmaker 
from sqlalchemy.orm.collections import column_mapped_collection 
from sqlalchemy.ext.associationproxy import association_proxy 

Créer un environnement de test:

engine = create_engine('sqlite:///:memory:', echo=True) 
meta = MetaData(bind=engine) 

Définir les tables:

tb_items = Table('items', meta, 
     Column('id', Integer, primary_key=True), 
     Column('name', String(20)), 
     Column('description', String(100)), 
    ) 
tb_notes = Table('notes', meta, 
     Column('id_item', Integer, ForeignKey('items.id'), primary_key=True), 
     Column('name', String(20), primary_key=True), 
     Column('value', String(100)), 
    ) 
meta.create_all() 

classes (notez le association_proxy dans la classe):

class Note(object): 
    def __init__(self, name, value): 
     self.name = name 
     self.value = value 
class Item(object): 
    def __init__(self, name, description=''): 
     self.name = name 
     self.description = description 
    notes = association_proxy('_notesdict', 'value', creator=Note) 

Cartographie:

mapper(Note, tb_notes) 
mapper(Item, tb_items, properties={ 
     '_notesdict': relation(Note, 
      collection_class=column_mapped_collection(tb_notes.c.name)), 
    }) 

Puis juste le tester:

Session = sessionmaker(bind=engine) 
s = Session() 

i = Item('ball', 'A round full ball') 
i.notes['color'] = 'orange' 
i.notes['size'] = 'big' 
i.notes['data'] = 'none' 

s.add(i) 
s.commit() 
print i.notes 

qui imprime:

{u'color': u'orange', u'data': u'none', u'size': u'big'} 

Mais, sont ceux dans le tableau des notes?

>>> print list(tb_notes.select().execute()) 
[(1, u'color', u'orange'), (1, u'data', u'none'), (1, u'size', u'big')] 

Cela fonctionne !! :)

+0

Comment pouvez-vous supprimer 'i' alors?Je reçois une '' aise AssertionError ("La règle de dépendance essaye de vider la colonne de clé primaire '% s' sur l'instance '% s'" (r, mapperutil.state_str (dest))) 'exception en essayant' s.delete (i) ' – Sardathrion

+0

Répondre à ma propre question:' Dans mapper (item [...], cascade = "all, supprimer-orphelin"), [...] ' – Sardathrion

-6

La réponse simple est 'non'. SQLAlchemy est l'encapsuleur d'une base de données SQL.

Les exemples de relation que vous citez traduisent une relation entre des tables SQL dans une structure de type Python pour simplifier un peu les instructions SQL SELECT et localiser des lignes dans une autre table.

Le

item.notes['color'] = Note('color', 'blue') 

est essentiel parce que le Note est une table séparée avec deux colonnes. Vous ne pouvez pas laisser la pièce Note

Vous devez définir cette autre table SQL et vous devez créer des objets mappés à cette table SQL.