def testBasicTagging(self): dead = Parrot.objects.create(state='dead') Tag.objects.update_tags(dead, 'foo,bar,"ter"') self.assertListsEqual(get_tag_list('bar foo ter'), Tag.objects.get_for_object(dead)) Tag.objects.update_tags(dead, '"foo" bar "baz"') self.assertListsEqual(get_tag_list('bar baz foo'), Tag.objects.get_for_object(dead)) Tag.objects.add_tag(dead, 'foo') self.assertListsEqual(get_tag_list('bar baz foo'), Tag.objects.get_for_object(dead)) Tag.objects.add_tag(dead, 'zip') self.assertListsEqual(get_tag_list('bar baz foo zip'), Tag.objects.get_for_object(dead)) self.assertRaises(AttributeError, Tag.objects.add_tag, dead, ' ') self.assertRaises(AttributeError, Tag.objects.add_tag, dead, 'one two') Tag.objects.update_tags(dead, 'ŠĐĆŽćžšđ') self.assertEqual( '[<Tag: \xc5\xa0\xc4\x90\xc4\x86\xc5\xbd\xc4\x87\xc5\xbe\xc5\xa1\xc4\x91>]', repr(Tag.objects.get_for_object(dead))) Tag.objects.update_tags(dead, None) self.assertListsEqual([], Tag.objects.get_for_object(dead))
def relate(self, tags, relation_type='~', related_tags=[], force_create=False): ''' Relates each tag in a list of tags with each tag in a list of related_tags with the given relation type. Tag lists can be Tag instances or strings. Relations are created symmetrically. If force_create = True, tags are created from string if they do not already exist. If just a list of tags are given, it calls relate_all() to relate them with each other using '~' relation. Updates existing relations if needed. ''' #TODO: WTF tags = get_tag_list(tags) if related_tags == []: self.relate_all(tags) else: related_tags = get_tag_list(related_tags) for tag in tags: tag = get_tag(tag) if tag and tag.is_valid: for related_tag in related_tags: related_tag = get_tag(related_tag) if related_tag and related_tag.is_valid: if tag != related_tag: rel, c = RelatedTag.objects.get_or_create(tag=tag, related_tag=related_tag, defaults={'relation_type': relation_type, 'count': 1}) if not c: rel.count += 1 # check if the existing relation is correct if rel.relation_type != relation_type: rel.relation_type = relation_type rel.save()
def test_with_invalid_input_mix_of_string_and_instance(self): try: get_tag_list(["cheese", self.toast]) except ValueError, ve: self.assertEquals( str(ve), "If a list or tuple of tags is provided, they must all be tag names, Tag objects or Tag ids." )
def testForcingTagsToLowercase(self): settings.FORCE_LOWERCASE_TAGS = True dead = Parrot.objects.create(state='dead') Tag.objects.update_tags(dead, 'foO bAr Ter') self.assertListsEqual(get_tag_list('bar foo ter'), Tag.objects.get_for_object(dead)) Tag.objects.update_tags(dead, 'foO bAr baZ') self.assertListsEqual(get_tag_list('bar baz foo'), Tag.objects.get_for_object(dead)) Tag.objects.add_tag(dead, 'FOO') self.assertListsEqual(get_tag_list('bar baz foo'), Tag.objects.get_for_object(dead)) Tag.objects.add_tag(dead, 'Zip') self.assertListsEqual(get_tag_list('bar baz foo zip'), Tag.objects.get_for_object(dead)) Tag.objects.update_tags(dead, None) f1 = FormTest.objects.create(tags=u'test3 test2 test1') f1.tags = u'TEST5' f1.save() self.assertListsEqual(get_tag_list('test5'), Tag.objects.get_for_object(f1)) self.assertEqual(u'test5', f1.tags)
def test_with_invalid_input_mix_of_string_and_instance(self): try: get_tag_list(['cheese', self.toast]) except ValueError, ve: self.assertEquals( str(ve), 'If a list or tuple of tags is provided, they must all be tag names, Tag objects or Tag ids.' )
def testUsingAModelsTagField(self): f1 = FormTest.objects.create(tags=u'test3 test2 test1') self.assertListsEqual(get_tag_list('test1 test2 test3'), Tag.objects.get_for_object(f1)) f1.tags = u'test4' f1.save() self.assertListsEqual(get_tag_list('test4'), Tag.objects.get_for_object(f1)) f1.tags = '' f1.save() self.assertListsEqual([], Tag.objects.get_for_object(f1))
def test_with_invalid_input(self): try: get_tag_list(29) except ValueError as ve: self.assertEqual(str(ve), 'The tag input given was invalid.') except Exception as e: raise self.failureException('the wrong type of exception was raised: type [%s] value [%s]' %\ (str(type(e)), str(e))) else: raise self.failureException('a ValueError exception was supposed to be raised!')
def test_with_invalid_input(self): try: get_tag_list(29) except ValueError as ve: self.assertEqual(str(ve), 'The tag input given was invalid.') except Exception as e: print('--', e) raise self.failureException('the wrong type of exception was raised: type [%s] value [%s]' % \ (str(type(e)), str(e))) else: raise self.failureException('a ValueError exception was supposed to be raised!')
def test_with_invalid_input_mix_of_string_and_instance(self): try: get_tag_list(['cheese', self.toast]) except ValueError as ve: self.assertEqual(str(ve), 'If a list or tuple of tags is provided, they must all be tag names, Tag objects or Tag ids.') except Exception as e: raise self.failureException('the wrong type of exception was raised: type [%s] value [%]' %\ (str(type(e)), str(e))) else: raise self.failureException('a ValueError exception was supposed to be raised!')
def test_with_invalid_input_mix_of_string_and_instance(self): try: get_tag_list(['cheese', self.toast]) except ValueError as ve: self.assertEqual(str(ve), 'If a list or tuple of tags is provided, they must all be tag names, Tag objects or Tag ids.') except Exception as e: raise self.failureException('the wrong type of exception was raised: type [%s] value [%]' % \ (str(type(e)), str(e))) else: raise self.failureException('a ValueError exception was supposed to be raised!')
def test_with_invalid_input(self): try: get_tag_list(29) except ValueError as ve: self.assertEqual(str(ve), "The tag input given was invalid.") except Exception as e: print("--", e) raise self.failureException( "the wrong type of exception was raised: " "type [%s] value [%s]" % (str(type(e)), str(e)) ) else: raise self.failureException("a ValueError exception was supposed to be raised!")
def testNormalisedTagListInput(self): cheese = Tag.objects.create(name='cheese') toast = Tag.objects.create(name='toast') self.assertListsEqual([cheese], get_tag_list(cheese)) self.assertListsEqual([cheese, toast], get_tag_list('cheese toast')) self.assertListsEqual([cheese, toast], get_tag_list('cheese,toast')) self.assertListsEqual([], get_tag_list([])) self.assertListsEqual([cheese, toast], get_tag_list(['cheese', 'toast'])) self.assertListsEqual([cheese, toast], get_tag_list([cheese.id, toast.id])) self.assertListsEqual([cheese, toast], get_tag_list(['cheese', 'toast', 'ŠĐĆŽćžšđ'])) self.assertListsEqual([cheese, toast], get_tag_list([cheese, toast])) self.assertEqual((cheese, toast), get_tag_list((cheese, toast))) self.assertListsEqual([cheese, toast], get_tag_list(Tag.objects.filter(name__in=['cheese', 'toast']))) self.assertRaises(ValueError, get_tag_list, ['cheese', toast]) self.assertRaises(ValueError, get_tag_list, 29)
def related_for_model(self, tags, model, counts=False, min_count=None, label=None): """ Obtain a list of tags related to a given list of tags - that is, other tags used by items which have all the given tags. If ``counts`` is True, a ``count`` attribute will be added to each tag, indicating the number of items which have it in addition to the given list of tags. If ``min_count`` is given, only tags which have a ``count`` greater than or equal to ``min_count`` will be returned. Passing a value for ``min_count`` implies ``counts=True``. """ ## Nonrel requires two (maybe three) queries and in-memory aggregation and sorting ## (1) grab all of the object_ids that point to the specified tags, for the model object_ids = TaggedItem.objects._get_intersection_object_ids(model, tags) ## (2) grab all of the TaggedItems that point to the same objects content_type = ContentType.objects.get_for_model(model) related = TaggedItem._default_manager.filter(object_id__in=object_ids, content_type=content_type) ## if there are no related TaggedItems at all, then there are no related tags if len(list(related)) == 0: ## TODO: django-nonrel len() bug return [] ## (3) Simulate SQL aggregation in memory, and exclude the original tags. exclude_ids = set() for tag in get_tag_list(tags): #this may, or may not, execute an additional query exclude_ids.add(tag.id) return self._package_and_sort(related, counts, min_count, exclude_ids)
def edit_product(request, pid): prod = Product.objects.get(pk=pid) if request.method == 'POST': form = ProductForm(request.POST, instance=prod) if form.is_valid(): form.save() tags = request.POST.getlist('tags') t = ", ".join(tags) prod.tags = t messages.add_message(request, messages.SUCCESS, 'Product updated successfully.', extra_tags='alert-success') return HttpResponseRedirect(reverse('view_product', args=(pid, ))) else: form = ProductForm(instance=prod, initial={ 'auth_users': prod.authorized_users.all(), 'tags': get_tag_list(Tag.objects.get_for_object(prod)) }) add_breadcrumb(parent=prod, title="Edit", top_level=False, request=request) return render(request, 'dojo/edit_product.html', { 'form': form, 'product': prod, })
def edit_object(request, pid, ttid): object = Objects.objects.get(pk=ttid) if request.method == 'POST': tform = ObjectSettingsForm(request.POST, instance=object) if tform.is_valid(): tform.save() tags = request.POST.getlist('tags') t = ", ".join(tags) object.tags = t messages.add_message( request, messages.SUCCESS, 'Tool Product Configuration Successfully Updated.', extra_tags='alert-success') return HttpResponseRedirect(reverse('view_objects', args=(pid, ))) else: tform = ObjectSettingsForm( instance=object, initial={'tags': get_tag_list(Tag.objects.get_for_object(object))}) tform.initial['tags'] = [tag.name for tag in object.tags] add_breadcrumb(title="Edit Tracked Files", top_level=False, request=request) return render(request, 'dojo/edit_object.html', { 'tform': tform, })
def render(self, request, place, content, context, *args, **kwargs): if content and content.tags: content_tags = [i.name for i in get_tag_list(content.tags)] taglist = ITag.objects.filter(name__in=content_tags) return self.render_block(request, template_name='itags/blocks/content_tags.html', context={'taglist': taglist}) return ''
def edit_object(request, pid, ttid): object = Objects.objects.get(pk=ttid) if request.method == 'POST': tform = ObjectSettingsForm(request.POST, instance=object) if tform.is_valid(): tform.save() tags = request.POST.getlist('tags') t = ", ".join(tags) object.tags = t messages.add_message(request, messages.SUCCESS, 'Tool Product Configuration Successfully Updated.', extra_tags='alert-success') return HttpResponseRedirect(reverse('view_objects', args=(pid,))) else: tform = ObjectSettingsForm(instance=object, initial={'tags': get_tag_list(Tag.objects.get_for_object(object))}) tform.initial['tags'] = [tag.name for tag in object.tags] add_breadcrumb(title="Edit Tracked Files", top_level=False, request=request) return render(request, 'dojo/edit_object.html', { 'tform': tform, })
def get_by_model(self, queryset_or_model, tags): """ Create a ``QuerySet`` containing instances of the specified model associated with a given tag or list of tags. """ tags = get_tag_list(tags) tag_count = len(tags) if tag_count == 0: # No existing tags were given queryset, model = get_queryset_and_model(queryset_or_model) return model._default_manager.none() elif tag_count == 1: # Optimisation for single tag - fall through to the simpler # query below. tag = tags[0] else: return self.get_intersection_by_model(queryset_or_model, tags) queryset, model = get_queryset_and_model(queryset_or_model) content_type = ContentType.objects.get_for_model(model) opts = self.model._meta tagged_item_table = qn(opts.db_table) return queryset.extra( tables=[opts.db_table], where=[ '%s.content_type_id = %%s' % tagged_item_table, '%s.tag_id = %%s' % tagged_item_table, '%s.%s = %s.object_id' % (qn(model._meta.db_table), qn(model._meta.pk.column), tagged_item_table) ], params=[content_type.pk, tag.pk], )
def tag_search(request, tag, page=1, paginate_by=10, rtemplate="contacts/tag_results.html"): qstagquery = tagutils.get_tag_list(tag) taggedcontacts = TaggedItem.objects.get_by_model(Contact, [tag.name for tag in qstagquery]) qscontacts = Contact.objects.filter(id__in = [c.id for c in taggedcontacts]) request.session['searchresults'] = qscontacts return HttpResponseRedirect(reverse('contacts-searchresults'))
def get_by_model(self, queryset_or_model, tags): """ Create a ``QuerySet`` containing instances of the specified model associated with a given tag or list of tags. """ tags = get_tag_list(tags) tag_count = len(tags) if tag_count == 0: # No existing tags were given queryset, model = get_queryset_and_model(queryset_or_model) return model._default_manager.none() elif tag_count == 1: # Optimisation for single tag - fall through to the simpler # query below. tag = tags[0] else: return self.get_intersection_by_model(queryset_or_model, tags) queryset, model = get_queryset_and_model(queryset_or_model) content_type = ContentType.objects.get_for_model(model) opts = self.model._meta tagged_item_table = qn(opts.db_table) return queryset.extra( tables=[opts.db_table], where=[ '%s.content_type_id = %%s' % tagged_item_table, '%s.tag_id = %%s' % tagged_item_table, '%s.%s = %s.object_id' % (qn(model._meta.db_table), qn( model._meta.pk.column), tagged_item_table) ], params=[content_type.pk, tag.pk], )
def get_union_by_model(self, queryset_or_model, tags): """ Create a ``QuerySet`` containing instances of the specified model associated with *any* of the given list of tags. """ tags = get_tag_list(tags) tag_count = len(tags) queryset, model = get_queryset_and_model(queryset_or_model) model_table = qn(model._meta.db_table) # This query selects the ids of all objects which have any of # the given tags. query = """ SELECT %(model_pk)s FROM %(model)s, %(tagged_item)s WHERE %(tagged_item)s.content_type_id = %(content_type_id)s AND %(tagged_item)s.tag_id IN (%(tag_id_placeholders)s) AND %(model_pk)s = %(tagged_item)s.object_id GROUP BY %(model_pk)s""" % { 'model_pk': '%s.%s' % (model_table, qn(model._meta.pk.column)), 'model': model_table, 'tagged_item': qn(self.model._meta.db_table), 'content_type_id': ContentType.objects.get_for_model(model).pk, 'tag_id_placeholders': ','.join(['%s'] * tag_count), } cursor = connection.cursor() cursor.execute(query, [tag.pk for tag in tags]) object_ids = [row[0] for row in cursor.fetchall()] if len(object_ids) > 0: return queryset.filter(pk__in=object_ids) else: return model._default_manager.none()
def edit_product(request, pid): prod = Product.objects.get(pk=pid) jira_enabled = True jira_inst = None try: jira_inst = JIRA_PKey.objects.get(product=prod) except: jira_enabled = False pass if request.method == 'POST': form = ProductForm(request.POST, instance=prod) if form.is_valid(): form.save() tags = request.POST.getlist('tags') t = ", ".join(tags) prod.tags = t messages.add_message(request, messages.SUCCESS, 'Product updated successfully.', extra_tags='alert-success') if hasattr(settings, 'ENABLE_JIRA'): if settings.ENABLE_JIRA: if jira_enabled: jform = JIRAPKeyForm(request.POST, instance=jira_inst) else: jform = JIRAPKeyForm(request.POST) new_conf = jform.save(commit=False) new_conf.product_id = pid new_conf.save() messages.add_message( request, messages.SUCCESS, 'JIRA information updated successfully.', extra_tags='alert-success') return HttpResponseRedirect(reverse('view_product', args=(pid, ))) else: form = ProductForm(instance=prod, initial={ 'auth_users': prod.authorized_users.all(), 'tags': get_tag_list(Tag.objects.get_for_object(prod)) }) if hasattr(settings, 'ENABLE_JIRA'): if settings.ENABLE_JIRA: if jira_enabled: jform = JIRAPKeyForm(instance=jira_inst) else: jform = JIRAPKeyForm() else: jform = None add_breadcrumb(parent=prod, title="Edit", top_level=False, request=request) return render(request, 'dojo/edit_product.html', { 'form': form, 'jform': jform, 'product': prod, })
def related_for_model(self, tags, model, counts=False, min_count=None, wildcard=None, default_namespace=None): """ Obtain a list of tags related to a given list of tags - that is, other tags used by items which have all the given tags. If ``counts`` is True, a ``count`` attribute will be added to each tag, indicating the number of items which have it in addition to the given list of tags. If ``min_count`` is given, only tags which have a ``count`` greater than or equal to ``min_count`` will be returned. Passing a value for ``min_count`` implies ``counts=True``. """ if min_count is not None: counts = True tags = get_tag_list(tags, wildcard=wildcard, default_namespace=default_namespace) tag_count = len(tags) tagged_item_table = qn(TaggedItem._meta.db_table) query = """ SELECT %(tag)s.id, %(tag)s.namespace, %(tag)s.name, %(tag)s.value%(count_sql)s FROM %(tagged_item)s INNER JOIN %(tag)s ON %(tagged_item)s.tag_id = %(tag)s.id WHERE %(tagged_item)s.content_type_id = %(content_type_id)s AND %(tagged_item)s.object_id IN ( SELECT %(tagged_item)s.object_id FROM %(tagged_item)s, %(tag)s WHERE %(tagged_item)s.content_type_id = %(content_type_id)s AND %(tag)s.id = %(tagged_item)s.tag_id AND %(tag)s.id IN (%(tag_id_placeholders)s) GROUP BY %(tagged_item)s.object_id HAVING COUNT(%(tagged_item)s.object_id) = %(tag_count)s ) AND %(tag)s.id NOT IN (%(tag_id_placeholders)s) GROUP BY %(tag)s.id, %(tag)s.namespace, %(tag)s.name, %(tag)s.value %(min_count_sql)s ORDER BY %(tag)s.name ASC""" % { 'tag': qn(self.model._meta.db_table), 'count_sql': counts and ', COUNT(%s.object_id)' % tagged_item_table or '', 'tagged_item': tagged_item_table, 'content_type_id': ContentType.objects.get_for_model(model).pk, 'tag_id_placeholders': ','.join(['%s'] * tag_count), 'tag_count': tag_count, 'min_count_sql': min_count is not None and ('HAVING COUNT(%s.object_id) >= %%s' % tagged_item_table) or '', } params = [tag.pk for tag in tags] * 2 if min_count is not None: params.append(min_count) cursor = connection.cursor() cursor.execute(query, params) related = [] for row in cursor.fetchall(): tag = self.model(*row[:4]) if counts is True: tag.count = row[4] related.append(tag) return related
def related_for_model(self, tags, model, counts=False, min_count=None): """ Obtain a list of tags related to a given list of tags - that is, other tags used by items which have all the given tags. If ``counts`` is True, a ``count`` attribute will be added to each tag, indicating the number of items which have it in addition to the given list of tags. If ``min_count`` is given, only tags which have a ``count`` greater than or equal to ``min_count`` will be returned. Passing a value for ``min_count`` implies ``counts=True``. """ if min_count is not None: counts = True tags = get_tag_list(tags) tag_count = len(tags) tagged_item_table = qn(TaggedItem._meta.db_table) query = """ SELECT %(tag)s.id%(count_sql)s FROM %(tagged_item)s INNER JOIN %(tag)s ON %(tagged_item)s.tag_id = %(tag)s.id WHERE %(tagged_item)s.content_type_id = %(content_type_id)s AND %(tagged_item)s.object_id IN ( SELECT %(tagged_item)s.object_id FROM %(tagged_item)s, %(tag)s WHERE %(tagged_item)s.content_type_id = %(content_type_id)s AND %(tag)s.id = %(tagged_item)s.tag_id AND %(tag)s.id IN (%(tag_id_placeholders)s) GROUP BY %(tagged_item)s.object_id HAVING COUNT(%(tagged_item)s.object_id) = %(tag_count)s ) AND %(tag)s.id NOT IN (%(tag_id_placeholders)s) GROUP BY %(tag)s.id %(min_count_sql)s""" % { "tag": qn(self.model._meta.db_table), "count_sql": counts and ", COUNT(%s.object_id)" % tagged_item_table or "", "tagged_item": tagged_item_table, "content_type_id": ContentType.objects.get_for_model(model).pk, "tag_id_placeholders": ",".join(["%s"] * tag_count), "tag_count": tag_count, "min_count_sql": min_count is not None and ("HAVING COUNT(%s.object_id) >= %%s" % tagged_item_table) or "", } params = [tag.pk for tag in tags] * 2 if min_count is not None: params.append(min_count) cursor = connection.cursor() cursor.execute(query, params) related = [] for row in cursor.fetchall(): tag = self.model.objects.get(pk=row[0]) if counts is True: tag.count = row[1] related.append(tag) related.sort() return related
def related_for_model(self, tags, model, counts=False, min_count=None): """ Obtain a list of tags related to a given list of tags - that is, other tags used by items which have all the given tags. If ``counts`` is True, a ``count`` attribute will be added to each tag, indicating the number of items which have it in addition to the given list of tags. If ``min_count`` is given, only tags which have a ``count`` greater than or equal to ``min_count`` will be returned. Passing a value for ``min_count`` implies ``counts=True``. """ from tagging.models import TaggedItem if min_count is not None: counts = True tags = get_tag_list(tags) tag_count = len(tags) tagged_item_table = qn(TaggedItem._meta.db_table) query = """ SELECT %(tag)s.id, %(tag)s.name%(count_sql)s FROM %(tagged_item)s INNER JOIN %(tag)s ON %(tagged_item)s.tag_id = %(tag)s.id WHERE %(tagged_item)s.content_type_id = %(content_type_id)s AND %(tagged_item)s.object_id IN ( SELECT %(tagged_item)s.object_id FROM %(tagged_item)s, %(tag)s WHERE %(tagged_item)s.content_type_id = %(content_type_id)s AND %(tag)s.id = %(tagged_item)s.tag_id AND %(tag)s.id IN (%(tag_id_placeholders)s) GROUP BY %(tagged_item)s.object_id HAVING COUNT(%(tagged_item)s.object_id) = %(tag_count)s ) AND %(tag)s.id NOT IN (%(tag_id_placeholders)s) GROUP BY %(tag)s.id, %(tag)s.name %(min_count_sql)s ORDER BY %(tag)s.name ASC""" % { 'tag': qn(self.model._meta.db_table), 'count_sql': counts and ', COUNT(%s.object_id)' % tagged_item_table or '', 'tagged_item': tagged_item_table, 'content_type_id': ContentType.objects.get_for_model(model).pk, 'tag_id_placeholders': ','.join(['%s'] * tag_count), 'tag_count': tag_count, 'min_count_sql': min_count is not None and ('HAVING COUNT(%s.object_id) >= %%s' % tagged_item_table) or '', } params = [tag.pk for tag in tags] * 2 if min_count is not None: params.append(min_count) cursor = connection.cursor() cursor.execute(query, params) related = [] for row in cursor.fetchall(): tag = self.model(id=row[0], name=row[1]) if counts is True: tag.count = row[2] related.append(tag) return related
def delete_story_tags(sender, instance, **kwargs): ctype = ContentType.objects.get_for_model(instance) tags = get_tag_list(instance.tags) TaggedItem._default_manager.filter(content_type__pk=ctype.pk, object_id=instance.pk, tag__in=tags).delete() for tag in tags: if not tag.items.count(): tag.delete()
def render(self, request, place, content, context, *args, **kwargs): if content and content.tags: content_tags = [i.name for i in get_tag_list(content.tags)] taglist = ITag.objects.filter(name__in=content_tags) return self.render_block( request, template_name='itags/blocks/content_tags.html', context={'taglist': taglist}) return ''
def get_related(self, tags, relation_types=['~']): """ Takes a list of tags and returns tags that are related to all of them """ tags = get_tag_list(tags) result_tags = Tag.objects.all().distinct() for tag in tags: result_tags = result_tags & tag.get_related(relation_types=relation_types) return result_tags
def clean(self, value): if self.required and not value: raise ValidationError(self.error_messages['required']) elif not self.required and not value: return [] try: return get_tag_list(value) except ValueError: raise ValidationError(self.error_messages['list'])
def combine_tags(request, template_name="tags/tagcloud.html", form_class=CombineTagsForm): def get_not_deleted_tribe_wikies(): deleted_tribes = Tribe.objects.filter(deleted=True) ctype_tribe = ContentType.objects.get(name="tribe") return WikiArticle.objects.all().filter(content_type=ctype_tribe).exclude(object_id__in=deleted_tribes) # @todo: cache det her query = ( ("phototags", Image.objects.all()), ("bookmarktags", BookmarkInstance.objects.all()), ("tribe_tags", Tribe.objects.all().filter(deleted=False)), ("tribe_topic_tags", TribeTopic.objects.all().filter(tribe__deleted=False)), ("wiki_article_tags", get_not_deleted_tribe_wikies()), ("document_tags", Document.objects.all().filter(tribe__deleted=False)), ) tags = [] if request.method == "POST": combine_form = form_class(request.POST) if combine_form.is_valid(): return_dict = {} tags = combine_form.cleaned_data['tags'] if request.POST["bool"] == "AND": if len(get_tag_list(tags)) == len(tags): # If all of the userinput tags are valid, then there's a # point in searching for objects that contain them all. # Otherwise only the excisting tags would be used when # comparing. for name, thing in query: return_dict[name] = TaggedItem.objects.get_intersection_by_model(thing, tags) elif request.POST["bool"] == "OR": # If either of the tags.. for name, thing in query: return_dict[name] = TaggedItem.objects.get_union_by_model(thing, tags) return_dict.update({ "form": combine_form, "tags": tags, 'tag_cloud' : tagcloud(), }) return render_to_response(template_name, return_dict, context_instance=RequestContext(request)) else: combine_form = form_class() return render_to_response(template_name, { "form": combine_form, "tags": tags, 'tag_cloud' : tagcloud(), }, context_instance=RequestContext(request))
def testNormalisedTagListInput(self): cheese = Tag.objects.create(name='cheese') toast = Tag.objects.create(name='toast') self.assertListsEqual([cheese], get_tag_list(cheese)) self.assertListsEqual([cheese, toast], get_tag_list('cheese toast')) self.assertListsEqual([cheese, toast], get_tag_list('cheese,toast')) self.assertListsEqual([], get_tag_list([])) self.assertListsEqual([cheese, toast], get_tag_list(['cheese', 'toast'])) self.assertListsEqual([cheese, toast], get_tag_list([cheese.id, toast.id])) self.assertListsEqual([cheese, toast], get_tag_list(['cheese', 'toast', 'ŠĐĆŽćžšđ'])) self.assertListsEqual([cheese, toast], get_tag_list([cheese, toast])) self.assertEqual((cheese, toast), get_tag_list((cheese, toast))) self.assertListsEqual( [cheese, toast], get_tag_list(Tag.objects.filter(name__in=['cheese', 'toast']))) self.assertRaises(ValueError, get_tag_list, ['cheese', toast]) self.assertRaises(ValueError, get_tag_list, 29)
def get_intersection_by_model(self, queryset_or_model, tags, include_synonyms=True): """ Create a ``QuerySet`` containing instances of the specified model associated with *all* of the given list of tags. """ tags = get_tag_list(tags) tag_count = len(tags) if not tag_count: return model._default_manager.none() # replace the tags with their preferred synonyms if they exist temp_tags = [] for tag in tags: try: rt = RelatedTag.objects.get(tag=tag, relation_type='=>') except RelatedTag.DoesNotExist: temp_tags.append(tag) else: temp_tags.append(rt.related_tag) # make sure the tags are unique tags = list(set(temp_tags)) queryset, model = get_queryset_and_model(queryset_or_model) model_table = qn(model._meta.db_table) # This query selects the ids of all objects which have all the # given tags. query = """ SELECT %(model_pk)s FROM %(model)s, %(tagged_item)s WHERE %(tagged_item)s.content_type_id = %(content_type_id)s AND %(tagged_item)s.tag_id IN (%(tag_id_placeholders)s) AND %(model_pk)s = %(tagged_item)s.object_id GROUP BY %(model_pk)s HAVING COUNT(%(model_pk)s) = %(tag_count)s""" % { 'model_pk': '%s.%s' % (model_table, qn(model._meta.pk.column)), 'model': model_table, 'tagged_item': qn(self.model._meta.db_table), 'content_type_id': ContentType.objects.get_for_model(model).pk, 'tag_id_placeholders': ','.join(['%s'] * tag_count), 'tag_count': tag_count, } print query, ','.join(['%s'] * tag_count), [tag.pk for tag in tags] cursor = connection.cursor() cursor.execute(query, [tag.pk for tag in tags]) object_ids = [row[0] for row in cursor.fetchall()] if len(object_ids) > 0: return queryset.filter(pk__in=object_ids) else: return model._default_manager.none()
def delete_story_tags(sender, instance, **kwargs): try: ctype = ContentType.objects.get_for_model(instance) tags = get_tag_list(instance.tags) TaggedItem._default_manager.filter(content_type__pk=ctype.pk, object_id=instance.pk, tag__in=tags).delete() for tag in tags: if not tag.items.count(): tag.delete() except Exception, e: # let 'django.request' logger handle the exception raise e
def get_intersection_by_model(self, Model, tags): """ Create a queryset matching instances of the given Model associated with all the given list of Tags. FIXME The query currently used to grab the ids of objects which have all the tags should be all that we need run, using a non-explicit join for the QuerySet returned, as in get_by_model, but there's currently no way to get the required GROUP BY and HAVING clauses into Django's ORM. Once the ORM is capable of this, we should have a solution which requires only a single query and won't have the problem where the number of ids in the IN clause in the QuerySet could exceed the length allowed, as could currently happen. """ tags = get_tag_list(tags) tag_count = len(tags) model_table = backend.quote_name(Model._meta.db_table) # This query selects the ids of all objects which have all the # given tags. query = """ SELECT %(model_pk)s FROM %(model)s, %(tagged_item)s WHERE %(tagged_item)s.content_type_id = %(content_type_id)s AND %(tagged_item)s.tag_id IN (%(tag_id_placeholders)s) AND %(model_pk)s = %(tagged_item)s.object_id GROUP BY %(model_pk)s HAVING COUNT(%(model_pk)s) = %(tag_count)s""" % { 'model_pk': '%s.%s' % (model_table, backend.quote_name(Model._meta.pk.column)), 'model': model_table, 'tagged_item': backend.quote_name(self.model._meta.db_table), 'content_type_id': ContentType.objects.get_for_model(Model).id, 'tag_id_placeholders': ','.join(['%s'] * tag_count), 'tag_count': tag_count, } cursor = connection.cursor() cursor.execute(query, [tag.id for tag in tags]) object_ids = [row[0] for row in cursor.fetchall()] if len(object_ids) > 0: return Model._default_manager.filter(pk__in=object_ids) else: return Model._default_manager.none()
def get_intersection_by_model(self, queryset_or_model, tags, wildcard=None, default_namespace=None): """ Create a ``QuerySet`` containing instances of the specified model associated with *all* of the given list of tags. The ``wildcard`` and the ``default_namespace`` parameters are allowed. For more details see the ``get_tag_list`` function. """ tags = get_tag_list(tags, wildcard=wildcard, default_namespace=default_namespace) tag_count = len(tags) queryset, model = get_queryset_and_model(queryset_or_model) if not tag_count: return model._default_manager.none() model_table = qn(model._meta.db_table) # This query selects the ids of all objects which have all the # given tags. query = """ SELECT %(model_pk)s FROM %(model)s, %(tagged_item)s WHERE %(tagged_item)s.content_type_id = %(content_type_id)s AND %(tagged_item)s.tag_id IN (%(tag_id_placeholders)s) AND %(model_pk)s = %(tagged_item)s.object_id GROUP BY %(model_pk)s HAVING COUNT(%(model_pk)s) = %(tag_count)s""" % { 'model_pk': '%s.%s' % (model_table, qn(model._meta.pk.column)), 'model': model_table, 'tagged_item': qn(self.model._meta.db_table), 'content_type_id': ContentType.objects.get_for_model(model).pk, 'tag_id_placeholders': ','.join(['%s'] * tag_count), 'tag_count': tag_count, } cursor = connection.cursor() cursor.execute(query, [tag.pk for tag in tags]) object_ids = [row[0] for row in cursor.fetchall()] if len(object_ids) > 0: return queryset.filter(pk__in=object_ids) else: return model._default_manager.none()
def tagged_object_list(request, queryset=None, tags=None, related_tags=False, related_tag_counts=True, union=True, **kwargs): """ A thin wrapper around ``django.views.generic.list_detail.object_list`` which creates a ``QuerySet`` containing instances of the given queryset or model tagged with the given tag. In addition to the context variables set up by ``object_list``, a ``tag`` context variable will contain the ``Tag`` instance for the tag. If ``related_tags`` is ``True``, a ``related_tags`` context variable will contain tags related to the given tag for the given model. Additionally, if ``related_tag_counts`` is ``True``, each related tag will have a ``count`` attribute indicating the number of items which have it in addition to the given tag. If ``union`` is ``True``, will return list of objects, which are marked by one of mentioned tags. In other case will return list of object, which are marked by all of mentioned tags. """ tag_instances = get_tag_list(tags) if not tag_instances: raise Http404 if union: qs_func = TaggedItem.objects.get_union_by_model else: qs_func = TaggedItem.objects.get_intersection_by_model if not kwargs.has_key('extra_context'): kwargs['extra_context'] = {} kwargs['extra_context']['union'] = union if tag_instances: queryset = qs_func(queryset, tag_instances) kwargs['extra_context']['tags'] = tag_instances if related_tags: kwargs['extra_context']['related_tags'] = \ Tag.objects.related_for_model(tag_instances, queryset, counts=related_tag_counts) else: queryset = get_queryset_and_model(queryset)[0] return object_list(request, queryset, **kwargs)
def get_intersection_by_model(self, model, tags): """ Create a queryset matching instances of the given Model associated with all the given list of Tags. FIXME The query currently used to grab the ids of objects which have all the tags should be all that we need run, using a non-explicit join for the QuerySet returned, as in get_by_model, but there's currently no way to get the required GROUP BY and HAVING clauses into Django's ORM. Once the ORM is capable of this, we should have a solution which requires only a single query and won't have the problem where the number of ids in the IN clause in the QuerySet could exceed the length allowed, as could currently happen. """ tags = get_tag_list(tags) tag_count = len(tags) model_table = qn(model._meta.db_table) # This query selects the ids of all objects which have all the # given tags. query = """ SELECT %(model_pk)s FROM %(model)s, %(tagged_item)s WHERE %(tagged_item)s.content_type_id = %(content_type_id)s AND %(tagged_item)s.tag_id IN (%(tag_id_placeholders)s) AND %(model_pk)s = %(tagged_item)s.object_id GROUP BY %(model_pk)s HAVING COUNT(%(model_pk)s) = %(tag_count)s""" % { 'model_pk': '%s.%s' % (model_table, qn(model._meta.pk.column)), 'model': model_table, 'tagged_item': qn(self.model._meta.db_table), 'content_type_id': ContentType.objects.get_for_model(model).pk, 'tag_id_placeholders': ','.join(['%s'] * tag_count), 'tag_count': tag_count, } cursor = connection.cursor() cursor.execute(query, [tag.pk for tag in tags]) object_ids = [row[0] for row in cursor.fetchall()] if len(object_ids) > 0: return model._default_manager.filter(pk__in=object_ids) else: return model._default_manager.none()
def relate_all(self, tags): ''' Relates a list of tags with each other (for '~' relations only) ''' tags = get_tag_list(tags) for tag in tags: tag = get_tag(tag) if tag and tag.is_valid: for related_tag in tags: related_tag = get_tag(related_tag) if related_tag and related_tag.is_valid: if tag != related_tag: rel, c = RelatedTag.objects.get_or_create(tag=tag, related_tag=related_tag, defaults={'relation_type': '~', 'count': 1}) if not c: rel.count += 1 rel.save()
def get_intersection(self, user_list, tag_list): tags = get_tag_list(tag_list) tags_id = [int(tag.pk) for tag in tags] tags_id.append(0) tags_id.append(0) user_list.append(0) user_list.append(0) query = """ SELECT DISTINCT flow_flow.id FROM flow_flow INNER JOIN tagging_taggeditem ON flow_flow.id = tagging_taggeditem.object_id WHERE post = 1 AND (user_id IN %(user_list)s OR tag_id IN %(tag_list)s)""" % { 'user_list': tuple(user_list), 'tag_list': tuple(tags_id) } cursor = connection.cursor() cursor.execute(query) object_ids = [row[0] for row in cursor.fetchall()] return self.model.objects.select_related().filter(pk__in = object_ids)
def category(request, slug): category = Category.objects.get(slug=slug) # get posts: first published posts, then filter to findatcat tag, # then any matching category tag qs = Post.objects.published().select_related() qs = TaggedItem.objects.get_by_model(qs, get_tag('Finance Data Catalog')) posts = TaggedItem.objects.get_union_by_model( qs, get_tag_list(category.tag_list())) context = { 'posts': posts, 'links': category.links.all(), 'current_category': category, 'categories': Category.objects.all(), } return render_to_response('findatcat/category.html', context)
def get_by_model(self, queryset_or_model, tags, include_synonyms=True): # TODO: we may remove include synonyms as the preferred synonym is forced when tagging """ Create a ``QuerySet`` containing instances of the specified model associated with a given tag or list of tags. """ tags = get_tag_list(tags) tag_count = len(tags) if tag_count == 0: # No existing tags were given queryset, model = get_queryset_and_model(queryset_or_model) return model._default_manager.none() elif tag_count == 1: # Optimization for single tag - fall through to the simpler # query below. tag = tags[0] if include_synonyms: related_tags = tag.get_related(relation_types=['=', '=>', '<=']) if related_tags.count() > 0: # we have synonyms; 1 tag & n synonyms; return the union return self.get_union_by_model(queryset_or_model, [tag] + list(related_tags)) # No synonyms; go on with the usual way queryset, model = get_queryset_and_model(queryset_or_model) content_type = ContentType.objects.get_for_model(model) db_table = self.model._meta.db_table tagged_item_table = qn(db_table) return queryset.extra( tables=[db_table], where=[ '%s.content_type_id = %%s' % tagged_item_table, '%s.tag_id = %%s' % tagged_item_table, '%s.%s = %s.object_id' % (qn(model._meta.db_table), qn(model._meta.pk.column), tagged_item_table) ], params=[content_type.pk, tag.pk], ) else: # we have multiple tags return self.get_intersection_by_model(queryset_or_model, tags, include_synonyms=include_synonyms)
def get_intersection_by_model(self, queryset_or_model, tags): """ Create a ``QuerySet`` containing instances of the specified model associated with *all* of the given list of tags. """ tags = get_tag_list(tags) tag_count = len(tags) queryset, model = get_queryset_and_model(queryset_or_model) if not tag_count: return model._default_manager.none() model_table = qn(model._meta.db_table) # This query selects the ids of all objects which have all the # given tags. query = """ SELECT %(model_pk)s FROM %(model)s, %(tagged_item)s WHERE %(tagged_item)s.content_type_id = %(content_type_id)s AND %(tagged_item)s.tag_id IN (%(tag_id_placeholders)s) AND %(model_pk)s = %(tagged_item)s.object_id GROUP BY %(model_pk)s HAVING COUNT(%(model_pk)s) = %(tag_count)s""" % { "model_pk": "%s.%s" % (model_table, qn(model._meta.pk.column)), "model": model_table, "tagged_item": qn(self.model._meta.db_table), "content_type_id": ContentType.objects.get_for_model(model).pk, "tag_id_placeholders": ",".join(["%s"] * tag_count), "tag_count": tag_count, } cursor = connection.cursor() cursor.execute(query, [tag.pk for tag in tags]) object_ids = [row[0] for row in cursor.fetchall()] if len(object_ids) > 0: return queryset.filter(pk__in=object_ids) else: return model._default_manager.none()
def get_by_model(self, Model, tags): """ Create a queryset matching instances of the given Model associated with a given Tag or list of Tags. """ tags = get_tag_list(tags) if len(tags) == 1: tag = tags[0] # Optimisation for single tag else: return self.get_intersection_by_model(Model, tags) ctype = ContentType.objects.get_for_model(Model) rel_table = backend.quote_name(self.model._meta.db_table) return Model._default_manager.extra( tables=[self.model._meta.db_table], # Use a non-explicit join where=[ '%s.content_type_id = %%s' % rel_table, '%s.tag_id = %%s' % rel_table, '%s.%s = %s.object_id' % (backend.quote_name(Model._meta.db_table), backend.quote_name(Model._meta.pk.column), rel_table) ], params=[ctype.id, tag.id], )
def get_intersection_by_model(self, queryset_or_model, tags): """ Create a ``QuerySet`` containing instances of the specified model associated with *all* of the given list of tags. """ tags = get_tag_list(tags) tag_count = len(tags) queryset, model = get_queryset_and_model(queryset_or_model) if not tag_count: return model._default_manager.none() model_table = qn(model._meta.db_table) tagged_item_table = qn(self.model._meta.db_table) # This query selects the ids of all objects which have all the # given tags. joins, params = self.get_tagged_item_joins(tags, tagged_item_table, model) query = ''' SELECT %(model_pk)s FROM %(model)s %(joins)s GROUP BY %(model_pk)s ''' % { 'model': model_table, 'model_pk': '%s.%s' % (model_table, qn(model._meta.pk.column)), 'joins': joins, } cursor = connection.cursor() cursor.execute(query, params) object_ids = [row[0] for row in cursor.fetchall()] if len(object_ids) > 0: return queryset.filter(pk__in=object_ids) else: return model._default_manager.none()
def render(self, context, instance, placeholder): """ Render the latest entries """ qs = Entry.published.all() if instance.current_language_only: language = get_language_from_request(context["request"]) kw = get_translation_filter_language(Entry, language) qs = qs.filter(**kw) if instance.tagged: tags = get_tag_list(instance.tagged) qs = TaggedItem.objects.get_by_model(qs , tags) latest = qs[:instance.limit] context.update({ 'instance': instance, 'latest': latest, 'object_list': latest, 'placeholder': placeholder }) return context
def test_comma_delimeted_string_as_input(self): ret = get_tag_list('cheese,toast') self.assertEquals(len(ret), 2) self.failUnless(self.cheese in ret) self.failUnless(self.toast in ret)
def test_single_tag_object_as_input(self): self.assertEquals(get_tag_list(self.cheese), [self.cheese])
def test_with_invalid_input(self): try: get_tag_list(29) except ValueError, ve: self.assertEquals(str(ve), 'The tag input given was invalid.')
def test_with_tag_filter(self): ret = get_tag_list(Tag.objects.filter(name__in=['cheese', 'toast'])) self.assertEquals(len(ret), 2) self.failUnless(self.cheese in ret) self.failUnless(self.toast in ret)
def test_tuple_of_instances(self): ret = get_tag_list((self.cheese, self.toast)) self.assertEquals(len(ret), 2) self.failUnless(self.cheese in ret) self.failUnless(self.toast in ret)