Active Object in Simpycity

Simpycity is, as we've previously covered, a small library that permits for the direct mapping of arbitrary SQL statements to Python callables. This power allows for the development of complex representations that do not need to directly map to the underlying database representation. This differs from most conventional ORM technology, which follows the ActiveRecord pattern. Simpycity was implemented in this way for a great many reasons, first and foremost that the Active Record pattern is not the best representation of your data to your application logic. Should the application need to be aware of underlying data relationships? Should the application be aware of foreign keys, structures, and other underlying constructs? Or should the application be able to interact with the data in a form that is logical, and sensible to the application, without needing deep knowledge of underlying representations? We thought so, and Simpycity, and a concept more along the lines of Active Object is our result.

Disparate Representations

Simpycity, instead of writing SQL for you via query generators, requires that the developer write SQL by hand. The reason for this is that database representations are not generalizable into object relations - this disconnect is the entire reason behind the object-relational difficulties. A proper object representation encapsulates all the possible information about a method in a single location, as well as all the necessary methods to act on that data. A single object then represents a single quantum of data. However, for a relational system, normal form requires that disparate pieces of information are further broken down, into points of absolute truth about the data. A person's name, for instance, is a point of absolute truth, and should exist in only a single place in the database, whereas a person's name could exist in several places in an Object system, in a sensible manner. The disparity comes in that a Person, in terms of business requirements, is rather different from a Person in SQL terms, to the point where it would not be sensible to represent a database Person as an object Person - Address information, birthdate, all sorts of ancillary data that would normally be present isn't, per correct normal form. Simpycity works to avoid this, by allowing for business models that have little if anything in common with the underlying table structure, allowing for proper normalization as well as useful business objects.

Forging Anew

As Simpycity does not impose the database structure on your objects, it can't immediately provide the functionalities of .new() in the way a conventional ORM can - even though we've seen Simpycity handle the .save() feature brilliantly. Instead, if you instance a Simpycity model, not from the database, you get precisely and only a Simpycity instance. As it's not connected to a known set of database data, all the functions and other associated items have no way of operating, and the model just sits there, forlorn and empty. But since we have to match the Active Record pattern, how would we go about providing .new() in Simpycity? Here's how we do it: Given a standard model that looks like this,
class model(ctx.Model()):
     table = ['id','name']
     __load__ = ctx.Function("load_obj", ['id'])
We're able to do simple and basic load operations. Right? But, to create a new object, the pattern more resembles:
class model(ctx.Model()):
...

new = ctx.Function("new_obj",['name'], return_type=model)
Which allows for the external interface of:
import yourmodel
o = yourmodel.new("Some name")
o.commit()
Providing a clean and sensible model API, following the ActiveRecord pattern, but still offering all the power of Simpycity.

Twisty Little Properties

Another very nifty capability of ActiveRecord systems is that of reflection, automatically retrieving the far end of a foreign key constraint. This allows for useful functionality like
aModel.comments
correctly reaching across the one-to-many relationship and pulling all the comments. As Simpycity doesn't directly map tables, capabilities such as this aren't directly implemented in Simpycity. However, since we do realize that business objects need to perform similar tricks and load data in via properties, we added specific support for this into Simpycity. But, since Simpycity is entirely callable based, we had to be able to support this feature with our existing metaphors. To that end, we included a simple function that will take any Simpycity callable (or any callable, really), interrogate its argument list, and handle argument mapping as you'd expect. Using this feature is as simple as:
    from simpycity.helpers import prop
    
    class myTextObject( ctx.Model() ):
        table = ['id', 'value']
        __load__ = ctx.Function("textobject.by_id",['id'])
        comments = prop( get=ctx.Function("textobject.comments", ['id']) )
        
    mto = myText(1)
    comments = mto.comments
Easily allowing for sensible properties to be created, based entirely on clean Simpycity code. Properties created in this way even support set and delete functionality, identical to a standard property, allowing for property accessors to easily manipulate the database layer. As a note, prop() is a new feature in 0.3.1. 0.3.0 and below should use
    from simpycity.helpers import sprop
    
    class myTextObject( ctx.Model() ):
        ...
        comments = property(sprop( get=ctx.Function("textobject.comments", ['id']) ))
Obviously not as clean, and not as capable. You should upgrade ASAP.

Next

We've stabilized the API, made everything work through the consistent Context interface, and have built a powerful callable-based model infrastructure for all sorts of application development. So what's next for Simpycity? Well, some of the things we're planning on include breaking the Model object away from psycopg2 dependency, allowing us to use other PG drivers (such as pg8000), as well as opening up the Model protocol we've defined for other contexts - file access, for instance. Anywhere that an application needs to represent a complex underlying structure as a simple object, the Model could be used. More in the future, we're really looking forward to integrating Simpycity callables with Django and SQLAlchemy model objects, using Simpycity to provide strong, clean functional and raw query support in those environments. And vice-versa as well: Binding a SQLAlchemy or Django ORM chain to a Simpycity object, using it to populate an object, and building even more complex, effective business objects for your application. Even farther afield, we've been looking at integrating query generation to Simpycity. There's a lot of boilerplate SQL that needs writing, and being able to hand it to an elegant, PostgreSQL-focussed abstraction would be, we think, ideal. As always, Simpycity is available on our Wiki, and our code can always be checked out from our Subversion repository. Finally, Simpycity is easily installed from the PyPI index, using easy_install Simpycity.