|
11
|
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) |