views:

255

answers:

1

Scenario: large project with many third party apps. Want to add tagging to those apps without having to modify the apps' source.

My first thought was to first specify a list of models in settings.py (like ['appname.modelname',], and call django-tagging's register function on each of them. The register function adds a TagField and a custom manager to the specified model. The problem with that approach is that the function needs to run BEFORE the DB schema is generated.

I tried running the register function directly in settings.py, but I need django.db.models.get_model to get the actual model reference from only a string, and I can't seem to import that from settings.py - no matter what I try I get an ImportError. The tagging.register function imports OK however.

So I changed tactics and wrote a custom management command in an otherwise empty app. The problem there is that the only signal which hooks into syncdb is post_syncdb which is useless to me since it fires after the DB schema has been generated.

The only other approach I can think of at the moment is to generate and run a 'south' like database schema migration. This seems more like a hack than a solution.

This seems like it should be a pretty common need, but I haven't been able to find a clean solution.

So my question is: Is it possible to dynamically add fields to a model BEFORE the schema is generated, but more specifically, is it possible to add tagging to a third party model without editing it's source.

To clarify, I know it is possible to create and store Tags without having a TagField on the model, but there is a major flaw in that approach in that it is difficult to simultaneously create and tag a new model.

+1  A: 

From the docs:

You don't have to register your models in order to use them with the tagging application - many of the features added by registration are just convenience wrappers around the tagging API provided by the Tag and TaggedItem models and their managers, as documented further below.

Take a look at the API documentation and the examples that follow for how you can add tags to any arbitrary object in the system.

http://api.rst2a.com/1.0/rst2/html?uri=http://django-tagging.googlecode.com/svn/trunk/docs/overview.txt#tags

Updated

#views.py
def tag_model_view(request, model_id):
    instance_to_tag = SomeModel.objects.get(pk=model_id)
    setattr(instance_to_tag, 'tags_for_instance', request.POST['tags'])
    ...
    instance_to_tag.save()
    ...returns response

#models.py
#this is the post_save signal receiver
def tagging_post_save_handler(sender, instance, created):
    if hasattr(instance, 'tags_for_instance'):
        Tag.objects.update_tags(instance, instance.tags_for_instance)
Brian Luft
Yes of course. But where does the code to add the tags go? I need to add the tags immediately after the object is saved - obviously that could be done easily by overriding the object's save() method, but the point of the question is to avoid modifying the app.
Chris Lawlor
You can use the post_save signal to accomplish this:http://docs.djangoproject.com/en/1.1/ref/signals/#django.db.models.signals.post_save
Brian Luft
I think you are on the right track - I've been considering using post_save but it has another problem. post_save provides three arguments: The class being saved, the actual instance, and a boolean which is True if a new record is created. There is no way to access the `request` object, which is where the new tags are.
Chris Lawlor
I was under the assumption that at some point during the execution of the request (and prior to calling save() on the model instance) you would have access to both the tags and the model instance you'd like to tag (in a view handler function for example). Then you would just stick the tags on the instance and in the post_save handler use the tagging API to add the tags. Updating my answer with a pseudo-code example.
Brian Luft
This would involve either re-writing the app's view, or at least writing a wrapper around it and re-writing the app's URLConf to point at the wrapper... Ultimately the goal was to be able to 'marry' tagging and the third party app only in the template, and not touch the app's source at all, but I don't think that is quite possible.
Chris Lawlor
Thanks for the answer vote! If I'm following correctly, you have a template that is being rendered by a 3rd party app's view and includes the model instance(s) you would like to tag in the render context. Is the 3rd party app not using a RequestContext? It seems like you could write a template tag that would take the model instance you'd like to tag as an argument and then you'd pull the tags from the request and add them there. The model should already be saved at the point of template render. Even w/o RequestContext, you could still use a context processor to get around that.
Brian Luft