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 likeaModel.commentscorrectly 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.commentsEasily 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.