[storm] subclassing and foreign keys with respect to properties from parent

Jamu Kakar jkakar at kakar.ca
Wed Aug 13 22:26:55 BST 2008


Hi,

Olaf Conradi wrote:
> Ok, so instead of using subclassing I'd be better of using separate classes?

That's what we do in Landscape.

> For example, I'm storing credentials for subjects, and each subject
> can either be an agent or a person. (I left out the credential part
> here)
> 
> class Subject(Storm):
>     __storm_table__ = 'subjects'
>     id = Int(primary=True)
>     name = Unicode()
>     type = Enum(map={'agent': 0, 'person': 1})
>     type_id = Int()
> 
> 
> class Agent(Storm):
>     __storm_table__ = 'agents'
>     id = Int(primary=True)
>     subject_id = Int()
> 
>     subject = Reference(subject_id, 'Subject.id')
> 
> 
> class Person(Storm):
>     __storm_table__ = 'persons'
>     id = Int(primary=True)
>     subject_id = Int()
>     gender = Enum(map={'Undisclosed': 0, 'Male': 1, 'Female': 2})
>     date_of_birth = Date()
> 
>     subject = Reference(subject_id, 'Subject.id')
> 
> 
> What would be the best way to add a property to class Subject to get
> the real subject depending on the type?

We use a very similar pattern in Landscape.  What you have is almost
the same.  We use a registry to accomplish what you want (untested
code follows):

subject_info_type = {}

def register_subject_info_type(type_info, type_class):
     existing_info_class = subject_info_types.get(info_type)
     if existing_info_class is not None:
         raise RuntimeError("%r has the same info_type of %r" %
                            (info_class, existing_info_class))
     subject_info_types[info_type] = info_class
     subject_class.info_type = info_type

Somewhere in your application, before any of these things are used
you need to register subjects info classes, being careful to use
stable info type values since they'll be stored in the database:

register_subject_info_type(1, Agent)
register_subject_info_type(2, Person)

Add a constructor and an info property to your Subject class:

class Subject(Storm):

     __storm_table__ = "subjects"

     id = Int(primary=True)
     name = Unicode()
     info_type = Int(allow_none=False)
     _info = None

     def __init__(self, subject_info_type, **kwds):
         if subject_info_type not in subject_info_types.values():
             raise UnregisteredSubjectError(
                 "%s is not a registered subject info type."
                  % (subject_info_type,))
         self.info_type = subject_info_type.get_info_type()
         self._info = subject_info_type(self, **kwds)

     @property
     def info(self):
         if self._info is not None:
             return self._info
         assert self.id is not None
         type = subject_info_types[self.info_type]
         if not hasattr(type, "__storm_table__"):
             info = type.__new__(type)
             info.subject = self
         else:
             info = Store.of(self).get(type, self.id)
         self._info = info
         return info

Note that the check for a __storm_table__ attribute means you can
use in-memory info classes in the same system.  Now subjects are
created as follows:

agent = Subject(Agent, name="Max Smart", secret_code=123)
person = Subject(Person, name="Jane Doe", gender="Female")

Hope this helps,
J.



More information about the storm mailing list