Changeset 322

Show
Ignore:
Timestamp:
04/23/08 11:56:12 (6 years ago)
Author:
ged
Message:

- Added two new methods on the base entity: from_dict and to_dict, which can

be used to create (or output) a whole hierarchy of instances from (to) a
simple JSON-like dictionary notation (patch from Paul Johnston,
closes ticket #40).

Location:
elixir/trunk
Files:
1 added
2 modified

Legend:

Unmodified
Added
Removed
  • elixir/trunk/CHANGES

    r319 r322  
    110.5.3 
     2 
     3New features: 
     4- Added two new methods on the base entity: from_dict and to_dict, which can 
     5  be used to create (or output) a whole hierarchy of instances from (to) a 
     6  simple JSON-like dictionary notation (patch from Paul Johnston, 
     7  closes ticket #40). 
    28 
    39Bug fixes: 
  • elixir/trunk/elixir/entity.py

    r321 r322  
    840840 
    841841    def set(self, **kwargs): 
    842         for key, value in kwargs.items(): 
    843             setattr(self, key, value) 
     842        self.from_dict(kwargs) 
     843 
     844    def from_dict(self, data): 
     845        """ 
     846        Update a mapped class with data from a JSON-style nested dict/list 
     847        structure. 
     848        """ 
     849        mapper = sqlalchemy.orm.object_mapper(self) 
     850        session = sqlalchemy.orm.object_session(self) 
     851        pkey = [c for c in mapper.mapped_table.columns if c.primary_key] 
     852 
     853        for col in mapper.mapped_table.c: 
     854            if not col.primary_key and data.has_key(col.name): 
     855                setattr(self, col.name, data[col.name]) 
     856 
     857        for rel in mapper.iterate_properties: 
     858            rname = rel.key 
     859            if isinstance(rel, sqlalchemy.orm.properties.PropertyLoader) \ 
     860                    and data.has_key(rname): 
     861                dbdata = getattr(self, rname) 
     862                if rel.uselist: 
     863                    # Build a lookup dict: {(pk1, pk2): value} 
     864                    lookup = dict([ 
     865                        (tuple([getattr(o, c.name) for c in pkey]), o)  
     866                        for o in dbdata]) 
     867                    for row in data[rname]: 
     868                        # If any primary key columns are missing or None,  
     869                        # create a new object 
     870                        if [1 for c in pkey if not row.get(c.name)]: 
     871                            subobj = rel.mapper.class_() 
     872                            dbdata.append(subobj) 
     873                        else: 
     874                            key = tuple([row[c.name] for c in pkey]) 
     875                            subobj = lookup.pop(key, None) 
     876 
     877                            # If the row isn't found, we must fail the request 
     878                            # in a web scenario, this could be a parameter  
     879                            # tampering attack 
     880                            if not subobj: 
     881                                raise sqlalchemy.exceptions.ArgumentError( 
     882                                        '%s row not found in database: %s' \ 
     883                                        % (rname, repr(row))) 
     884                        subobj.from_dict(row) 
     885 
     886                    # Make sure the object list attribute doesn't contain any 
     887                    # old value (which are not present in the new data). 
     888                    for delobj in lookup.itervalues(): 
     889                        dbdata.remove(delobj) 
     890                        session.delete(delobj) 
     891                else: 
     892                    if data[rname] is None: 
     893                        setattr(self, rname, None) 
     894                    else: 
     895                        if not dbdata: 
     896                            dbdata = rel.mapper.class_() 
     897                            setattr(self, rname, dbdata) 
     898                        dbdata.from_dict(data[rname]) 
     899 
     900    def to_dict(self, deep={}, exclude=[]): 
     901        """Generate a JSON-style nested dict/list structure from an object.""" 
     902        data = dict([(col.name, getattr(self, col.name)) 
     903                     for col in self.table.c if col.name not in exclude]) 
     904        for rname, rdeep in deep.iteritems(): 
     905            dbdata = getattr(self, rname) 
     906            fks = self.mapper.get_property(rname).foreign_keys 
     907            exclude = [c.name for c in fks] 
     908            if isinstance(dbdata, list): 
     909                data[rname] = [o.to_dict(rdeep, exclude) for o in dbdata] 
     910            else: 
     911                data[rname] = dbdata.to_dict(rdeep, exclude) 
     912        return data 
    844913 
    845914    # session methods