Two Months Working With Django… Wishing CakePHP had a Better ORM

I have been working with the Django Web framework for close to two months now and I’m loving it. Not so much for the free admin interface, but for the sheer ease of development that it provides. For the first time, I cared more about how my models are designed and not how they are laid out and implemented in the database. For the two projects that I am working on, I have yet to write a custom SQL statement under Django.

It makes me wish CakePHP offered more when it comes to object-relational mapping. How I wish it was as simple as defining your models and running php cake/scripts/bake.php syncdb. CakePHP does it the other way around: you create the database first and then use cake/scripts/bake.php to create your models and views.

I found that Django’s way of doing ORM is significantly faster. You specify your models as Python classes using Django’s modeling API. You run python manage.py syncdb and Django takes care of generating the correct SQL for your database backend. It takes model relationships into account and also generates the correct foreign key constraints. Beautiful!

A similar system should also be possible to implement in CakePHP. I’ve been having a few ideas of how to go about it and hopefully, I can find the time to do them as an add-on to CakePHP 1.2.x (and hopefully, someone else is already working on the same thing).

Here is what I imagine it would look like:

class User extends MetaModel {
    var $username;
    var $password;
    var $lastname;
    var $firstname;
    var $is_active;
    var $is_staff;
    var $is_admin;
    var $created;
    var $updated;

    function __construct() {
        $this->username = FieldFactory::CharField('Username', 32, IS_PK, IS_REQUIRED, NOT_NULL);
        $this->password = FieldFactory::CharField('Password', 64, NO_INDEX, IS_REQUIRED, NOT_NULL);
        $this->lastname = FieldFactory::CharField('Last Name', 64, INDEX, IS_OPTIONAL, NOT_NULL);
        $this->firstname = FieldFactory::CharField('First Name', 64, INDEX, IS_OPTIONAL, NOT_NULL);
        $this->is_active = FieldFactory::BooleanField('Active', IS_REQUIRED, NOT_NULL, false);
        $this->is_staff = FieldFactory::BooleanField('Staff', IS_REQUIRED, NOT_NULL, false);
        $this->is_admin = FieldFactory::BooleanField('Admin', IS_REQUIRED, NOT_NULL, false);
        $this->created = FieldFactory::DateTimeField('Created', NO_INDEX, IS_REQUIRED, NOT_NULL);
        $this->updated = FieldFactory::DateTimeField('Updated', NO_INDEX, IS_REQUIRED, NOT_NULL);
    }
}

FieldFactory would have static methods for instantiating the correct field objects for each type. It would also have methods for generating constraints for implementing many-to-one and many-to-many relationships. I think a new model class is required to be able to do the additional magic without changing the current API.

I haven’t thought about how to implement the analogs of Admin and Meta inner-classes yet. I think those can be done as fields in the model, maybe $this->__meta and $this->__admin. As for QuerySets, Q objects and other classes in the Django DB API those are pretty much doable under PHP.

For SQL generation using the models, I imagine the backend will first load all the model classes found. Then models extending the MetaModel base class are instantiated and introspected to generate the correct DDL statements in database-specific SQL.

This is not a trivial undertaking and will take a lot of planning and thought. Model introspection and ORM should use as much of the existing CakePHP API as possible.

One last thing, if you are working on a similar system for CakePHP leave comments below. Remember: DRY.

Leave a Reply

Comments are moderated by the administrator. If this is your first time posting a comment, your comment will go to a moderation queue and it may take a while for your comment to appear. Or it may get deleted.