GSoC Proposal: Plugin API

0 views
Skip to first unread message

Alberto

unread,
Apr 3, 2008, 3:57:54 PM4/3/08
to django-gsoc
Plugins are one of the things that make a good application an awesome
application. Take, for example, Firefox. It's plugin API lets the
developers focus on the core browser, while third parties can provide
extensions for doing anything you can think of. From my point of view,
any Django application could greatly benefit from having a standard
plugin API, defining, at least, the next operations:


Extending a model:

Currently, there's no mechanism for extending models other than
editing models.py. One of the aims of this project is defining a set
of classes and functions which will extend the target model, so
plugins can store its data in the most easy way. For example:

from myblog.models import Comment

class MyPluginComment(models.ExtensionModel):
class Meta:
model = Comment
subscribed = models.BooleanField(default=False)

Extending a model's methods:

Take the previous example. Let's suppose we want to send an email to
users which choose to subscribe to a post. The plugin would want to
attach to Comment.save() and send an email when a new comment is
posted. For example:

from myblog.models import Comment

class MyPluginComment(models.ExtensionModel):
class Meta:
model = Comment
subscribed = models.BooleanField(default=False)

def _save(self):
send_email_to_subscribers(self)

save = method_append(_save) # method_prepend(), method_override()

Registering a plugin:

The plugin API also needs some functions for a plugin to register in
the project. This functions would check the tables for the new fields
defined and create them when needed, as well as override the
corresponding methods.

Awaiting your comments,
Alberto

Malcolm Tredinnick

unread,
Apr 4, 2008, 7:54:13 PM4/4/08
to djang...@googlegroups.com

Adding a new field or overriding a method are both best done via
subclassing, which will be in trunk very soon now.

Providing new behaviour for a model that already exists is best done by
registering a signal handler (you can only change functional behaviour
in this way, but you aren't going to want to add new fields to a default
model anyway, since existing users won't know how to use them -- thus a
subclass is better and quite appropriate for that case).

In summary, I think all the behaviour you're wanting is already possible
or work that is under active development and nearly finished.

Regards,
Malcolm

--
I've got a mind like a... a... what's that thing called?
http://www.pointy-stick.com/blog/

Jason L.

unread,
Apr 7, 2008, 12:46:40 PM4/7/08
to django-gsoc
Alberto,

Just a fellow student applicant, but thought I'd chime in.

Your goal, it seems, is to be able to add functionality to a model
without the model needing to be aware of that added functionality.
Most such "uncoupled" extensions are currently done through generic
foreign keys.

To rephrase your example:

class Subscriber(models.Model):
subscriber_model = models.ForeignKey(ContentType)
subscriber_id = models.PositiveIntegerField()
subscriber = generic.GenericForeignKey('content_type',
'object_id')

comment = models.ForeignKey(Comment)

def send_emails(self):
...

This approach in general is a bit awkward and sub-optimal at the
moment and subclassing will do little to "uncouple" a plugin from its
model. Bear in mind, however, that your approach wouldn't uncouple
things either. Users of the given plugin still have to hand-append
their extra functionality in their own code. Everything still has
rigid links.

When you're rigidly coupled, this can create a variety of problems.
First, if the implementation of the model changes on you, your code
breaks. Furthermore, namespace issues can easily arise, where two
plugins add different fields with the same name. Subclassing avoids
this because the model heirarchy garuantees which field you end up
with at a given point in the inheritance tree. Your plugin method,
however, would have an undefined result (the field you end up with
depends on the path of code execution).

Automatically adjusting tables on the back-end is seriously bad mojo.
What if a namespace conflict occurs, and suddenly your table juggler
sees a field, "priority", which was an integer but is now a positive
integer because a new plugin has overridden the old field. Your
juggler will change around the field, potentially destroying whole
swaths of data.

As Malcolm mentioned, signal handlers can be used to tell when an
object is saved (check out the dispatch code in trunk). You'd have to
work a little magic on processing the signals (since you won't know
beforehand what models to watch for, so you can't bind to a complex
signal that includes the model). Rewriting dispatch isn't necessarily
a good idea; dispatch has some neat tricks like a well-written "robust
apply" method for intelligently passing arguments to callbacks.


You could change your approach and instead of writing new
functionality for all of this, you could make convenience functions
that wrap around the current functionality, to make such extensions of
current apps easier to write. I'm not sure if this would constitute
enough work on its own to be a SoC project, however.

Also bear in mind that you're talking about extending django
'applications', not extending Django itself, so your firefox analogy
isn't quite accurate.

It's not a bad idea to want to streamline this process, but doing so
runs pretty deep into the code and involves considerations you haven't
taken into account.

Sorry to be a gloomy gus, hope I've helped some.


-Jason L.
Reply all
Reply to author
Forward
0 new messages