Skip to content
Toggle navigation
P
Projects
G
Groups
S
Snippets
Help
CIRCLE
/
django-taggit
This project
Loading...
Sign in
Toggle navigation
Go to a project
Project
Repository
Issues
0
Merge Requests
0
Wiki
Members
Activity
Graph
Charts
Create a new issue
Commits
Issue Boards
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Commit
f8e0d4d2
authored
May 03, 2010
by
Carl Meyer
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
tests for direct-tagging
parent
7ee9fd3a
Show whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
119 additions
and
82 deletions
+119
-82
taggit/managers.py
+18
-14
taggit/models.py
+5
-1
taggit/tests/__init__.py
+2
-3
taggit/tests/forms.py
+5
-1
taggit/tests/models.py
+20
-2
taggit/tests/tests.py
+69
-61
No files found.
taggit/managers.py
View file @
f8e0d4d2
...
...
@@ -5,7 +5,6 @@ from django.contrib.contenttypes.models import ContentType
from
django.contrib.contenttypes.generic
import
GenericForeignKey
from
django.db
import
models
from
django.db.models.related
import
RelatedObject
from
django.db.models.fields
import
FieldDoesNotExist
from
django.db.models.fields.related
import
ManyToManyRel
from
django.db.models.query_utils
import
QueryWrapper
...
...
@@ -126,17 +125,16 @@ class TaggableManager(object):
if
len
(
cts
)
==
1
:
return
[(
"
%
s__content_type"
%
prefix
,
cts
[
0
])]
return
[(
"
%
s__content_type__in"
%
prefix
,
cts
)]
return
self
.
through
.
_meta
.
db_table
class
_TaggableManager
(
models
.
Manager
):
def
__init__
(
self
,
through
=
None
):
self
.
through
=
through
or
TaggedItem
def
__init__
(
self
,
through
):
self
.
through
=
through
def
get_query_set
(
self
):
return
self
.
through
.
tags_for
(
self
.
model
,
self
.
instance
)
def
lookup_kwargs
(
self
):
def
_
lookup_kwargs
(
self
):
return
self
.
through
.
lookup_kwargs
(
self
.
instance
)
@require_instance_manager
...
...
@@ -144,7 +142,7 @@ class _TaggableManager(models.Manager):
for
tag
in
tags
:
if
not
isinstance
(
tag
,
Tag
):
tag
,
_
=
Tag
.
objects
.
get_or_create
(
name
=
tag
)
self
.
through
.
objects
.
get_or_create
(
**
dict
(
self
.
lookup_kwargs
(),
self
.
through
.
objects
.
get_or_create
(
**
dict
(
self
.
_
lookup_kwargs
(),
tag
=
tag
))
@require_instance_manager
...
...
@@ -154,12 +152,12 @@ class _TaggableManager(models.Manager):
@require_instance_manager
def
remove
(
self
,
*
tags
):
self
.
through
.
objects
.
filter
(
**
self
.
lookup_kwargs
())
.
filter
(
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
()
self
.
through
.
objects
.
filter
(
**
self
.
_
lookup_kwargs
())
.
delete
()
def
most_common
(
self
):
return
self
.
get_query_set
()
.
annotate
(
...
...
@@ -168,13 +166,19 @@ class _TaggableManager(models.Manager):
@require_instance_manager
def
similar_objects
(
self
):
qs
=
self
.
through
.
objects
.
values
(
*
self
.
lookup_kwargs
()
.
keys
())
qs
=
self
.
through
.
objects
.
values
(
*
self
.
_
lookup_kwargs
()
.
keys
())
qs
=
qs
.
annotate
(
n
=
models
.
Count
(
'pk'
))
qs
=
qs
.
exclude
(
**
self
.
lookup_kwargs
())
qs
=
qs
.
exclude
(
**
self
.
_
lookup_kwargs
())
qs
=
qs
.
filter
(
tag__in
=
self
.
all
())
qs
=
qs
.
order_by
(
'-n'
)
if
not
'content_object'
in
self
.
lookup_kwargs
():
if
'content_object'
in
self
.
_lookup_kwargs
():
using_gfk
=
False
items
=
dict
([(
o
.
pk
,
o
)
for
o
in
self
.
through
.
_meta
.
get_field
(
'content_object'
)
.
rel
.
to
.
objects
.
filter
(
pk__in
=
[
r
[
'content_object'
]
for
r
in
qs
])])
else
:
using_gfk
=
True
preload
=
defaultdict
(
set
)
for
result
in
qs
:
preload
[
result
[
"content_type"
]]
.
add
(
result
[
"object_id"
])
...
...
@@ -187,10 +191,10 @@ class _TaggableManager(models.Manager):
results
=
[]
for
result
in
qs
:
try
:
obj
=
result
[
'content_object'
]
except
KeyError
:
if
using_gfk
:
obj
=
items
[
result
[
"content_type"
]][
result
[
"object_id"
]]
else
:
obj
=
items
[
result
[
"content_object"
]]
obj
.
similar_tags
=
result
[
"n"
]
results
.
append
(
obj
)
return
results
...
...
taggit/models.py
View file @
f8e0d4d2
import
django
from
django.contrib.contenttypes.models
import
ContentType
from
django.contrib.contenttypes.generic
import
GenericForeignKey
from
django.db
import
models
,
IntegrityError
...
...
@@ -26,6 +27,9 @@ class Tag(models.Model):
class
TaggedItemBase
(
models
.
Model
):
if
django
.
VERSION
<
(
1
,
2
):
tag
=
models
.
ForeignKey
(
Tag
,
related_name
=
"
%(class)
s_items"
)
else
:
tag
=
models
.
ForeignKey
(
Tag
,
related_name
=
"
%(app_label)
s_
%(class)
s_items"
)
def
__unicode__
(
self
):
...
...
@@ -47,7 +51,7 @@ class TaggedItemBase(models.Model):
if
instance
is
not
None
:
return
Tag
.
objects
.
filter
(
**
{
'
%
s__content_object'
%
cls
.
tag_relname
():
instance
})
else
:
return
Tag
.
objects
.
filter
(
**
{
'
%
s__content_object__isnull'
%
cls
.
tag_relname
():
False
})
return
Tag
.
objects
.
filter
(
**
{
'
%
s__content_object__isnull'
%
cls
.
tag_relname
():
False
})
.
distinct
()
class
TaggedItem
(
TaggedItemBase
):
...
...
taggit/tests/__init__.py
View file @
f8e0d4d2
from
taggit.tests.tests
import
(
AddTagTestCase
,
DeleteObjecTestCase
,
LookupByTagTestCase
,
TaggableFormTestCase
,
SimilarityByTagTestCase
,
TagReuseTestCase
)
from
taggit.tests.tests
import
(
TaggableManagerTestCase
,
TaggableManagerDirectTestCase
,
TaggableFormTestCase
,
TaggableFormDirectTestCase
)
taggit/tests/forms.py
View file @
f8e0d4d2
from
django
import
forms
from
taggit.tests.models
import
Food
from
taggit.tests.models
import
Food
,
DirectFood
class
FoodForm
(
forms
.
ModelForm
):
class
Meta
:
model
=
Food
class
DirectFoodForm
(
forms
.
ModelForm
):
class
Meta
:
model
=
DirectFood
taggit/tests/models.py
View file @
f8e0d4d2
from
django.db
import
models
from
taggit.managers
import
TaggableManager
from
taggit.models
import
TaggedItemBase
class
Food
(
models
.
Model
):
name
=
models
.
CharField
(
max_length
=
50
)
...
...
@@ -11,7 +11,6 @@ class Food(models.Model):
def
__unicode__
(
self
):
return
self
.
name
class
Pet
(
models
.
Model
):
name
=
models
.
CharField
(
max_length
=
50
)
...
...
@@ -19,3 +18,22 @@ class Pet(models.Model):
class
HousePet
(
Pet
):
trained
=
models
.
BooleanField
()
class
TaggedFood
(
TaggedItemBase
):
content_object
=
models
.
ForeignKey
(
'DirectFood'
)
class
DirectFood
(
models
.
Model
):
name
=
models
.
CharField
(
max_length
=
50
)
tags
=
TaggableManager
(
through
=
TaggedFood
)
class
TaggedPet
(
TaggedItemBase
):
content_object
=
models
.
ForeignKey
(
'DirectPet'
)
class
DirectPet
(
models
.
Model
):
name
=
models
.
CharField
(
max_length
=
50
)
tags
=
TaggableManager
(
through
=
TaggedPet
)
class
DirectHousePet
(
DirectPet
):
trained
=
models
.
BooleanField
()
taggit/tests/tests.py
View file @
f8e0d4d2
...
...
@@ -4,8 +4,8 @@ from contextlib import contextmanager
from
django.test
import
TestCase
from
taggit.models
import
Tag
from
taggit.tests.forms
import
FoodForm
from
taggit.tests.models
import
Food
,
Pet
,
HousePet
from
taggit.tests.forms
import
FoodForm
,
DirectFoodForm
from
taggit.tests.models
import
Food
,
Pet
,
HousePet
,
DirectFood
,
DirectPet
,
DirectHousePet
class
BaseTaggingTest
(
TestCase
):
...
...
@@ -27,128 +27,136 @@ class BaseTaggingTest(TestCase):
self
.
fail
(
"No exception raised, expected
%
s"
%
exc_type
)
class
AddTagTestCase
(
BaseTaggingTest
):
class
TaggableManagerTestCase
(
BaseTaggingTest
):
food_model
=
Food
pet_model
=
Pet
housepet_model
=
HousePet
def
test_add_tag
(
self
):
apple
=
Food
.
objects
.
create
(
name
=
"apple"
)
apple
=
self
.
food_model
.
objects
.
create
(
name
=
"apple"
)
self
.
assertEqual
(
list
(
apple
.
tags
.
all
()),
[])
self
.
assertEqual
(
list
(
Food
.
tags
.
all
()),
[])
self
.
assertEqual
(
list
(
self
.
food_model
.
tags
.
all
()),
[])
apple
.
tags
.
add
(
'green'
)
self
.
assert_tags_equal
(
apple
.
tags
.
all
(),
[
'green'
])
self
.
assert_tags_equal
(
Food
.
tags
.
all
(),
[
'green'
])
self
.
assert_tags_equal
(
self
.
food_model
.
tags
.
all
(),
[
'green'
])
pear
=
Food
.
objects
.
create
(
name
=
"pear"
)
pear
=
self
.
food_model
.
objects
.
create
(
name
=
"pear"
)
pear
.
tags
.
add
(
'green'
)
self
.
assert_tags_equal
(
pear
.
tags
.
all
(),
[
'green'
])
self
.
assert_tags_equal
(
Food
.
tags
.
all
(),
[
'green'
])
self
.
assert_tags_equal
(
self
.
food_model
.
tags
.
all
(),
[
'green'
])
apple
.
tags
.
add
(
'red'
)
self
.
assert_tags_equal
(
apple
.
tags
.
all
(),
[
'green'
,
'red'
])
self
.
assert_tags_equal
(
Food
.
tags
.
all
(),
[
'green'
,
'red'
])
self
.
assert_tags_equal
(
self
.
food_model
.
tags
.
all
(),
[
'green'
,
'red'
])
self
.
assert_tags_equal
(
Food
.
tags
.
most_common
(),
[
'green'
,
'red'
],
sort
=
False
)
self
.
assert_tags_equal
(
self
.
food_model
.
tags
.
most_common
(),
[
'green'
,
'red'
],
sort
=
False
)
apple
.
tags
.
remove
(
'green'
)
self
.
assert_tags_equal
(
apple
.
tags
.
all
(),
[
'red'
])
self
.
assert_tags_equal
(
Food
.
tags
.
all
(),
[
'green'
,
'red'
])
self
.
assert_tags_equal
(
self
.
food_model
.
tags
.
all
(),
[
'green'
,
'red'
])
tag
=
Tag
.
objects
.
create
(
name
=
"delicious"
)
apple
.
tags
.
add
(
tag
)
self
.
assert_tags_equal
(
apple
.
tags
.
all
(),
[
"red"
,
"delicious"
])
apple
.
delete
()
self
.
assert_tags_equal
(
Food
.
tags
.
all
(),
[
"green"
])
self
.
assert_tags_equal
(
self
.
food_model
.
tags
.
all
(),
[
"green"
])
f
=
Food
()
f
=
self
.
food_model
()
with
self
.
assert_raises
(
ValueError
):
f
.
tags
.
all
()
def
test_unique_slug
(
self
):
apple
=
Food
.
objects
.
create
(
name
=
"apple"
)
apple
=
self
.
food_model
.
objects
.
create
(
name
=
"apple"
)
apple
.
tags
.
add
(
"Red"
,
"red"
)
class
DeleteObjecTestCase
(
BaseTaggingTest
):
def
test_delete_obj
(
self
):
apple
=
Food
.
objects
.
create
(
name
=
"apple"
)
apple
=
self
.
food_model
.
objects
.
create
(
name
=
"apple"
)
apple
.
tags
.
add
(
"red"
)
self
.
assert_tags_equal
(
apple
.
tags
.
all
(),
[
"red"
])
strawberry
=
Food
.
objects
.
create
(
name
=
"strawberry"
)
strawberry
=
self
.
food_model
.
objects
.
create
(
name
=
"strawberry"
)
strawberry
.
tags
.
add
(
"red"
)
apple
.
delete
()
self
.
assert_tags_equal
(
strawberry
.
tags
.
all
(),
[
"red"
])
class
LookupByTagTestCase
(
BaseTaggingTest
):
def
test_lookup_by_tag
(
self
):
apple
=
Food
.
objects
.
create
(
name
=
"apple"
)
apple
=
self
.
food_model
.
objects
.
create
(
name
=
"apple"
)
apple
.
tags
.
add
(
"red"
,
"green"
)
pear
=
Food
.
objects
.
create
(
name
=
"pear"
)
pear
=
self
.
food_model
.
objects
.
create
(
name
=
"pear"
)
pear
.
tags
.
add
(
"green"
)
self
.
assertEqual
(
list
(
Food
.
objects
.
filter
(
tags__in
=
[
"red"
])),
[
apple
])
self
.
assertEqual
(
list
(
Food
.
objects
.
filter
(
tags__in
=
[
"green"
])),
[
apple
,
pear
])
self
.
assertEqual
(
list
(
self
.
food_model
.
objects
.
filter
(
tags__in
=
[
"red"
])),
[
apple
])
self
.
assertEqual
(
list
(
self
.
food_model
.
objects
.
filter
(
tags__in
=
[
"green"
])),
[
apple
,
pear
])
kitty
=
Pet
.
objects
.
create
(
name
=
"kitty"
)
kitty
=
self
.
pet_model
.
objects
.
create
(
name
=
"kitty"
)
kitty
.
tags
.
add
(
"fuzzy"
,
"red"
)
dog
=
Pet
.
objects
.
create
(
name
=
"dog"
)
dog
=
self
.
pet_model
.
objects
.
create
(
name
=
"dog"
)
dog
.
tags
.
add
(
"woof"
,
"red"
)
self
.
assertEqual
(
list
(
Food
.
objects
.
filter
(
tags__in
=
[
"red"
])
.
distinct
()),
[
apple
])
self
.
assertEqual
(
list
(
self
.
food_model
.
objects
.
filter
(
tags__in
=
[
"red"
])
.
distinct
()),
[
apple
])
tag
=
Tag
.
objects
.
get
(
name
=
"woof"
)
self
.
assertEqual
(
list
(
Pet
.
objects
.
filter
(
tags__in
=
[
tag
])),
[
dog
])
self
.
assertEqual
(
list
(
self
.
pet_model
.
objects
.
filter
(
tags__in
=
[
tag
])),
[
dog
])
cat
=
HousePet
.
objects
.
create
(
name
=
"cat"
,
trained
=
True
)
cat
=
self
.
housepet_model
.
objects
.
create
(
name
=
"cat"
,
trained
=
True
)
cat
.
tags
.
add
(
"fuzzy"
)
self
.
assertEqual
(
map
(
lambda
o
:
o
.
pk
,
Pet
.
objects
.
filter
(
tags__in
=
[
"fuzzy"
])),
map
(
lambda
o
:
o
.
pk
,
self
.
pet_model
.
objects
.
filter
(
tags__in
=
[
"fuzzy"
])),
[
kitty
.
pk
,
cat
.
pk
]
)
def
test_similarity_by_tag
(
self
):
"""Test that pears are more similar to apples than watermelons"""
apple
=
self
.
food_model
.
objects
.
create
(
name
=
"apple"
)
apple
.
tags
.
add
(
"green"
,
"juicy"
,
"small"
,
"sour"
)
pear
=
self
.
food_model
.
objects
.
create
(
name
=
"pear"
)
pear
.
tags
.
add
(
"green"
,
"juicy"
,
"small"
,
"sweet"
)
watermelon
=
self
.
food_model
.
objects
.
create
(
name
=
"watermelon"
)
watermelon
.
tags
.
add
(
"green"
,
"juicy"
,
"large"
,
"sweet"
)
similar_objs
=
apple
.
tags
.
similar_objects
()
self
.
assertEqual
(
similar_objs
,
[
pear
,
watermelon
])
self
.
assertEqual
(
map
(
lambda
x
:
x
.
similar_tags
,
similar_objs
),
[
3
,
2
])
def
test_tag_reuse
(
self
):
apple
=
self
.
food_model
.
objects
.
create
(
name
=
"apple"
)
apple
.
tags
.
add
(
"juicy"
,
"juicy"
)
self
.
assert_tags_equal
(
apple
.
tags
.
all
(),
[
'juicy'
])
class
TaggableManagerDirectTestCase
(
TaggableManagerTestCase
):
food_model
=
DirectFood
pet_model
=
DirectPet
housepet_model
=
DirectHousePet
class
TaggableFormTestCase
(
BaseTaggingTest
):
form_class
=
FoodForm
food_model
=
Food
def
test_form
(
self
):
self
.
assertEqual
(
FoodForm
.
base_fields
.
keys
(),
[
'name'
,
'tags'
])
self
.
assertEqual
(
self
.
form_class
.
base_fields
.
keys
(),
[
'name'
,
'tags'
])
f
=
FoodForm
({
'name'
:
'apple'
,
'tags'
:
'green, red, yummy'
})
f
=
self
.
form_class
({
'name'
:
'apple'
,
'tags'
:
'green, red, yummy'
})
self
.
assertEqual
(
str
(
f
),
"""<tr><th><label for="id_name">Name:</label></th><td><input id="id_name" type="text" name="name" value="apple" maxlength="50" /></td></tr>
\n
<tr><th><label for="id_tags">Tags:</label></th><td><input type="text" name="tags" value="green, red, yummy" id="id_tags" /></td></tr>"""
)
f
.
save
()
apple
=
Food
.
objects
.
get
(
name
=
'apple'
)
apple
=
self
.
food_model
.
objects
.
get
(
name
=
'apple'
)
self
.
assert_tags_equal
(
apple
.
tags
.
all
(),
[
'green'
,
'red'
,
'yummy'
])
f
=
FoodForm
({
'name'
:
'apple'
,
'tags'
:
'green, red, yummy, delicious'
},
instance
=
apple
)
f
=
self
.
form_class
({
'name'
:
'apple'
,
'tags'
:
'green, red, yummy, delicious'
},
instance
=
apple
)
f
.
save
()
apple
=
Food
.
objects
.
get
(
name
=
'apple'
)
apple
=
self
.
food_model
.
objects
.
get
(
name
=
'apple'
)
self
.
assert_tags_equal
(
apple
.
tags
.
all
(),
[
'green'
,
'red'
,
'yummy'
,
'delicious'
])
self
.
assertEqual
(
Food
.
objects
.
count
(),
1
)
self
.
assertEqual
(
self
.
food_model
.
objects
.
count
(),
1
)
f
=
FoodForm
({
"name"
:
"raspberry"
})
f
=
self
.
form_class
({
"name"
:
"raspberry"
})
raspberry
=
f
.
save
()
self
.
assert_tags_equal
(
raspberry
.
tags
.
all
(),
[])
f
=
FoodForm
(
instance
=
apple
)
f
=
self
.
form_class
(
instance
=
apple
)
self
.
assertEqual
(
str
(
f
),
"""<tr><th><label for="id_name">Name:</label></th><td><input id="id_name" type="text" name="name" value="apple" maxlength="50" /></td></tr>
\n
<tr><th><label for="id_tags">Tags:</label></th><td><input type="text" name="tags" value="green, red, yummy, delicious" id="id_tags" /></td></tr>"""
)
class
SimilarityByTagTestCase
(
BaseTaggingTest
):
def
test_similarity_by_tag
(
self
):
"""Test that pears are more similar to apples than watermelons"""
apple
=
Food
.
objects
.
create
(
name
=
"apple"
)
apple
.
tags
.
add
(
"green"
,
"juicy"
,
"small"
,
"sour"
)
pear
=
Food
.
objects
.
create
(
name
=
"pear"
)
pear
.
tags
.
add
(
"green"
,
"juicy"
,
"small"
,
"sweet"
)
watermelon
=
Food
.
objects
.
create
(
name
=
"watermelon"
)
watermelon
.
tags
.
add
(
"green"
,
"juicy"
,
"large"
,
"sweet"
)
similar_objs
=
apple
.
tags
.
similar_objects
()
self
.
assertEqual
(
similar_objs
,
[
pear
,
watermelon
])
self
.
assertEqual
(
map
(
lambda
x
:
x
.
similar_tags
,
similar_objs
),
[
3
,
2
])
class
TagReuseTestCase
(
BaseTaggingTest
):
def
test_tag_reuse
(
self
):
apple
=
Food
.
objects
.
create
(
name
=
"apple"
)
apple
.
tags
.
add
(
"juicy"
,
"juicy"
)
self
.
assert_tags_equal
(
apple
.
tags
.
all
(),
[
'juicy'
])
class
TaggableFormDirectTestCase
(
TaggableFormTestCase
):
form_class
=
DirectFoodForm
food_model
=
DirectFood
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment