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 -~----------~----~----~----~------~----~------~--~---