Dear List,

This is my first time writing to the list. I've been working with Django for
the last two years and recently started hacking on Django itself.

For a while I have felt restricted by the fact that I cannot replace the
user model. I use the profile feature for additional methods and attributes.
However, it is inelegant, particularly when I wish to modify an existing
user method. Also, as I develop a library of applications, and try to use
apps I find on the web, I find it difficult to reuse apps because they
expect certain models to be imported from certain places. For example, two
years ago I created an app that integrated with the US postal service web
APIs. I recently was working on a crm-type website that I would like to add
shipping functionality to. However, the shipping app had its own
Package model that needed to tightly integrate with a ShippingEvent model.
My crm already has its own Letter model that tightly integrates with the
UserProfile model and the reports views. I have no easy way to integrate
these two applications. The only way I can see is to completely fork my USPS
app and integrate its functionality line-by-line with my crm.

I think this problem could be solved by making models pluggable. I realize
that this has been proposed before (
http://groups.google.com/group/django-developers/browse_thread/thread/2dd8fb9ea1fd3763/ef7db33f2fe713a3).
I have been thinking about how to bring the advantages of a pluggable
architecture to django's models without any of the disadvantages described
in the thread.

It would need to:
1) be completely backwards compatible
2) not require additional unpythonic behavior (such as interfaces)
3) Existing models should be pluggable with no modification
4) Not require any significant change in code base

I think I may have a system that meets these requirements. All it would
require of the developer is to place a dictionary of components in her
project's settings.py file. Each key would be the dotted path to an original
model, each value would be the dotted path to the model that will replace
the original model. When the original model is imported, the replacement
model would instead be returned completely transparently to the application
importing the model.

With this system in place, I could simply move all the functionality in my
package model in my USPS app to a base class, then my letter model in my crm
could inherit from that base model. I set my
settings.COMPONENTS={'usps.models.package':'crm.models.letter'} and
magically, my USPS app and letter app work together. Every time my USPS app
gets a usps.models.package it actually receives a crm.models.letter that
does the same thing. My ShippingEvents is now

This system definitely meets the first three requirements.

1) it is completely backwards compatible. If there is no COMPONENTS
dictionary, everything works exactly the way it did before plugging was
added.
2) there is no need for interfaces or getter functions that get the
appropriate class or object, you simply import and call as usual.
3) existing models can be overridden with no extra work.

4) is the only part I cannot answer fully. I started an implementation, just
to see if it was even possible,  and I've made some progress - it works with
quite a few of my test models. However, I am just learning the internals of
Django, and there are still errors with my implementation. I wanted to put
this to the list before going further.
As far as I've gotten with actual implementation: When a project imports the
original model (original.model), django.db.models.base.ModelBase.__new__
checks to see if "original.model." is in the COMPONENTS dictionary. If it
is, it gets the path to the replacement model ("replacement.model2"), and
gets the replacement class. It calls
django.db.models.register_models('original', model=<class "model2">) which
registers the replacement model class under the original application and
with the name 'model'. Now, there are multiple copies of replacement.model2
in django.db.models.loading.cache, so django.db.models.get_models() removes
duplicates before returning all models. There are other internals that will
need to be modified, besides these three methods, in order to eliminate the
occasional errors I'm getting in syncdb; I'm still figuring out what those
are.

Your feedback is appreciated.

David Greisen

-- 
You received this message because you are subscribed to the Google Groups 
"Django developers" group.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to 
[email protected].
For more options, visit this group at 
http://groups.google.com/group/django-developers?hl=en.

Reply via email to