Skip to content

Commit 219fa45

Browse files
committed
latest
1 parent aa18815 commit 219fa45

24 files changed

+1538
-857
lines changed

tagging/__init__.py

Lines changed: 11 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,62 +1,15 @@
1-
VERSION = (0, 4, 0, "dev", 1)
1+
"""
2+
Django-tagging
3+
"""
4+
__version__ = '0.4.6'
5+
__license__ = 'BSD License'
26

7+
__author__ = 'Jonathan Buchanan'
8+
__author_email__ = '[email protected]'
39

10+
__maintainer__ = 'Fantomas42'
11+
__maintainer_email__ = '[email protected]'
412

5-
def get_version():
6-
if VERSION[3] == "final":
7-
return "%s.%s.%s" % (VERSION[0], VERSION[1], VERSION[2])
8-
elif VERSION[3] == "dev":
9-
if VERSION[2] == 0:
10-
return "%s.%s.%s%s" % (VERSION[0], VERSION[1], VERSION[3], VERSION[4])
11-
return "%s.%s.%s.%s%s" % (VERSION[0], VERSION[1], VERSION[2], VERSION[3], VERSION[4])
12-
else:
13-
return "%s.%s.%s%s" % (VERSION[0], VERSION[1], VERSION[2], VERSION[3])
13+
__url__ = 'https://github.com/Fantomas42/django-tagging'
1414

15-
16-
__version__ = get_version()
17-
18-
19-
class AlreadyRegistered(Exception):
20-
"""
21-
An attempt was made to register a model more than once.
22-
"""
23-
pass
24-
25-
26-
registry = []
27-
28-
29-
def register(model, tag_descriptor_attr='tags',
30-
tagged_item_manager_attr='tagged'):
31-
"""
32-
Sets the given model class up for working with tags.
33-
"""
34-
35-
from tagging.managers import ModelTaggedItemManager, TagDescriptor
36-
37-
if model in registry:
38-
raise AlreadyRegistered("The model '%s' has already been "
39-
"registered." % model._meta.object_name)
40-
if hasattr(model, tag_descriptor_attr):
41-
raise AttributeError("'%s' already has an attribute '%s'. You must "
42-
"provide a custom tag_descriptor_attr to register." % (
43-
model._meta.object_name,
44-
tag_descriptor_attr,
45-
)
46-
)
47-
if hasattr(model, tagged_item_manager_attr):
48-
raise AttributeError("'%s' already has an attribute '%s'. You must "
49-
"provide a custom tagged_item_manager_attr to register." % (
50-
model._meta.object_name,
51-
tagged_item_manager_attr,
52-
)
53-
)
54-
55-
# Add tag descriptor
56-
setattr(model, tag_descriptor_attr, TagDescriptor())
57-
58-
# Add custom manager
59-
ModelTaggedItemManager().contribute_to_class(model, tagged_item_manager_attr)
60-
61-
# Finally register in registry
62-
registry.append(model)
15+
default_app_config = 'tagging.apps.TaggingConfig'

tagging/admin.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,16 @@
1+
"""
2+
Admin components for tagging.
3+
"""
14
from django.contrib import admin
2-
from tagging.models import Tag, TaggedItem
5+
6+
from tagging.models import Tag
7+
from tagging.models import TaggedItem
38
from tagging.forms import TagAdminForm
49

10+
511
class TagAdmin(admin.ModelAdmin):
612
form = TagAdminForm
713

14+
815
admin.site.register(TaggedItem)
916
admin.site.register(Tag, TagAdmin)
10-
11-
12-
13-

tagging/apps.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
"""
2+
Apps for tagging.
3+
"""
4+
from django.apps import AppConfig
5+
from django.utils.translation import ugettext_lazy as _
6+
7+
8+
class TaggingConfig(AppConfig):
9+
"""
10+
Config for Tagging application.
11+
"""
12+
name = 'tagging'
13+
label = 'tagging'
14+
verbose_name = _('Tagging')

tagging/fields.py

Lines changed: 22 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
from tagging import settings
99
from tagging.models import Tag
1010
from tagging.utils import edit_string_for_tags
11+
from tagging.forms import TagField as TagFormField
12+
1113

1214
class TagField(CharField):
1315
"""
@@ -18,8 +20,6 @@ class TagField(CharField):
1820
def __init__(self, *args, **kwargs):
1921
kwargs['max_length'] = kwargs.get('max_length', 255)
2022
kwargs['blank'] = kwargs.get('blank', True)
21-
kwargs['default'] = kwargs.get('default', '')
22-
self._initialized = False
2323
super(TagField, self).__init__(*args, **kwargs)
2424

2525
def contribute_to_class(self, cls, name):
@@ -54,31 +54,34 @@ class Link(models.Model):
5454
if instance is None:
5555
return edit_string_for_tags(Tag.objects.usage_for_model(owner))
5656

57+
tags = self._get_instance_tag_cache(instance)
58+
if tags is None:
59+
if instance.pk is None:
60+
self._set_instance_tag_cache(instance, '')
61+
else:
62+
self._set_instance_tag_cache(
63+
instance, edit_string_for_tags(
64+
Tag.objects.get_for_object(instance)))
5765
return self._get_instance_tag_cache(instance)
5866

5967
def __set__(self, instance, value):
6068
"""
6169
Set an object's tags.
6270
"""
6371
if instance is None:
64-
raise AttributeError(_('%s can only be set on instances.') % self.name)
72+
raise AttributeError(
73+
_('%s can only be set on instances.') % self.name)
6574
if settings.FORCE_LOWERCASE_TAGS and value is not None:
6675
value = value.lower()
6776
self._set_instance_tag_cache(instance, value)
6877

69-
def _save(self, **kwargs): #signal, sender, instance):
78+
def _save(self, **kwargs): # signal, sender, instance):
7079
"""
7180
Save tags back to the database
7281
"""
7382
tags = self._get_instance_tag_cache(kwargs['instance'])
74-
Tag.objects.update_tags(kwargs['instance'], tags)
75-
76-
def _update(self, **kwargs): #signal, sender, instance):
77-
"""
78-
Update tag cache from TaggedItem objects.
79-
"""
80-
instance = kwargs['instance']
81-
self._update_instance_tag_cache(instance)
83+
if tags is not None:
84+
Tag.objects.update_tags(kwargs['instance'], tags)
8285

8386
def __delete__(self, instance):
8487
"""
@@ -90,31 +93,24 @@ def _get_instance_tag_cache(self, instance):
9093
"""
9194
Helper: get an instance's tag cache.
9295
"""
93-
if not self._initialized:
94-
self._initialized = True
95-
self._update(instance=instance)
9696
return getattr(instance, '_%s_cache' % self.attname, None)
9797

9898
def _set_instance_tag_cache(self, instance, tags):
9999
"""
100100
Helper: set an instance's tag cache.
101101
"""
102+
# The next instruction does nothing particular,
103+
# but needed to by-pass the deferred fields system
104+
# when saving an instance, which check the keys present
105+
# in instance.__dict__.
106+
# The issue is introducted in Django 1.10
107+
instance.__dict__[self.attname] = tags
102108
setattr(instance, '_%s_cache' % self.attname, tags)
103109

104-
def _update_instance_tag_cache(self, instance):
105-
"""
106-
Helper: update an instance's tag cache from actual Tags.
107-
"""
108-
# for an unsaved object, leave the default value alone
109-
if instance.pk is not None:
110-
tags = edit_string_for_tags(Tag.objects.get_for_object(instance))
111-
self._set_instance_tag_cache(instance, tags)
112-
113110
def get_internal_type(self):
114111
return 'CharField'
115112

116113
def formfield(self, **kwargs):
117-
from tagging import forms
118-
defaults = {'form_class': forms.TagField}
114+
defaults = {'form_class': TagFormField}
119115
defaults.update(kwargs)
120116
return super(TagField, self).formfield(**defaults)

tagging/forms.py

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
"""
2-
Tagging components for Django's form library.
2+
Form components for tagging.
33
"""
44
from django import forms
55
from django.utils.translation import ugettext as _
@@ -8,33 +8,30 @@
88
from tagging.models import Tag
99
from tagging.utils import parse_tag_input
1010

11+
1112
class TagAdminForm(forms.ModelForm):
1213
class Meta:
1314
model = Tag
15+
fields = ('name',)
1416

1517
def clean_name(self):
1618
value = self.cleaned_data['name']
1719
tag_names = parse_tag_input(value)
1820
if len(tag_names) > 1:
1921
raise forms.ValidationError(_('Multiple tags were given.'))
20-
elif len(tag_names[0]) > settings.MAX_TAG_LENGTH:
21-
raise forms.ValidationError(
22-
_('A tag may be no more than %s characters long.') %
23-
settings.MAX_TAG_LENGTH)
2422
return value
2523

24+
2625
class TagField(forms.CharField):
2726
"""
2827
A ``CharField`` which validates that its input is a valid list of
2928
tag names.
3029
"""
3130
def clean(self, value):
3231
value = super(TagField, self).clean(value)
33-
if value == u'':
34-
return value
3532
for tag_name in parse_tag_input(value):
3633
if len(tag_name) > settings.MAX_TAG_LENGTH:
3734
raise forms.ValidationError(
3835
_('Each tag may be no more than %s characters long.') %
39-
settings.MAX_TAG_LENGTH)
36+
settings.MAX_TAG_LENGTH)
4037
return value

tagging/generic.py

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
1+
"""
2+
Generic components for tagging.
3+
"""
14
from django.contrib.contenttypes.models import ContentType
25

6+
37
def fetch_content_objects(tagged_items, select_related_for=None):
48
"""
59
Retrieves ``ContentType`` and content objects for the given list of
@@ -15,7 +19,8 @@ def fetch_content_objects(tagged_items, select_related_for=None):
1519
``ContentType``) for which ``select_related`` should be used when
1620
retrieving model instances.
1721
"""
18-
if select_related_for is None: select_related_for = []
22+
if select_related_for is None:
23+
select_related_for = []
1924

2025
# Group content object pks by their content type pks
2126
objects = {}
@@ -27,9 +32,11 @@ def fetch_content_objects(tagged_items, select_related_for=None):
2732
for content_type_pk, object_pks in objects.iteritems():
2833
model = content_types[content_type_pk].model_class()
2934
if content_types[content_type_pk].model in select_related_for:
30-
objects[content_type_pk] = model._default_manager.select_related().in_bulk(object_pks)
35+
objects[content_type_pk] = model._default_manager.select_related(
36+
).in_bulk(object_pks)
3137
else:
32-
objects[content_type_pk] = model._default_manager.in_bulk(object_pks)
38+
objects[content_type_pk] = model._default_manager.in_bulk(
39+
object_pks)
3340

3441
# Set content types and content objects in the appropriate cache
3542
# attributes, so accessing the 'content_type' and 'object'

tagging/managers.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,18 @@
11
"""
2-
Custom managers for Django models registered with the tagging
3-
application.
2+
Custom managers for tagging.
43
"""
5-
from django.contrib.contenttypes.models import ContentType
64
from django.db import models
5+
from django.contrib.contenttypes.models import ContentType
6+
7+
from tagging.models import Tag
8+
from tagging.models import TaggedItem
79

8-
from tagging.models import Tag, TaggedItem
910

1011
class ModelTagManager(models.Manager):
1112
"""
1213
A manager for retrieving tags for a particular model.
1314
"""
14-
def get_query_set(self):
15+
def get_queryset(self):
1516
ctype = ContentType.objects.get_for_model(self.model)
1617
return Tag.objects.filter(
1718
items__content_type__pk=ctype.pk).distinct()
@@ -25,6 +26,7 @@ def related(self, tags, *args, **kwargs):
2526
def usage(self, *args, **kwargs):
2627
return Tag.objects.usage_for_model(self.model, *args, **kwargs)
2728

29+
2830
class ModelTaggedItemManager(models.Manager):
2931
"""
3032
A manager for retrieving model instances based on their tags.
@@ -47,6 +49,7 @@ def with_any(self, tags, queryset=None):
4749
else:
4850
return TaggedItem.objects.get_union_by_model(queryset, tags)
4951

52+
5053
class TagDescriptor(object):
5154
"""
5255
A descriptor which provides access to a ``ModelTagManager`` for

tagging/migrations/0001_initial.py

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
from django.db import models
2+
from django.db import migrations
3+
4+
5+
class Migration(migrations.Migration):
6+
7+
dependencies = [
8+
('contenttypes', '0001_initial'),
9+
]
10+
11+
operations = [
12+
migrations.CreateModel(
13+
name='Tag',
14+
fields=[
15+
('id', models.AutoField(
16+
verbose_name='ID', serialize=False,
17+
auto_created=True, primary_key=True)),
18+
('name', models.CharField(
19+
unique=True, max_length=50,
20+
verbose_name='name', db_index=True)),
21+
],
22+
options={
23+
'ordering': ('name',),
24+
'verbose_name': 'tag',
25+
'verbose_name_plural': 'tags',
26+
},
27+
),
28+
migrations.CreateModel(
29+
name='TaggedItem',
30+
fields=[
31+
('id', models.AutoField(
32+
verbose_name='ID', serialize=False,
33+
auto_created=True, primary_key=True)),
34+
('object_id', models.PositiveIntegerField(
35+
verbose_name='object id', db_index=True)),
36+
('content_type', models.ForeignKey(
37+
verbose_name='content type',
38+
on_delete=models.SET_NULL,
39+
to='contenttypes.ContentType')),
40+
('tag', models.ForeignKey(
41+
related_name='items', verbose_name='tag',
42+
on_delete=models.SET_NULL,
43+
to='tagging.Tag')),
44+
],
45+
options={
46+
'verbose_name': 'tagged item',
47+
'verbose_name_plural': 'tagged items',
48+
},
49+
),
50+
migrations.AlterUniqueTogether(
51+
name='taggeditem',
52+
unique_together=set([('tag', 'content_type', 'object_id')]),
53+
),
54+
]

0 commit comments

Comments
 (0)