web/lib/tagging/fields.py
author ymh <ymh.work@gmail.com>
Thu, 05 Aug 2010 17:28:09 +0200
changeset 50 012451a812f1
parent 11 f236caaceb43
permissions -rw-r--r--
Merge with a2711e44ba5de8b1675d7e0ee6aaa4a6c56a9b46
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
11
f236caaceb43 add pois
ymh <ymh.work@gmail.com>
parents:
diff changeset
     1
"""
f236caaceb43 add pois
ymh <ymh.work@gmail.com>
parents:
diff changeset
     2
A custom Model Field for tagging.
f236caaceb43 add pois
ymh <ymh.work@gmail.com>
parents:
diff changeset
     3
"""
f236caaceb43 add pois
ymh <ymh.work@gmail.com>
parents:
diff changeset
     4
from django.db.models import signals
f236caaceb43 add pois
ymh <ymh.work@gmail.com>
parents:
diff changeset
     5
from django.db.models.fields import CharField
f236caaceb43 add pois
ymh <ymh.work@gmail.com>
parents:
diff changeset
     6
from django.utils.translation import ugettext_lazy as _
f236caaceb43 add pois
ymh <ymh.work@gmail.com>
parents:
diff changeset
     7
f236caaceb43 add pois
ymh <ymh.work@gmail.com>
parents:
diff changeset
     8
from tagging import settings
f236caaceb43 add pois
ymh <ymh.work@gmail.com>
parents:
diff changeset
     9
from tagging.models import Tag
f236caaceb43 add pois
ymh <ymh.work@gmail.com>
parents:
diff changeset
    10
from tagging.utils import edit_string_for_tags
f236caaceb43 add pois
ymh <ymh.work@gmail.com>
parents:
diff changeset
    11
f236caaceb43 add pois
ymh <ymh.work@gmail.com>
parents:
diff changeset
    12
class TagField(CharField):
f236caaceb43 add pois
ymh <ymh.work@gmail.com>
parents:
diff changeset
    13
    """
f236caaceb43 add pois
ymh <ymh.work@gmail.com>
parents:
diff changeset
    14
    A "special" character field that actually works as a relationship to tags
f236caaceb43 add pois
ymh <ymh.work@gmail.com>
parents:
diff changeset
    15
    "under the hood". This exposes a space-separated string of tags, but does
f236caaceb43 add pois
ymh <ymh.work@gmail.com>
parents:
diff changeset
    16
    the splitting/reordering/etc. under the hood.
f236caaceb43 add pois
ymh <ymh.work@gmail.com>
parents:
diff changeset
    17
    """
f236caaceb43 add pois
ymh <ymh.work@gmail.com>
parents:
diff changeset
    18
    def __init__(self, *args, **kwargs):
f236caaceb43 add pois
ymh <ymh.work@gmail.com>
parents:
diff changeset
    19
        kwargs['max_length'] = kwargs.get('max_length', 255)
f236caaceb43 add pois
ymh <ymh.work@gmail.com>
parents:
diff changeset
    20
        kwargs['blank'] = kwargs.get('blank', True)
f236caaceb43 add pois
ymh <ymh.work@gmail.com>
parents:
diff changeset
    21
        super(TagField, self).__init__(*args, **kwargs)
f236caaceb43 add pois
ymh <ymh.work@gmail.com>
parents:
diff changeset
    22
f236caaceb43 add pois
ymh <ymh.work@gmail.com>
parents:
diff changeset
    23
    def contribute_to_class(self, cls, name):
f236caaceb43 add pois
ymh <ymh.work@gmail.com>
parents:
diff changeset
    24
        super(TagField, self).contribute_to_class(cls, name)
f236caaceb43 add pois
ymh <ymh.work@gmail.com>
parents:
diff changeset
    25
f236caaceb43 add pois
ymh <ymh.work@gmail.com>
parents:
diff changeset
    26
        # Make this object the descriptor for field access.
f236caaceb43 add pois
ymh <ymh.work@gmail.com>
parents:
diff changeset
    27
        setattr(cls, self.name, self)
f236caaceb43 add pois
ymh <ymh.work@gmail.com>
parents:
diff changeset
    28
f236caaceb43 add pois
ymh <ymh.work@gmail.com>
parents:
diff changeset
    29
        # Save tags back to the database post-save
f236caaceb43 add pois
ymh <ymh.work@gmail.com>
parents:
diff changeset
    30
        signals.post_save.connect(self._save, cls, True)
f236caaceb43 add pois
ymh <ymh.work@gmail.com>
parents:
diff changeset
    31
f236caaceb43 add pois
ymh <ymh.work@gmail.com>
parents:
diff changeset
    32
    def __get__(self, instance, owner=None):
f236caaceb43 add pois
ymh <ymh.work@gmail.com>
parents:
diff changeset
    33
        """
f236caaceb43 add pois
ymh <ymh.work@gmail.com>
parents:
diff changeset
    34
        Tag getter. Returns an instance's tags if accessed on an instance, and
f236caaceb43 add pois
ymh <ymh.work@gmail.com>
parents:
diff changeset
    35
        all of a model's tags if called on a class. That is, this model::
f236caaceb43 add pois
ymh <ymh.work@gmail.com>
parents:
diff changeset
    36
f236caaceb43 add pois
ymh <ymh.work@gmail.com>
parents:
diff changeset
    37
           class Link(models.Model):
f236caaceb43 add pois
ymh <ymh.work@gmail.com>
parents:
diff changeset
    38
               ...
f236caaceb43 add pois
ymh <ymh.work@gmail.com>
parents:
diff changeset
    39
               tags = TagField()
f236caaceb43 add pois
ymh <ymh.work@gmail.com>
parents:
diff changeset
    40
f236caaceb43 add pois
ymh <ymh.work@gmail.com>
parents:
diff changeset
    41
        Lets you do both of these::
f236caaceb43 add pois
ymh <ymh.work@gmail.com>
parents:
diff changeset
    42
f236caaceb43 add pois
ymh <ymh.work@gmail.com>
parents:
diff changeset
    43
           >>> l = Link.objects.get(...)
f236caaceb43 add pois
ymh <ymh.work@gmail.com>
parents:
diff changeset
    44
           >>> l.tags
f236caaceb43 add pois
ymh <ymh.work@gmail.com>
parents:
diff changeset
    45
           'tag1 tag2 tag3'
f236caaceb43 add pois
ymh <ymh.work@gmail.com>
parents:
diff changeset
    46
f236caaceb43 add pois
ymh <ymh.work@gmail.com>
parents:
diff changeset
    47
           >>> Link.tags
f236caaceb43 add pois
ymh <ymh.work@gmail.com>
parents:
diff changeset
    48
           'tag1 tag2 tag3 tag4'
f236caaceb43 add pois
ymh <ymh.work@gmail.com>
parents:
diff changeset
    49
f236caaceb43 add pois
ymh <ymh.work@gmail.com>
parents:
diff changeset
    50
        """
f236caaceb43 add pois
ymh <ymh.work@gmail.com>
parents:
diff changeset
    51
        # Handle access on the model (i.e. Link.tags)
f236caaceb43 add pois
ymh <ymh.work@gmail.com>
parents:
diff changeset
    52
        if instance is None:
f236caaceb43 add pois
ymh <ymh.work@gmail.com>
parents:
diff changeset
    53
            return edit_string_for_tags(Tag.objects.usage_for_model(owner))
f236caaceb43 add pois
ymh <ymh.work@gmail.com>
parents:
diff changeset
    54
f236caaceb43 add pois
ymh <ymh.work@gmail.com>
parents:
diff changeset
    55
        tags = self._get_instance_tag_cache(instance)
f236caaceb43 add pois
ymh <ymh.work@gmail.com>
parents:
diff changeset
    56
        if tags is None:
f236caaceb43 add pois
ymh <ymh.work@gmail.com>
parents:
diff changeset
    57
            if instance.pk is None:
f236caaceb43 add pois
ymh <ymh.work@gmail.com>
parents:
diff changeset
    58
                self._set_instance_tag_cache(instance, '')
f236caaceb43 add pois
ymh <ymh.work@gmail.com>
parents:
diff changeset
    59
            else:
f236caaceb43 add pois
ymh <ymh.work@gmail.com>
parents:
diff changeset
    60
                self._set_instance_tag_cache(
f236caaceb43 add pois
ymh <ymh.work@gmail.com>
parents:
diff changeset
    61
                    instance, edit_string_for_tags(Tag.objects.get_for_object(instance)))
f236caaceb43 add pois
ymh <ymh.work@gmail.com>
parents:
diff changeset
    62
        return self._get_instance_tag_cache(instance)
f236caaceb43 add pois
ymh <ymh.work@gmail.com>
parents:
diff changeset
    63
f236caaceb43 add pois
ymh <ymh.work@gmail.com>
parents:
diff changeset
    64
    def __set__(self, instance, value):
f236caaceb43 add pois
ymh <ymh.work@gmail.com>
parents:
diff changeset
    65
        """
f236caaceb43 add pois
ymh <ymh.work@gmail.com>
parents:
diff changeset
    66
        Set an object's tags.
f236caaceb43 add pois
ymh <ymh.work@gmail.com>
parents:
diff changeset
    67
        """
f236caaceb43 add pois
ymh <ymh.work@gmail.com>
parents:
diff changeset
    68
        if instance is None:
f236caaceb43 add pois
ymh <ymh.work@gmail.com>
parents:
diff changeset
    69
            raise AttributeError(_('%s can only be set on instances.') % self.name)
f236caaceb43 add pois
ymh <ymh.work@gmail.com>
parents:
diff changeset
    70
        if settings.FORCE_LOWERCASE_TAGS and value is not None:
f236caaceb43 add pois
ymh <ymh.work@gmail.com>
parents:
diff changeset
    71
            value = value.lower()
f236caaceb43 add pois
ymh <ymh.work@gmail.com>
parents:
diff changeset
    72
        self._set_instance_tag_cache(instance, value)
f236caaceb43 add pois
ymh <ymh.work@gmail.com>
parents:
diff changeset
    73
f236caaceb43 add pois
ymh <ymh.work@gmail.com>
parents:
diff changeset
    74
    def _save(self, **kwargs): #signal, sender, instance):
f236caaceb43 add pois
ymh <ymh.work@gmail.com>
parents:
diff changeset
    75
        """
f236caaceb43 add pois
ymh <ymh.work@gmail.com>
parents:
diff changeset
    76
        Save tags back to the database
f236caaceb43 add pois
ymh <ymh.work@gmail.com>
parents:
diff changeset
    77
        """
f236caaceb43 add pois
ymh <ymh.work@gmail.com>
parents:
diff changeset
    78
        tags = self._get_instance_tag_cache(kwargs['instance'])
f236caaceb43 add pois
ymh <ymh.work@gmail.com>
parents:
diff changeset
    79
        if tags is not None:
f236caaceb43 add pois
ymh <ymh.work@gmail.com>
parents:
diff changeset
    80
            Tag.objects.update_tags(kwargs['instance'], tags)
f236caaceb43 add pois
ymh <ymh.work@gmail.com>
parents:
diff changeset
    81
f236caaceb43 add pois
ymh <ymh.work@gmail.com>
parents:
diff changeset
    82
    def __delete__(self, instance):
f236caaceb43 add pois
ymh <ymh.work@gmail.com>
parents:
diff changeset
    83
        """
f236caaceb43 add pois
ymh <ymh.work@gmail.com>
parents:
diff changeset
    84
        Clear all of an object's tags.
f236caaceb43 add pois
ymh <ymh.work@gmail.com>
parents:
diff changeset
    85
        """
f236caaceb43 add pois
ymh <ymh.work@gmail.com>
parents:
diff changeset
    86
        self._set_instance_tag_cache(instance, '')
f236caaceb43 add pois
ymh <ymh.work@gmail.com>
parents:
diff changeset
    87
f236caaceb43 add pois
ymh <ymh.work@gmail.com>
parents:
diff changeset
    88
    def _get_instance_tag_cache(self, instance):
f236caaceb43 add pois
ymh <ymh.work@gmail.com>
parents:
diff changeset
    89
        """
f236caaceb43 add pois
ymh <ymh.work@gmail.com>
parents:
diff changeset
    90
        Helper: get an instance's tag cache.
f236caaceb43 add pois
ymh <ymh.work@gmail.com>
parents:
diff changeset
    91
        """
f236caaceb43 add pois
ymh <ymh.work@gmail.com>
parents:
diff changeset
    92
        return getattr(instance, '_%s_cache' % self.attname, None)
f236caaceb43 add pois
ymh <ymh.work@gmail.com>
parents:
diff changeset
    93
f236caaceb43 add pois
ymh <ymh.work@gmail.com>
parents:
diff changeset
    94
    def _set_instance_tag_cache(self, instance, tags):
f236caaceb43 add pois
ymh <ymh.work@gmail.com>
parents:
diff changeset
    95
        """
f236caaceb43 add pois
ymh <ymh.work@gmail.com>
parents:
diff changeset
    96
        Helper: set an instance's tag cache.
f236caaceb43 add pois
ymh <ymh.work@gmail.com>
parents:
diff changeset
    97
        """
f236caaceb43 add pois
ymh <ymh.work@gmail.com>
parents:
diff changeset
    98
        setattr(instance, '_%s_cache' % self.attname, tags)
f236caaceb43 add pois
ymh <ymh.work@gmail.com>
parents:
diff changeset
    99
f236caaceb43 add pois
ymh <ymh.work@gmail.com>
parents:
diff changeset
   100
    def get_internal_type(self):
f236caaceb43 add pois
ymh <ymh.work@gmail.com>
parents:
diff changeset
   101
        return 'CharField'
f236caaceb43 add pois
ymh <ymh.work@gmail.com>
parents:
diff changeset
   102
f236caaceb43 add pois
ymh <ymh.work@gmail.com>
parents:
diff changeset
   103
    def formfield(self, **kwargs):
f236caaceb43 add pois
ymh <ymh.work@gmail.com>
parents:
diff changeset
   104
        from tagging import forms
f236caaceb43 add pois
ymh <ymh.work@gmail.com>
parents:
diff changeset
   105
        defaults = {'form_class': forms.TagField}
f236caaceb43 add pois
ymh <ymh.work@gmail.com>
parents:
diff changeset
   106
        defaults.update(kwargs)
f236caaceb43 add pois
ymh <ymh.work@gmail.com>
parents:
diff changeset
   107
        return super(TagField, self).formfield(**defaults)