Luke,

I'm definitely interested, since I've been thinking about doing this
very thing.

Regards,
-scott

On Fri, 2006-05-26 at 00:57 +0100, Luke Plant wrote:
> Hi all,
> 
> I've been writing some tagging functionality for my site, and I've 
> developed it in a way that is reusable and generic, similar in some 
> ways to the 'comments' app in contrib.  Would anyone be interested in 
> me tidying it up for release as a Django app?  It would require a 
> little bit of tidying up (mainly fixing Python 2.3 incompatibilities), 
> and if popular, it could do with a bit of community input.  I'd be 
> happy to release it under a BSD license.
> 
> Below is an overview of what is included.  
> 
> Tag model
> =========
> - The central model.  A *simplified* version is below:
> 
> class Tag(models.Model):
>     text = models.CharField()
>     target = GenericForeignKey()
>     creator = GenericForeignKey()
>     added = models.DateTimeField()
> 
> GenericForeignKey works like a foreign key field except that it is 
> identified by two pieces of information - an 'id' (stored as a string 
> for maximum generality), and a ContentType id. (The model actually 
> requires other parameters and other fields to be present, not shown).  
> For the most part, however, you can ignore this implementation detail.  
> Externally, you just set/get mytag.target or mytag.creator to any 
> Django ORM object and it will work. 
> 
> Tag objects also have other methods to do efficient SQL queries for 
> summarising Tag information.  e.g. .count_tagged_by_others() returns  
> the number of other 'creator' objects that have tagged the same object, 
> and there are other methods on the TagManager for doing this kind of 
> thing (see below).
> 
> If you want, you can have different types of object that are taggable, 
> so:
> 
>   [t.target for t in Tag.objects.all()] 
> 
> could be a heterogeneous list. In templates that show lists of Tag 
> objects in detail, this could be a problem, since you don't know what 
> type of object mytag.target is, and you might want to customise how you 
> display a tagged target object. So I've added a 'render_target' method 
> which is pluggable i.e. you can provide different ways of rendering 
> different types of target objects, and then just use tag.render_target
> in the template.
> 
> (You could also have different types of 'creator' object, but in my case 
> I haven't used this, so haven't tested it much, but I'm not aware of 
> any problems).
> 
> Tag Manager
> ===========
> The Manager class for Tag has various methods to return database 
> information about tagged objects.  It uses efficient SQL to do so, 
> which means that most of them can't build up queries the way you 
> normally do in Django, but instead the methods provide optional 
> parameters that should cover most of the kind of queries you want to 
> do, including searching for objects that are tagged with multiple text 
> values.
> 
> The methods tend to return either simple values, or 'partially 
> aggregated' versions of the Tag object:
> 
> TagSummary
> ----------
> Contains 'text' and 'count', where count is the number of Tag objects
> with that 'text' value.
> 
> TagTarget
> ---------
> The 'text' + 'target' half of a 'Tag' object, used for answering the 
> question: "What objects are the target of a certain 'text' value (or 
> values) and how many 'creator' objects have tagged it like that?"
> 
> The tag manager methods also work correctly in a 'related' context (see 
> below).
> 
> Tag relationships
> =================
> A Tag is essentially two foreign key fields (plus metadata), but since 
> it isn't actually a ForeignKey object, and can point to multiple
> models, it doesn't appear on the 'pointed to' models automatically (e.g. 
> as in mypost.tags.all() or myuser.created_tags.all()). However, you can 
> set this up with the add_tagging_fields() utility method, which allows 
> you add attributes to models with complete flexibility.  You don't 
> define the tags as part of your model, but use this utility method 
> after creating the model to add the attributes.
> 
> This has been done like this mainly for ease of implementation, but it
> also keeps your model decoupled from 'tagging' -- after you've defined 
> your model, or imported someone else's, you can add tagging fields very 
> easily.
> 
> In this related context, you also get methods that parallel normal 
> foreign keys i.e. mypost.tags.create() and mypost.tags.add(), which 
> work as expected.
> 
> Finally, the Tag Manager methods for advanced queries also work 
> correctly in the 'related' context e.g.  
> mypost.tags.get_distinct_text() limits itself to the relevant Tag 
> objects. 
> 
> Views
> =====
> A simple tagging facility will allow users to tag objects, and then 
> display those tags inline on the object that is tagged (e.g. a post or 
> a topic etc).  To enable this, a 'create_update' view is provide, with 
> a sample template -- a del.icio.us style form for editing tags for an 
> item.
> 
> A more complete solution will involve the ability to browse/search tags,
> view recent tags etc.  For this, I've written a 'recent_popular' view, 
> that shows recent tags, optionally limiting to a specific target or 
> 'text' value etc, with paging, and shows a list of popular tags 
> (limited by the same query).
> 
> Finally, there is 'targets_for_text' which searches for targets with a 
> specific 'text' value, or several text values, and displays them in a 
> paged list by decreasing popularity.
> 
> Most of my tagging related views are simple wrappers around these three.
> 
> Template tags
> =============
> I've included a template tag for getting a list of TagSummary
> objects for a target object e.g.:
>   {% get_tag_summaries for post as tags %}
> 
> Templates
> =========
> I've included a simple template for create_update, that works as is. You
> can use another template, of course.
> 
> URLS
> ====
> I've found that sorting out my urls for tagging has been one of the 
> hardest things, especially as I'm going for a fairly complete solution 
> (e.g. you can drill down to the level of a particular Member, see all 
> their tags, see who tagged a particular target with a given text value 
> and when etc.).  I haven't been able to abstract this into a standard 
> system, so I haven't included any implementation of get_absolute_url or 
> anything else to do with URLs.  I have my own custom template_tags for 
> generating absolute urls for Tags in different contexts, and I also 
> sometimes use the ContentType object. 
> 
> Tag.target is implemented using:
>       Tag.target_ct = models.ForeignKey(ContentType)
> so for any specific Tag I can get tag.target_ct.name and I tend to use 
> that and and Tag.target_id to generate some URLs.
> 
> Feeds
> =====
> I normally do feeds using the same view functions as HTML pages, but 
> with ?format=atom.  I've added a hook to the relevant views that allows 
> this.
> 
> 
> Resusable bits
> ==============
> Some of the stuff I implemented along the way is even more generic, 
> including some 'generic foreign key' descriptors and 'generic m2m 
> object' descriptors, which manage to avoid using any SQL directly, and 
> so should be very robust. They depends on the ContentType model, and 
> some of the utility functions.
> 
> 
> Let me know if you are interested, I'll tidy up the code and post it 
> somewhere.
> 
> Luke
> 


--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups 
"Django users" group.
To post to this group, send email to django-users@googlegroups.com
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at 
http://groups.google.com/group/django-users
-~----------~----~----~----~------~----~------~--~---

Reply via email to