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