summaryrefslogtreecommitdiff
path: root/lib/taggit/managers.py
diff options
context:
space:
mode:
Diffstat (limited to 'lib/taggit/managers.py')
-rw-r--r--lib/taggit/managers.py244
1 files changed, 0 insertions, 244 deletions
diff --git a/lib/taggit/managers.py b/lib/taggit/managers.py
deleted file mode 100644
index 55201b4..0000000
--- a/lib/taggit/managers.py
+++ /dev/null
@@ -1,244 +0,0 @@
-from django.contrib.contenttypes.generic import GenericRelation
-from django.contrib.contenttypes.models import ContentType
-from django.db import models
-from django.db.models.fields.related import ManyToManyRel, RelatedField, add_lazy_relation
-from django.db.models.related import RelatedObject
-from django.utils.text import capfirst
-from django.utils.translation import ugettext_lazy as _
-
-from taggit.forms import TagField
-from taggit.models import TaggedItem, GenericTaggedItemBase
-from taggit.utils import require_instance_manager
-
-
-try:
- all
-except NameError:
- # 2.4 compat
- try:
- from django.utils.itercompat import all
- except ImportError:
- # 1.1.X compat
- def all(iterable):
- for item in iterable:
- if not item:
- return False
- return True
-
-
-class TaggableRel(ManyToManyRel):
- def __init__(self):
- self.related_name = None
- self.limit_choices_to = {}
- self.symmetrical = True
- self.multiple = True
- self.through = None
-
-
-class TaggableManager(RelatedField):
- def __init__(self, verbose_name=_("Tags"),
- help_text=_("A comma-separated list of tags."), through=None, blank=False):
- self.through = through or TaggedItem
- self.rel = TaggableRel()
- self.verbose_name = verbose_name
- self.help_text = help_text
- self.blank = blank
- self.editable = True
- self.unique = False
- self.creates_table = False
- self.db_column = None
- self.choices = None
- self.serialize = False
- self.null = True
- self.creation_counter = models.Field.creation_counter
- models.Field.creation_counter += 1
-
- def __get__(self, instance, model):
- if instance is not None and instance.pk is None:
- raise ValueError("%s objects need to have a primary key value "
- "before you can access their tags." % model.__name__)
- manager = _TaggableManager(
- through=self.through, model=model, instance=instance
- )
- return manager
-
- def contribute_to_class(self, cls, name):
- self.name = self.column = name
- self.model = cls
- cls._meta.add_field(self)
- setattr(cls, name, self)
- if not cls._meta.abstract:
- if isinstance(self.through, basestring):
- def resolve_related_class(field, model, cls):
- self.through = model
- self.post_through_setup(cls)
- add_lazy_relation(
- cls, self, self.through, resolve_related_class
- )
- else:
- self.post_through_setup(cls)
-
- def post_through_setup(self, cls):
- self.use_gfk = (
- self.through is None or issubclass(self.through, GenericTaggedItemBase)
- )
- self.rel.to = self.through._meta.get_field("tag").rel.to
- if self.use_gfk:
- tagged_items = GenericRelation(self.through)
- tagged_items.contribute_to_class(cls, "tagged_items")
-
- def save_form_data(self, instance, value):
- getattr(instance, self.name).set(*value)
-
- def formfield(self, form_class=TagField, **kwargs):
- defaults = {
- "label": capfirst(self.verbose_name),
- "help_text": self.help_text,
- "required": not self.blank
- }
- defaults.update(kwargs)
- return form_class(**defaults)
-
- def value_from_object(self, instance):
- if instance.pk:
- return self.through.objects.filter(**self.through.lookup_kwargs(instance))
- return self.through.objects.none()
-
- def related_query_name(self):
- return self.model._meta.module_name
-
- def m2m_reverse_name(self):
- return self.through._meta.get_field_by_name("tag")[0].column
-
- def m2m_target_field_name(self):
- return self.model._meta.pk.name
-
- def m2m_reverse_target_field_name(self):
- return self.rel.to._meta.pk.name
-
- def m2m_column_name(self):
- if self.use_gfk:
- return self.through._meta.virtual_fields[0].fk_field
- return self.through._meta.get_field('content_object').column
-
- def db_type(self, connection=None):
- return None
-
- def m2m_db_table(self):
- return self.through._meta.db_table
-
- def extra_filters(self, pieces, pos, negate):
- if negate or not self.use_gfk:
- return []
- prefix = "__".join(["tagged_items"] + pieces[:pos-2])
- cts = map(ContentType.objects.get_for_model, _get_subclasses(self.model))
- if len(cts) == 1:
- return [("%s__content_type" % prefix, cts[0])]
- return [("%s__content_type__in" % prefix, cts)]
-
- def bulk_related_objects(self, new_objs, using):
- return []
-
-
-class _TaggableManager(models.Manager):
- def __init__(self, through, model, instance):
- self.through = through
- self.model = model
- self.instance = instance
-
- def get_query_set(self):
- return self.through.tags_for(self.model, self.instance)
-
- def _lookup_kwargs(self):
- return self.through.lookup_kwargs(self.instance)
-
- @require_instance_manager
- def add(self, *tags):
- str_tags = set([
- t
- for t in tags
- if not isinstance(t, self.through.tag_model())
- ])
- tag_objs = set(tags) - str_tags
- # If str_tags has 0 elements Django actually optimizes that to not do a
- # query. Malcolm is very smart.
- existing = self.through.tag_model().objects.filter(
- name__in=str_tags
- )
- tag_objs.update(existing)
-
- for new_tag in str_tags - set(t.name for t in existing):
- tag_objs.add(self.through.tag_model().objects.create(name=new_tag))
-
- for tag in tag_objs:
- self.through.objects.get_or_create(tag=tag, **self._lookup_kwargs())
-
- @require_instance_manager
- def set(self, *tags):
- self.clear()
- self.add(*tags)
-
- @require_instance_manager
- def remove(self, *tags):
- self.through.objects.filter(**self._lookup_kwargs()).filter(
- tag__name__in=tags).delete()
-
- @require_instance_manager
- def clear(self):
- self.through.objects.filter(**self._lookup_kwargs()).delete()
-
- def most_common(self):
- return self.get_query_set().annotate(
- num_times=models.Count(self.through.tag_relname())
- ).order_by('-num_times')
-
- @require_instance_manager
- def similar_objects(self):
- lookup_kwargs = self._lookup_kwargs()
- lookup_keys = sorted(lookup_kwargs)
- qs = self.through.objects.values(*lookup_kwargs.keys())
- qs = qs.annotate(n=models.Count('pk'))
- qs = qs.exclude(**lookup_kwargs)
- qs = qs.filter(tag__in=self.all())
- qs = qs.order_by('-n')
-
- # TODO: This all feels like a bit of a hack.
- items = {}
- if len(lookup_keys) == 1:
- # Can we do this without a second query by using a select_related()
- # somehow?
- f = self.through._meta.get_field_by_name(lookup_keys[0])[0]
- objs = f.rel.to._default_manager.filter(**{
- "%s__in" % f.rel.field_name: [r["content_object"] for r in qs]
- })
- for obj in objs:
- items[(getattr(obj, f.rel.field_name),)] = obj
- else:
- preload = {}
- for result in qs:
- preload.setdefault(result['content_type'], set())
- preload[result["content_type"]].add(result["object_id"])
-
- for ct, obj_ids in preload.iteritems():
- ct = ContentType.objects.get_for_id(ct)
- for obj in ct.model_class()._default_manager.filter(pk__in=obj_ids):
- items[(ct.pk, obj.pk)] = obj
-
- results = []
- for result in qs:
- obj = items[
- tuple(result[k] for k in lookup_keys)
- ]
- obj.similar_tags = result["n"]
- results.append(obj)
- return results
-
-
-def _get_subclasses(model):
- subclasses = [model]
- for f in model._meta.get_all_field_names():
- field = model._meta.get_field_by_name(f)[0]
- if (isinstance(field, RelatedObject) and
- getattr(field.field.rel, "parent_link", None)):
- subclasses.extend(_get_subclasses(field.model))
- return subclasses