Pyramid traversal: wrap your model with a resource adapter
Saturday 10 March 2012
If you use the python-based Pyramid web application framework, and would like traversal flavoured view lookup, but want clean model classes, this might be the way to go.
Traversal traverses a tree of so-called resources, which often correspond to your models. For example, if you are working on an email client, you may have an Email model, which might be saved in the email table of your database, have a sender and subject property, and a send method.
class Email(object): def __init__(self, subject, from, to, content): self.subject = subject def subject(): return self.subject def send(self): #send emailThen if you want to display Emails in your web application, you need some class that acts as an email resource for traversal. A resource must implement the __name__ and __parent__ properties, and often has a __getitem__ and __iter__ method to traverse down to its children.
From a software architectural viewpoint, these properties and methods together form the Resource interface. This interface is part of your application’s infrastructure layer, or, to put it in Domain Driven Design
To come back to the example, your Email may appear elsewhere in your application, for instance when called from another API, such as a command-line interface or desktop GUI. It then wouldn’t need the Resource interface. Besides, it would complicate your domain model.
So the object-oriented solution is to separate these two responsibilities: the core Model class, and the class that implements the Resource interface for that model. The most straightforward way to do is with the adapter pattern. (here is an excellent blogpost about the adapter pattern for python) For each model FooModel, create a class FooResource, which implements the Resource interface, and wraps the model object.
class EmailResource(object): def __init__(self, email, parent): self.__name__ = email.subject self.__parent__ = parent self.inner = email def __getitem__(self, key): if key == ‘replies’: #use your email to somehow retrieve all replies to it, e.g. return EmailListResource(emailRepository.getRepliesFor(self.inner)) else: raise KeyError @property def subject(self): return self.inner.subject def send(self): self.inner.send()The Resource adapter fulfills the Resource interface, offering name, parent, and a getitem method to get to a child on the traversal tree. It delegates these to the inner model object. It also implements the regular model interface, meaning that your can use the traversal-retrieved object in your application as if it were a regular ‘Email’. It simply delegates this behavior to the inner object as well.
From this point, it will also be easier to standardize your resources into, say, a ListResource, a SearchableResource, you name it.
Comments? Write me an email or discuss on facebook, google+ or twitter.
Read other blog posts