On 08/17/2010 11:32 AM, yota wrote:
> Hello,
>
> sqlalchemy seems to be the proper tool for my needs but I can't figure
> out how to design my project or set the ORM properly.
>
> Let's say, I build a music database, storing tracks and their
> associated metadata in an sql-like database defined as such :
>
> TRACK_TABLE ( ident *, url , duration )
> METADATA_TABLE ( track_ident *, field_name *, field_content )
>
> (track_ident, field_name) being the primary key for METADATA_TABLE...
>
>
> intuitively, a meaningful object for a metadata set would be a
> dictionary-like object. The name and number of fields being unknown in
> advance but stored in the METADATA_TABLE as one row per field.
>
> Here is the question : how may I use the ORM to map
> a table :
> 123 / title / waka waka
> 123 / artist / shakira
> 123 / featuring / my sister
>
> to an object like :
> metadata.title = "waka waka"
> metadata.artist = "shakira"
> metadata.featuring = "my sister"
>
> or a dictionary like :
> metadata = { "title" : "waka waka", "artist" : "shakira",
> "featuring" : "my sister" }
>
> or whatsoever ...
>
> the solution might also be in rewrite of the sql schema :)
>
>
> thanks for your advices
>   

SQLAlchemy supports for this via the attribute_mapped_collection and
association_proxy classes:

from sqlalchemy.ext.associationproxy import association_proxy
from sqlalchemy.orm.collections import attribute_mapped_collection

def create_metadata(field_name, field_content):
    return TrackMetaData(field_name=field_name, field_content=field_content)

class Track(Base):
    __tablename__ = "track"

    id = Column(Integer, primary_key=True)
    [...]

    field2metadata = relationship(TrackMetaData,
                                  backref="track",
                                  
collection_class=attribute_mapped_collection("field_name"))
    field2content = association_proxy("field2metadata",
                                      "field_content",
                                      creator=create_metadata)

# I'm only naming this class TrackMetaData to prevent confusion with 
sqlalchemy.MetaData.
class TrackMetaData(Base):
    __tablename__ = "metadata"

    track_id = Column(Integer, ForeignKey("track.id"), primary_key=True)
    field_name = Column(Unicode(...), primary_key=True)
    field_content = Column(Unicode(...), nullable=False)

In this way you can access field2content like a dictionary:

artist = track.field2content[u"artist"]
track.field2content[u"title"] = u"waka waka"

Note that association_proxy does not supply a comparator yet, so if you
want to join/query on metadata then you need to use the field2metadata
relationship:

# Find all tracks by artist Shakira.
q = Session.query(Track)
q = q.filter(Track.field2metadata.any(and_(TrackMetaData.field_name == 
u"artist",
                                           TrackMetaData.field_content = 
u"Shakira")))
tracks = q.all()

# Load all tracks, eagerloading their metadata.
q = Session.query(Track)
q = q.options(joinedload(Track.field2metadata))
tracks = q.all()

-Conor

-- 
You received this message because you are subscribed to the Google Groups 
"sqlalchemy" group.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to 
[email protected].
For more options, visit this group at 
http://groups.google.com/group/sqlalchemy?hl=en.

Reply via email to