diff options
author | SVN-Git Migration <python-modules-team@lists.alioth.debian.org> | 2015-10-08 11:51:45 -0700 |
---|---|---|
committer | SVN-Git Migration <python-modules-team@lists.alioth.debian.org> | 2015-10-08 11:51:45 -0700 |
commit | 2228968f3d51a3d686adb2839bf43e018432f941 (patch) | |
tree | 9e95992c4b09bbea50336e91a709a775a20fd1e5 /tagging/tests | |
download | python-django-tagging-2228968f3d51a3d686adb2839bf43e018432f941.tar python-django-tagging-2228968f3d51a3d686adb2839bf43e018432f941.tar.gz |
Imported Upstream version 0.1+svn102upstream/0.1+svn102
Diffstat (limited to 'tagging/tests')
-rw-r--r-- | tagging/tests/__init__.py | 0 | ||||
-rw-r--r-- | tagging/tests/models.py | 38 | ||||
-rw-r--r-- | tagging/tests/runtests.py | 8 | ||||
-rw-r--r-- | tagging/tests/settings.py | 27 | ||||
-rw-r--r-- | tagging/tests/tags.txt | 122 | ||||
-rw-r--r-- | tagging/tests/tests.py | 384 |
6 files changed, 579 insertions, 0 deletions
diff --git a/tagging/tests/__init__.py b/tagging/tests/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/tagging/tests/__init__.py diff --git a/tagging/tests/models.py b/tagging/tests/models.py new file mode 100644 index 0000000..86754cd --- /dev/null +++ b/tagging/tests/models.py @@ -0,0 +1,38 @@ +from django.db import models + +from tagging.fields import TagField + +class Perch(models.Model): + size = models.IntegerField() + smelly = models.BooleanField(default=True) + +class Parrot(models.Model): + state = models.CharField(max_length=50) + perch = models.ForeignKey(Perch, null=True) + + def __unicode__(self): + return self.state + + class Meta: + ordering = ['state'] + +class Link(models.Model): + name = models.CharField(max_length=50) + + def __unicode__(self): + return self.name + + class Meta: + ordering = ['name'] + +class Article(models.Model): + name = models.CharField(max_length=50) + + def __unicode__(self): + return self.name + + class Meta: + ordering = ['name'] + +class FormTest(models.Model): + tags = TagField() diff --git a/tagging/tests/runtests.py b/tagging/tests/runtests.py new file mode 100644 index 0000000..ab1d49c --- /dev/null +++ b/tagging/tests/runtests.py @@ -0,0 +1,8 @@ +import os, sys +os.environ['DJANGO_SETTINGS_MODULE'] = 'tagging.tests.settings' + +from django.test.simple import run_tests + +failures = run_tests(None, verbosity=9) +if failures: + sys.exit(failures) diff --git a/tagging/tests/settings.py b/tagging/tests/settings.py new file mode 100644 index 0000000..26e659b --- /dev/null +++ b/tagging/tests/settings.py @@ -0,0 +1,27 @@ +import os +DIRNAME = os.path.dirname(__file__) + +DEFAULT_CHARSET = 'utf-8' + +DATABASE_ENGINE = 'sqlite3' +DATABASE_NAME = os.path.join(DIRNAME, 'tagging_test.db') + +#DATABASE_ENGINE = 'mysql' +#DATABASE_NAME = 'tagging_test' +#DATABASE_USER = 'root' +#DATABASE_PASSWORD = '' +#DATABASE_HOST = 'localhost' +#DATABASE_PORT = '3306' + +#DATABASE_ENGINE = 'postgresql_psycopg2' +#DATABASE_NAME = 'tagging_test' +#DATABASE_USER = 'postgres' +#DATABASE_PASSWORD = '' +#DATABASE_HOST = 'localhost' +#DATABASE_PORT = '5432' + +INSTALLED_APPS = ( + 'django.contrib.contenttypes', + 'tagging', + 'tagging.tests', +) diff --git a/tagging/tests/tags.txt b/tagging/tests/tags.txt new file mode 100644 index 0000000..8543411 --- /dev/null +++ b/tagging/tests/tags.txt @@ -0,0 +1,122 @@ +NewMedia 53 +Website 45 +PR 44 +Status 44 +Collaboration 41 +Drupal 34 +Journalism 31 +Transparency 30 +Theory 29 +Decentralization 25 +EchoChamberProject 24 +OpenSource 23 +Film 22 +Blog 21 +Interview 21 +Political 21 +Worldview 21 +Communications 19 +Conference 19 +Folksonomy 15 +MediaCriticism 15 +Volunteer 15 +Dialogue 13 +InternationalLaw 13 +Rosen 12 +Evolution 11 +KentBye 11 +Objectivity 11 +Plante 11 +ToDo 11 +Advisor 10 +Civics 10 +Roadmap 10 +Wilber 9 +About 8 +CivicSpace 8 +Ecosystem 8 +Choice 7 +Murphy 7 +Sociology 7 +ACH 6 +del.icio.us 6 +IntelligenceAnalysis 6 +Science 6 +Credibility 5 +Distribution 5 +Diversity 5 +Errors 5 +FinalCutPro 5 +Fundraising 5 +Law 5 +PhilosophyofScience 5 +Podcast 5 +PoliticalBias 5 +Activism 4 +Analysis 4 +CBS 4 +DeceptionDetection 4 +Editing 4 +History 4 +RSS 4 +Social 4 +Subjectivity 4 +Vlog 4 +ABC 3 +ALTubes 3 +Economics 3 +FCC 3 +NYT 3 +Sirota 3 +Sundance 3 +Training 3 +Wiki 3 +XML 3 +Borger 2 +Brody 2 +Deliberation 2 +EcoVillage 2 +Identity 2 +LAMP 2 +Lobe 2 +Maine 2 +May 2 +MediaLogic 2 +Metaphor 2 +Mitchell 2 +NBC 2 +OHanlon 2 +Psychology 2 +Queen 2 +Software 2 +SpiralDynamics 2 +Strobel 2 +Sustainability 2 +Transcripts 2 +Brown 1 +Buddhism 1 +Community 1 +DigitalDivide 1 +Donnelly 1 +Education 1 +FairUse 1 +FireANT 1 +Google 1 +HumanRights 1 +KM 1 +Kwiatkowski 1 +Landay 1 +Loiseau 1 +Math 1 +Music 1 +Nature 1 +Schechter 1 +Screencast 1 +Sivaraksa 1 +Skype 1 +SocialCapital 1 +TagCloud 1 +Thielmann 1 +Thomas 1 +Tiger 1 +Wedgwood 1
\ No newline at end of file diff --git a/tagging/tests/tests.py b/tagging/tests/tests.py new file mode 100644 index 0000000..41a7895 --- /dev/null +++ b/tagging/tests/tests.py @@ -0,0 +1,384 @@ +# -*- coding: utf-8 -*- +r""" +>>> import os +>>> from django import newforms as forms +>>> from tagging import settings +>>> from tagging.models import Tag, TaggedItem +>>> from tagging.tests.models import Article, Link, Perch, Parrot, FormTest +>>> from tagging.utils import calculate_cloud, get_tag_name_list, get_tag_list, get_tag, LINEAR +>>> from tagging.validators import isTagList, isTag +>>> from tagging.forms import TagField + +############# +# Utilities # +############# + +# Tag input ################################################################### + +# Tag names +>>> get_tag_name_list(None) +[] +>>> get_tag_name_list('') +[] +>>> get_tag_name_list('foo') +[u'foo'] +>>> get_tag_name_list('foo bar') +[u'foo', u'bar'] +>>> get_tag_name_list('foo,bar') +[u'foo', u'bar'] +>>> get_tag_name_list(', , foo , bar , ,baz, , ,') +[u'foo', u'bar', u'baz'] +>>> get_tag_name_list('foo,ŠĐĆŽćžšđ') +[u'foo', u'\u0160\u0110\u0106\u017d\u0107\u017e\u0161\u0111'] + +# Normalised Tag list input +>>> cheese = Tag.objects.create(name='cheese') +>>> toast = Tag.objects.create(name='toast') +>>> get_tag_list(cheese) +[<Tag: cheese>] +>>> get_tag_list('cheese toast') +[<Tag: cheese>, <Tag: toast>] +>>> get_tag_list(u'cheese toast') +[<Tag: cheese>, <Tag: toast>] +>>> get_tag_list([]) +[] +>>> get_tag_list(['cheese', 'toast']) +[<Tag: cheese>, <Tag: toast>] +>>> get_tag_list([cheese.id, toast.id]) +[<Tag: cheese>, <Tag: toast>] +>>> get_tag_list(['cheese', 'toast', 'ŠĐĆŽćžšđ']) +[<Tag: cheese>, <Tag: toast>] +>>> get_tag_list([cheese, toast]) +[<Tag: cheese>, <Tag: toast>] +>>> get_tag_list((cheese, toast)) +(<Tag: cheese>, <Tag: toast>) +>>> get_tag_list(Tag.objects.filter(name__in=['cheese', 'toast'])) +[<Tag: cheese>, <Tag: toast>] +>>> get_tag_list(['cheese', toast]) +Traceback (most recent call last): + ... +ValueError: If a list or tuple of tags is provided, they must all be tag names, Tag objects or Tag ids. +>>> get_tag_list(29) +Traceback (most recent call last): + ... +ValueError: The tag input given was invalid. + +# Normalised Tag input +>>> get_tag(cheese) +<Tag: cheese> +>>> get_tag('cheese') +<Tag: cheese> +>>> get_tag(cheese.id) +<Tag: cheese> +>>> get_tag('mouse') + +# Tag clouds ################################################################## +>>> tags = [] +>>> for line in open(os.path.join(os.path.dirname(__file__), 'tags.txt')).readlines(): +... name, count = line.rstrip().split() +... tag = Tag(name=name) +... tag.count = int(count) +... tags.append(tag) + +>>> sizes = {} +>>> for tag in calculate_cloud(tags, steps=5): +... sizes[tag.font_size] = sizes.get(tag.font_size, 0) + 1 + +# This isn't a pre-calculated test, just making sure it's consistent +>>> sizes +{1: 48, 2: 20, 3: 24, 4: 19, 5: 11} + +>>> sizes = {} +>>> for tag in calculate_cloud(tags, steps=5, distribution=LINEAR): +... sizes[tag.font_size] = sizes.get(tag.font_size, 0) + 1 + +# This isn't a pre-calculated test, just making sure it's consistent +>>> sizes +{1: 97, 2: 12, 3: 7, 4: 2, 5: 4} + +>>> calculate_cloud(tags, steps=5, distribution='cheese') +Traceback (most recent call last): + ... +ValueError: Invalid font size distribution algorithm specified: cheese. + +############## +# Validators # +############## + +>>> isTagList('foo', {}) +>>> isTagList('foo bar baz', {}) +>>> isTagList('foo,bar,baz', {}) +>>> isTagList('foo, bar, baz', {}) +>>> isTagList('foo, ŠĐĆŽćžšđ, baz', {}) +>>> isTagList('foo qwertyuiopasdfghjklzxcvbnmqwertyuiopasdfghjklzxcvb bar', {}) +>>> isTagList('', {}) +Traceback (most recent call last): + ... +ValidationError: [u'Tag names must contain only unicode alphanumeric characters, numbers, underscores or hyphens, with a comma, space or comma followed by space used to separate each tag name.'] +>>> isTagList(' foo', {}) +Traceback (most recent call last): + ... +ValidationError: [u'Tag names must contain only unicode alphanumeric characters, numbers, underscores or hyphens, with a comma, space or comma followed by space used to separate each tag name.'] +>>> isTagList('foo ', {}) +Traceback (most recent call last): + ... +ValidationError: [u'Tag names must contain only unicode alphanumeric characters, numbers, underscores or hyphens, with a comma, space or comma followed by space used to separate each tag name.'] +>>> isTagList('foo bar', {}) +Traceback (most recent call last): + ... +ValidationError: [u'Tag names must contain only unicode alphanumeric characters, numbers, underscores or hyphens, with a comma, space or comma followed by space used to separate each tag name.'] +>>> isTagList('foo qwertyuiopasdfghjklzxcvbnmqwertyuiopasdfghjklzxcvbn bar', {}) +Traceback (most recent call last): + ... +ValidationError: [u'Tag names must be no longer than 50 characters.'] +>>> isTag('f-o_1o', {}) +>>> isTag('ŠĐĆŽćžšđ', {}) +>>> isTag('f o o', {}) +Traceback (most recent call last): + ... +ValidationError: [u'Tag names must contain only unicode alphanumeric characters, numbers, underscores or hyphens.'] + +############### +# Form Fields # +############### + +>>> t = TagField() +>>> t.clean('foo') +u'foo' +>>> t.clean('foo bar baz') +u'foo bar baz' +>>> t.clean('foo,bar,baz') +u'foo,bar,baz' +>>> t.clean('foo, bar, baz') +u'foo, bar, baz' +>>> t.clean('foo qwertyuiopasdfghjklzxcvbnmqwertyuiopasdfghjklzxcvb bar') +u'foo qwertyuiopasdfghjklzxcvbnmqwertyuiopasdfghjklzxcvb bar' +>>> t.clean(' foo') +Traceback (most recent call last): + ... +ValidationError: [u'Tag names must contain only unicode alphanumeric characters, numbers, underscores or hyphens, with a comma, space or comma followed by space used to separate each tag name.'] +>>> t.clean('foo ') +Traceback (most recent call last): + ... +ValidationError: [u'Tag names must contain only unicode alphanumeric characters, numbers, underscores or hyphens, with a comma, space or comma followed by space used to separate each tag name.'] +>>> t.clean('foo bar') +Traceback (most recent call last): + ... +ValidationError: [u'Tag names must contain only unicode alphanumeric characters, numbers, underscores or hyphens, with a comma, space or comma followed by space used to separate each tag name.'] +>>> t.clean('foo qwertyuiopasdfghjklzxcvbnmqwertyuiopasdfghjklzxcvbn bar') +Traceback (most recent call last): + ... +ValidationError: [u'Tag names must be no longer than 50 characters.'] + +# Ensure that automatically created forms use TagField +>>> TestForm = forms.form_for_model(FormTest) +>>> form = TestForm() +>>> form.fields['tags'].__class__.__name__ +'TagField' +>>> instance = FormTest(tags='one two three') +>>> TestInstanceForm = forms.form_for_instance(instance) +>>> form = TestInstanceForm() +>>> form.fields['tags'].__class__.__name__ +'TagField' + +########### +# Tagging # +########### + +# Basic tagging ############################################################### + +>>> dead = Parrot.objects.create(state='dead') +>>> Tag.objects.update_tags(dead, 'foo bar ter') +>>> Tag.objects.get_for_object(dead) +[<Tag: bar>, <Tag: foo>, <Tag: ter>] +>>> Tag.objects.update_tags(dead, 'foo bar baz') +>>> Tag.objects.get_for_object(dead) +[<Tag: bar>, <Tag: baz>, <Tag: foo>] +>>> Tag.objects.add_tag(dead, 'foo') +>>> Tag.objects.get_for_object(dead) +[<Tag: bar>, <Tag: baz>, <Tag: foo>] +>>> Tag.objects.add_tag(dead, 'zip') +>>> Tag.objects.get_for_object(dead) +[<Tag: bar>, <Tag: baz>, <Tag: foo>, <Tag: zip>] +>>> Tag.objects.add_tag(dead, 'f o o') +Traceback (most recent call last): + ... +AttributeError: An invalid tag name was given: f o o. Tag names must contain only unicode alphanumeric characters, numbers, underscores or hyphens. + +# Note that doctest in Python 2.4 (and maybe 2.5?) doesn't support non-ascii +# characters in output, so we're displaying the repr() here. +>>> Tag.objects.update_tags(dead, 'ŠĐĆŽćžšđ') +>>> repr(Tag.objects.get_for_object(dead)) +'[<Tag: \xc5\xa0\xc4\x90\xc4\x86\xc5\xbd\xc4\x87\xc5\xbe\xc5\xa1\xc4\x91>]' + +>>> Tag.objects.update_tags(dead, None) +>>> Tag.objects.get_for_object(dead) +[] + +# Using a model's TagField +>>> f1 = FormTest.objects.create(tags=u'test3 test2 test1') +>>> Tag.objects.get_for_object(f1) +[<Tag: test1>, <Tag: test2>, <Tag: test3>] +>>> f1.tags = u'test4' +>>> f1.save() +>>> Tag.objects.get_for_object(f1) +[<Tag: test4>] +>>> f1.tags = '' +>>> f1.save() +>>> Tag.objects.get_for_object(f1) +[] + +# Forcing tags to lowercase +>>> settings.FORCE_LOWERCASE_TAGS = True +>>> Tag.objects.update_tags(dead, 'foO bAr Ter') +>>> Tag.objects.get_for_object(dead) +[<Tag: bar>, <Tag: foo>, <Tag: ter>] +>>> Tag.objects.update_tags(dead, 'foO bAr baZ') +>>> Tag.objects.get_for_object(dead) +[<Tag: bar>, <Tag: baz>, <Tag: foo>] +>>> Tag.objects.add_tag(dead, 'FOO') +>>> Tag.objects.get_for_object(dead) +[<Tag: bar>, <Tag: baz>, <Tag: foo>] +>>> Tag.objects.add_tag(dead, 'Zip') +>>> Tag.objects.get_for_object(dead) +[<Tag: bar>, <Tag: baz>, <Tag: foo>, <Tag: zip>] +>>> Tag.objects.update_tags(dead, None) +>>> f1.tags = u'TEST5' +>>> f1.save() +>>> Tag.objects.get_for_object(f1) +[<Tag: test5>] +>>> f1.tags +u'test5' + +# Retrieving tags by Model #################################################### + +>>> Tag.objects.usage_for_model(Parrot) +[] +>>> parrot_details = ( +... ('pining for the fjords', 9, True, 'foo bar'), +... ('passed on', 6, False, 'bar baz ter'), +... ('no more', 4, True, 'foo ter'), +... ('late', 2, False, 'bar ter'), +... ) + +>>> for state, perch_size, perch_smelly, tags in parrot_details: +... perch = Perch.objects.create(size=perch_size, smelly=perch_smelly) +... parrot = Parrot.objects.create(state=state, perch=perch) +... Tag.objects.update_tags(parrot, tags) + +>>> [(tag.name, tag.count) for tag in Tag.objects.usage_for_model(Parrot, counts=True)] +[(u'bar', 3), (u'baz', 1), (u'foo', 2), (u'ter', 3)] +>>> [(tag.name, tag.count) for tag in Tag.objects.usage_for_model(Parrot, min_count=2)] +[(u'bar', 3), (u'foo', 2), (u'ter', 3)] + +# Limiting results to a subset of the model +>>> [(tag.name, tag.count) for tag in Tag.objects.usage_for_model(Parrot, counts=True, filters=dict(state='no more'))] +[(u'foo', 1), (u'ter', 1)] +>>> [(tag.name, tag.count) for tag in Tag.objects.usage_for_model(Parrot, counts=True, filters=dict(state__startswith='p'))] +[(u'bar', 2), (u'baz', 1), (u'foo', 1), (u'ter', 1)] +>>> [(tag.name, tag.count) for tag in Tag.objects.usage_for_model(Parrot, counts=True, filters=dict(perch__size__gt=4))] +[(u'bar', 2), (u'baz', 1), (u'foo', 1), (u'ter', 1)] +>>> [(tag.name, tag.count) for tag in Tag.objects.usage_for_model(Parrot, counts=True, filters=dict(perch__smelly=True))] +[(u'bar', 1), (u'foo', 2), (u'ter', 1)] +>>> [(tag.name, tag.count) for tag in Tag.objects.usage_for_model(Parrot, min_count=2, filters=dict(perch__smelly=True))] +[(u'foo', 2)] +>>> [(tag.name, hasattr(tag, 'counts')) for tag in Tag.objects.usage_for_model(Parrot, filters=dict(perch__size__gt=4))] +[(u'bar', False), (u'baz', False), (u'foo', False), (u'ter', False)] +>>> [(tag.name, hasattr(tag, 'counts')) for tag in Tag.objects.usage_for_model(Parrot, filters=dict(perch__size__gt=99))] +[] + +# Related tags +>>> [(tag.name, tag.count) for tag in Tag.objects.related_for_model(Tag.objects.filter(name__in=['bar']), Parrot, counts=True)] +[(u'baz', 1), (u'foo', 1), (u'ter', 2)] +>>> [(tag.name, tag.count) for tag in Tag.objects.related_for_model(Tag.objects.filter(name__in=['bar']), Parrot, min_count=2)] +[(u'ter', 2)] +>>> [tag.name for tag in Tag.objects.related_for_model(Tag.objects.filter(name__in=['bar']), Parrot, counts=False)] +[u'baz', u'foo', u'ter'] +>>> [(tag.name, tag.count) for tag in Tag.objects.related_for_model(Tag.objects.filter(name__in=['bar', 'ter']), Parrot, counts=True)] +[(u'baz', 1)] +>>> [(tag.name, tag.count) for tag in Tag.objects.related_for_model(Tag.objects.filter(name__in=['bar', 'ter', 'baz']), Parrot, counts=True)] +[] + +# Once again, with feeling (strings) +>>> [(tag.name, tag.count) for tag in Tag.objects.related_for_model('bar', Parrot, counts=True)] +[(u'baz', 1), (u'foo', 1), (u'ter', 2)] +>>> [(tag.name, tag.count) for tag in Tag.objects.related_for_model('bar', Parrot, min_count=2)] +[(u'ter', 2)] +>>> [tag.name for tag in Tag.objects.related_for_model('bar', Parrot, counts=False)] +[u'baz', u'foo', u'ter'] +>>> [(tag.name, tag.count) for tag in Tag.objects.related_for_model(['bar', 'ter'], Parrot, counts=True)] +[(u'baz', 1)] +>>> [(tag.name, tag.count) for tag in Tag.objects.related_for_model(['bar', 'ter', 'baz'], Parrot, counts=True)] +[] + +# Retrieving tagged objects by Model ########################################## + +>>> foo = Tag.objects.get(name='foo') +>>> bar = Tag.objects.get(name='bar') +>>> baz = Tag.objects.get(name='baz') +>>> ter = Tag.objects.get(name='ter') +>>> TaggedItem.objects.get_by_model(Parrot, foo) +[<Parrot: no more>, <Parrot: pining for the fjords>] +>>> TaggedItem.objects.get_by_model(Parrot, bar) +[<Parrot: late>, <Parrot: passed on>, <Parrot: pining for the fjords>] + +# Intersections are supported +>>> TaggedItem.objects.get_by_model(Parrot, [foo, baz]) +[] +>>> TaggedItem.objects.get_by_model(Parrot, [foo, bar]) +[<Parrot: pining for the fjords>] +>>> TaggedItem.objects.get_by_model(Parrot, [bar, ter]) +[<Parrot: late>, <Parrot: passed on>] + +# You can also pass Tag QuerySets +>>> TaggedItem.objects.get_by_model(Parrot, Tag.objects.filter(name__in=['foo', 'baz'])) +[] +>>> TaggedItem.objects.get_by_model(Parrot, Tag.objects.filter(name__in=['foo', 'bar'])) +[<Parrot: pining for the fjords>] +>>> TaggedItem.objects.get_by_model(Parrot, Tag.objects.filter(name__in=['bar', 'ter'])) +[<Parrot: late>, <Parrot: passed on>] + +# You can also pass strings and lists of strings +>>> TaggedItem.objects.get_by_model(Parrot, 'foo baz') +[] +>>> TaggedItem.objects.get_by_model(Parrot, 'foo bar') +[<Parrot: pining for the fjords>] +>>> TaggedItem.objects.get_by_model(Parrot, 'bar ter') +[<Parrot: late>, <Parrot: passed on>] +>>> TaggedItem.objects.get_by_model(Parrot, ['foo', 'baz']) +[] +>>> TaggedItem.objects.get_by_model(Parrot, ['foo', 'bar']) +[<Parrot: pining for the fjords>] +>>> TaggedItem.objects.get_by_model(Parrot, ['bar', 'ter']) +[<Parrot: late>, <Parrot: passed on>] + +# Issue 50 - Get by non-existent tag +>>> TaggedItem.objects.get_by_model(Parrot, 'argatrons') +[] + +# Retrieving related objects by Model ######################################### + +# Related instances of the same Model +>>> l1 = Link.objects.create(name='link 1') +>>> Tag.objects.update_tags(l1, 'tag1 tag2 tag3 tag4 tag5') +>>> l2 = Link.objects.create(name='link 2') +>>> Tag.objects.update_tags(l2, 'tag1 tag2 tag3') +>>> l3 = Link.objects.create(name='link 3') +>>> Tag.objects.update_tags(l3, 'tag1') +>>> l4 = Link.objects.create(name='link 4') +>>> TaggedItem.objects.get_related(l1, Link) +[<Link: link 2>, <Link: link 3>] +>>> TaggedItem.objects.get_related(l1, Link, num=1) +[<Link: link 2>] +>>> TaggedItem.objects.get_related(l4, Link) +[] + +# Related instance of a different Model +>>> a1 = Article.objects.create(name='article 1') +>>> Tag.objects.update_tags(a1, 'tag1 tag2 tag3 tag4') +>>> TaggedItem.objects.get_related(a1, Link) +[<Link: link 1>, <Link: link 2>, <Link: link 3>] +>>> Tag.objects.update_tags(a1, 'tag6') +>>> TaggedItem.objects.get_related(a1, Link) +[] +""" |