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
ed8af524
authored
Sep 21, 2014
by
Florian Apolloner
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'develop'
parents
316ee157
20780f93
Hide whitespace changes
Inline
Side-by-side
Showing
15 changed files
with
199 additions
and
107 deletions
+199
-107
CHANGELOG.txt
+4
-0
setup.cfg
+11
-0
setup.py
+1
-1
taggit/__init__.py
+1
-1
taggit/forms.py
+7
-4
taggit/managers.py
+46
-31
taggit/migrations/0001_initial.py
+4
-3
taggit/models.py
+9
-7
taggit/utils.py
+1
-1
taggit/views.py
+1
-2
tests/forms.py
+3
-4
tests/migrations/0001_initial.py
+38
-14
tests/models.py
+10
-2
tests/tests.py
+62
-36
tox.ini
+1
-1
No files found.
CHANGELOG.txt
View file @
ed8af524
Changelog
=========
0.12.2 (21.09.2014)
~~~~~~~~~~~~~~~~~~~
* Fixed 1.7 migrations.
0.12.1 (10.08.2014)
~~~~~~~~~~~~~~~~~~~
* Final (hopefully) fixes for the upcoming Django 1.7 release.
...
...
setup.cfg
View file @
ed8af524
...
...
@@ -3,3 +3,14 @@ license-file = LICENSE
[wheel]
universal=1
[flake8]
# ignore=NONE
# max-line-length=100
# E302: expected 2 blank lines, found 1 [E302]
# E501: line too long
ignore=E501,E302
[isort]
forced_separate=tests,taggit
setup.py
View file @
ed8af524
...
...
@@ -7,7 +7,7 @@ f.close()
setup
(
name
=
'django-taggit'
,
version
=
'0.12.
1
'
,
version
=
'0.12.
2
'
,
description
=
'django-taggit is a reusable Django application for simple tagging.'
,
long_description
=
readme
,
author
=
'Alex Gaynor'
,
...
...
taggit/__init__.py
View file @
ed8af524
VERSION
=
(
0
,
12
,
1
)
VERSION
=
(
0
,
12
,
2
)
taggit/forms.py
View file @
ed8af524
from
__future__
import
unicode_literals
from
django
import
forms
from
django.utils.translation
import
ugettext
as
_
from
django.utils
import
six
from
django.utils.translation
import
ugettext
as
_
from
taggit.utils
import
parse_tags
,
edit_string_for
_tags
from
taggit.utils
import
edit_string_for_tags
,
parse
_tags
class
TagWidget
(
forms
.
TextInput
):
def
render
(
self
,
name
,
value
,
attrs
=
None
):
if
value
is
not
None
and
not
isinstance
(
value
,
six
.
string_types
):
value
=
edit_string_for_tags
([
o
.
tag
for
o
in
value
.
select_related
(
"tag"
)])
value
=
edit_string_for_tags
([
o
.
tag
for
o
in
value
.
select_related
(
"tag"
)])
return
super
(
TagWidget
,
self
)
.
render
(
name
,
value
,
attrs
)
class
TagField
(
forms
.
CharField
):
widget
=
TagWidget
...
...
@@ -21,4 +23,5 @@ class TagField(forms.CharField):
try
:
return
parse_tags
(
value
)
except
ValueError
:
raise
forms
.
ValidationError
(
_
(
"Please provide a comma-separated list of tags."
))
raise
forms
.
ValidationError
(
_
(
"Please provide a comma-separated list of tags."
))
taggit/managers.py
View file @
ed8af524
from
__future__
import
unicode_literals
from
operator
import
attrgetter
from
django
import
VERSION
try
:
from
django.contrib.contenttypes.fields
import
GenericRelation
except
ImportError
:
# django < 1.7
from
django.contrib.contenttypes.generic
import
GenericRelation
from
django.contrib.contenttypes.models
import
ContentType
from
django.db
import
models
,
router
from
django.db.models.fields
import
Field
from
django.db.models.fields.related
import
ManyToManyRel
,
RelatedField
,
add_lazy_relation
from
django.db.models.fields.related
import
(
add_lazy_relation
,
ManyToManyRel
,
RelatedField
)
from
django.db.models.related
import
RelatedObject
from
django.utils
import
six
from
django.utils.text
import
capfirst
from
django.utils.translation
import
ugettext_lazy
as
_
from
django.utils
import
six
from
taggit.forms
import
TagField
from
taggit.models
import
GenericTaggedItemBase
,
TaggedItem
from
taggit.utils
import
require_instance_manager
try
:
from
django.contrib.contenttypes.fields
import
GenericRelation
except
ImportError
:
# django < 1.7
from
django.contrib.contenttypes.generic
import
GenericRelation
try
:
from
django.db.models.related
import
PathInfo
except
ImportError
:
pass
# PathInfo is not used on Django < 1.6
from
taggit.forms
import
TagField
from
taggit.models
import
TaggedItem
,
GenericTaggedItemBase
from
taggit.utils
import
require_instance_manager
def
_model_name
(
model
):
...
...
@@ -102,7 +106,7 @@ class _TaggableManager(models.Manager):
else
'content_object'
)
fk
=
self
.
through
.
_meta
.
get_field
(
fieldname
)
query
=
{
'
%
s__
%
s__in'
%
(
self
.
through
.
tag_relname
(),
fk
.
name
)
:
'
%
s__
%
s__in'
%
(
self
.
through
.
tag_relname
(),
fk
.
name
):
set
(
obj
.
_get_pk_val
()
for
obj
in
instances
)
}
join_table
=
self
.
through
.
_meta
.
db_table
...
...
@@ -110,13 +114,13 @@ class _TaggableManager(models.Manager):
connection
=
connections
[
db
]
qn
=
connection
.
ops
.
quote_name
qs
=
self
.
get_queryset
()
.
using
(
db
)
.
_next_is_sticky
()
.
filter
(
**
query
)
.
extra
(
select
=
{
'_prefetch_related_val'
:
'
%
s.
%
s'
%
(
qn
(
join_table
),
qn
(
source_col
))
select
=
{
'_prefetch_related_val'
:
'
%
s.
%
s'
%
(
qn
(
join_table
),
qn
(
source_col
))
}
)
return
(
qs
,
attrgetter
(
'_prefetch_related_val'
),
attrgetter
(
instance
.
_meta
.
pk
.
name
),
lambda
obj
:
obj
.
_get_pk_val
(
),
False
,
self
.
prefetch_cache_name
)
...
...
@@ -129,12 +133,17 @@ class _TaggableManager(models.Manager):
@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
str_tags
=
set
()
tag_objs
=
set
()
for
t
in
tags
:
if
isinstance
(
t
,
self
.
through
.
tag_model
()):
tag_objs
.
add
(
t
)
elif
isinstance
(
t
,
six
.
string_types
):
str_tags
.
add
(
t
)
else
:
raise
ValueError
(
"Cannot add {0} ({1}). Expected {2} or str."
.
format
(
t
,
type
(
t
),
type
(
self
.
through
.
tag_model
())))
# 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
(
...
...
@@ -220,10 +229,12 @@ class _TaggableManager(models.Manager):
class
TaggableManager
(
RelatedField
,
Field
):
_related_name_counter
=
0
def
__init__
(
self
,
verbose_name
=
_
(
"Tags"
),
help_text
=
_
(
"A comma-separated list of tags."
),
through
=
None
,
blank
=
False
,
related_name
=
None
,
to
=
None
,
manager
=
_TaggableManager
):
Field
.
__init__
(
self
,
verbose_name
=
verbose_name
,
help_text
=
help_text
,
blank
=
blank
,
null
=
True
,
serialize
=
False
)
def
__init__
(
self
,
verbose_name
=
_
(
"Tags"
),
help_text
=
_
(
"A comma-separated list of tags."
),
through
=
None
,
blank
=
False
,
related_name
=
None
,
to
=
None
,
manager
=
_TaggableManager
):
Field
.
__init__
(
self
,
verbose_name
=
verbose_name
,
help_text
=
help_text
,
blank
=
blank
,
null
=
True
,
serialize
=
False
)
self
.
through
=
through
or
TaggedItem
self
.
rel
=
TaggableRel
(
self
,
related_name
,
self
.
through
,
to
=
to
)
self
.
swappable
=
False
...
...
@@ -233,12 +244,12 @@ class TaggableManager(RelatedField, Field):
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__
)
"before you can access their tags."
%
model
.
__name__
)
manager
=
self
.
manager
(
through
=
self
.
through
,
model
=
model
,
instance
=
instance
,
prefetch_cache_name
=
self
.
name
prefetch_cache_name
=
self
.
name
)
return
manager
...
...
@@ -287,7 +298,6 @@ class TaggableManager(RelatedField, Field):
else
:
self
.
post_through_setup
(
cls
)
def
__lt__
(
self
,
other
):
"""
Required contribute_to_class as Django uses bisect
...
...
@@ -364,7 +374,7 @@ class TaggableManager(RelatedField, Field):
def
extra_filters
(
self
,
pieces
,
pos
,
negate
):
if
negate
or
not
self
.
use_gfk
:
return
[]
prefix
=
"__"
.
join
([
"tagged_items"
]
+
pieces
[:
pos
-
2
])
prefix
=
"__"
.
join
([
"tagged_items"
]
+
pieces
[:
pos
-
2
])
get
=
ContentType
.
objects
.
get_for_model
cts
=
[
get
(
obj
)
for
obj
in
_get_subclasses
(
self
.
model
)]
if
len
(
cts
)
==
1
:
...
...
@@ -378,13 +388,18 @@ class TaggableManager(RelatedField, Field):
else
:
alias_to_join
=
lhs_alias
extra_col
=
self
.
through
.
_meta
.
get_field_by_name
(
'content_type'
)[
0
]
.
column
content_type_ids
=
[
ContentType
.
objects
.
get_for_model
(
subclass
)
.
pk
for
subclass
in
_get_subclasses
(
self
.
model
)]
content_type_ids
=
[
ContentType
.
objects
.
get_for_model
(
subclass
)
.
pk
for
subclass
in
_get_subclasses
(
self
.
model
)]
if
len
(
content_type_ids
)
==
1
:
content_type_id
=
content_type_ids
[
0
]
extra_where
=
" AND
%
s.
%
s =
%%
s"
%
(
qn
(
alias_to_join
),
qn
(
extra_col
))
extra_where
=
" AND
%
s.
%
s =
%%
s"
%
(
qn
(
alias_to_join
),
qn
(
extra_col
))
params
=
[
content_type_id
]
else
:
extra_where
=
" AND
%
s.
%
s IN (
%
s)"
%
(
qn
(
alias_to_join
),
qn
(
extra_col
),
','
.
join
([
'
%
s'
]
*
len
(
content_type_ids
)))
extra_where
=
" AND
%
s.
%
s IN (
%
s)"
%
(
qn
(
alias_to_join
),
qn
(
extra_col
),
','
.
join
([
'
%
s'
]
*
len
(
content_type_ids
)))
params
=
content_type_ids
return
extra_where
,
params
...
...
@@ -461,7 +476,7 @@ def _get_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
)):
getattr
(
field
.
field
.
rel
,
"parent_link"
,
None
)):
subclasses
.
extend
(
_get_subclasses
(
field
.
model
))
return
subclasses
...
...
taggit/migrations/0001_initial.py
View file @
ed8af524
#
encoding: utf8
#
-*- coding: utf-8 -*-
from
__future__
import
unicode_literals
from
django.db
import
models
,
migrations
...
...
@@ -27,9 +28,9 @@ class Migration(migrations.Migration):
name
=
'TaggedItem'
,
fields
=
[
(
'id'
,
models
.
AutoField
(
verbose_name
=
'ID'
,
serialize
=
False
,
auto_created
=
True
,
primary_key
=
True
)),
(
'tag'
,
models
.
ForeignKey
(
to
=
'taggit.Tag'
,
to_field
=
'id'
)),
(
'object_id'
,
models
.
IntegerField
(
verbose_name
=
'Object id'
,
db_index
=
True
)),
(
'content_type'
,
models
.
ForeignKey
(
to
=
'contenttypes.ContentType'
,
to_field
=
'id'
,
verbose_name
=
'Content type'
)),
(
'content_type'
,
models
.
ForeignKey
(
related_name
=
'taggit_taggeditem_tagged_items'
,
verbose_name
=
'Content type'
,
to
=
'contenttypes.ContentType'
)),
(
'tag'
,
models
.
ForeignKey
(
related_name
=
'taggit_taggeditem_items'
,
to
=
'taggit.Tag'
)),
],
options
=
{
'verbose_name'
:
'Tagged Item'
,
...
...
taggit/models.py
View file @
ed8af524
from
__future__
import
unicode_literals
from
django.contrib.contenttypes.models
import
ContentType
from
django.db
import
IntegrityError
,
models
,
transaction
from
django.db.models.query
import
QuerySet
from
django.template.defaultfilters
import
slugify
as
default_slugify
from
django.utils.encoding
import
python_2_unicode_compatible
from
django.utils.translation
import
ugettext_lazy
as
_
from
django.utils.translation
import
ugettext
try
:
from
django.contrib.contenttypes.fields
import
GenericForeignKey
except
ImportError
:
# django < 1.7
from
django.contrib.contenttypes.generic
import
GenericForeignKey
from
django.db
import
models
,
IntegrityError
,
transaction
from
django.db.models.query
import
QuerySet
from
django.template.defaultfilters
import
slugify
as
default_slugify
from
django.utils.translation
import
ugettext_lazy
as
_
,
ugettext
from
django.utils.encoding
import
python_2_unicode_compatible
try
:
...
...
@@ -59,7 +61,7 @@ class TagBase(models.Model):
except
IntegrityError
:
pass
# Now try to find existing slugs with similar names
slugs
=
set
(
Tag
.
objects
.
filter
(
slug__startswith
=
self
.
slug
)
\
slugs
=
set
(
Tag
.
objects
.
filter
(
slug__startswith
=
self
.
slug
)
.
values_list
(
'slug'
,
flat
=
True
))
i
=
1
while
True
:
...
...
@@ -145,7 +147,7 @@ class GenericTaggedItemBase(ItemBase):
content_object
=
GenericForeignKey
()
class
Meta
:
abstract
=
True
abstract
=
True
@classmethod
def
lookup_kwargs
(
cls
,
instance
):
...
...
taggit/utils.py
View file @
ed8af524
from
__future__
import
unicode_literals
from
django.utils
import
six
from
django.utils.encoding
import
force_text
from
django.utils.functional
import
wraps
from
django.utils
import
six
def
parse_tags
(
tagstring
):
...
...
taggit/views.py
View file @
ed8af524
...
...
@@ -4,7 +4,7 @@ from django.contrib.contenttypes.models import ContentType
from
django.shortcuts
import
get_object_or_404
from
django.views.generic.list
import
ListView
from
taggit.models
import
Tag
gedItem
,
Tag
from
taggit.models
import
Tag
,
TaggedItem
def
tagged_object_list
(
request
,
slug
,
queryset
,
**
kwargs
):
...
...
@@ -18,4 +18,3 @@ def tagged_object_list(request, slug, queryset, **kwargs):
kwargs
[
"extra_context"
]
=
{}
kwargs
[
"extra_context"
][
"tag"
]
=
tag
return
ListView
.
as_view
(
request
,
qs
,
**
kwargs
)
tests/forms.py
View file @
ed8af524
from
__future__
import
unicode_literals
,
absolute_import
from
__future__
import
absolute_import
,
unicode_literals
from
django
import
forms
,
VERSION
from
.models
import
Food
,
DirectFood
,
CustomPKFood
,
OfficialFood
from
.models
import
CustomPKFood
,
DirectFood
,
Food
,
OfficialFood
fields
=
None
if
VERSION
>=
(
1
,
6
):
if
VERSION
>=
(
1
,
6
):
fields
=
'__all__'
...
...
tests/migrations/0001_initial.py
View file @
ed8af524
...
...
@@ -54,7 +54,7 @@ class Migration(migrations.Migration):
migrations
.
CreateModel
(
name
=
'CustomPKHousePet'
,
fields
=
[
(
'custompkpet_ptr'
,
models
.
OneToOneField
(
auto_created
=
True
,
primary_key
=
True
,
serialize
=
False
,
to
=
'tests.CustomPKPet'
)),
(
'custompkpet_ptr'
,
models
.
OneToOneField
(
parent_link
=
True
,
auto_created
=
True
,
primary_key
=
True
,
serialize
=
False
,
to
=
'tests.CustomPKPet'
)),
(
'trained'
,
models
.
BooleanField
(
default
=
False
)),
],
options
=
{
...
...
@@ -84,7 +84,7 @@ class Migration(migrations.Migration):
migrations
.
CreateModel
(
name
=
'DirectHousePet'
,
fields
=
[
(
'directpet_ptr'
,
models
.
OneToOneField
(
auto_created
=
True
,
primary_key
=
True
,
serialize
=
False
,
to
=
'tests.DirectPet'
)),
(
'directpet_ptr'
,
models
.
OneToOneField
(
parent_link
=
True
,
auto_created
=
True
,
primary_key
=
True
,
serialize
=
False
,
to
=
'tests.DirectPet'
)),
(
'trained'
,
models
.
BooleanField
(
default
=
False
)),
],
options
=
{
...
...
@@ -155,7 +155,7 @@ class Migration(migrations.Migration):
migrations
.
CreateModel
(
name
=
'OfficialHousePet'
,
fields
=
[
(
'officialpet_ptr'
,
models
.
OneToOneField
(
auto_created
=
True
,
primary_key
=
True
,
serialize
=
False
,
to
=
'tests.OfficialPet'
)),
(
'officialpet_ptr'
,
models
.
OneToOneField
(
parent_link
=
True
,
auto_created
=
True
,
primary_key
=
True
,
serialize
=
False
,
to
=
'tests.OfficialPet'
)),
(
'trained'
,
models
.
BooleanField
(
default
=
False
)),
],
options
=
{
...
...
@@ -180,8 +180,8 @@ class Migration(migrations.Migration):
fields
=
[
(
'id'
,
models
.
AutoField
(
verbose_name
=
'ID'
,
serialize
=
False
,
auto_created
=
True
,
primary_key
=
True
)),
(
'object_id'
,
models
.
IntegerField
(
verbose_name
=
'Object id'
,
db_index
=
True
)),
(
'content_type'
,
models
.
ForeignKey
(
verbose_name
=
'Content type'
,
to
=
'contenttypes.ContentType'
)),
(
'tag'
,
models
.
ForeignKey
(
to
=
'tests.OfficialTag'
)),
(
'content_type'
,
models
.
ForeignKey
(
related_name
=
'tests_officialthroughmodel_tagged_items'
,
verbose_name
=
'Content type'
,
to
=
'contenttypes.ContentType'
)),
(
'tag'
,
models
.
ForeignKey
(
related_name
=
'tagged_items'
,
to
=
'tests.OfficialTag'
)),
],
options
=
{
'abstract'
:
False
,
...
...
@@ -201,6 +201,30 @@ class Migration(migrations.Migration):
preserve_default
=
True
,
),
migrations
.
CreateModel
(
name
=
'Parent'
,
fields
=
[
(
'id'
,
models
.
AutoField
(
verbose_name
=
'ID'
,
serialize
=
False
,
auto_created
=
True
,
primary_key
=
True
)),
],
options
=
{
},
bases
=
(
models
.
Model
,),
),
migrations
.
CreateModel
(
name
=
'Child'
,
fields
=
[
(
'parent_ptr'
,
models
.
OneToOneField
(
parent_link
=
True
,
auto_created
=
True
,
primary_key
=
True
,
serialize
=
False
,
to
=
'tests.Parent'
)),
],
options
=
{
},
bases
=
(
'tests.parent'
,),
),
migrations
.
AddField
(
model_name
=
'parent'
,
name
=
'tags'
,
field
=
taggit
.
managers
.
TaggableManager
(
to
=
'taggit.Tag'
,
through
=
'taggit.TaggedItem'
,
help_text
=
'A comma-separated list of tags.'
,
verbose_name
=
'Tags'
),
preserve_default
=
True
,
),
migrations
.
CreateModel
(
name
=
'Pet'
,
fields
=
[
(
'id'
,
models
.
AutoField
(
verbose_name
=
'ID'
,
serialize
=
False
,
auto_created
=
True
,
primary_key
=
True
)),
...
...
@@ -213,7 +237,7 @@ class Migration(migrations.Migration):
migrations
.
CreateModel
(
name
=
'HousePet'
,
fields
=
[
(
'pet_ptr'
,
models
.
OneToOneField
(
auto_created
=
True
,
primary_key
=
True
,
serialize
=
False
,
to
=
'tests.Pet'
)),
(
'pet_ptr'
,
models
.
OneToOneField
(
parent_link
=
True
,
auto_created
=
True
,
primary_key
=
True
,
serialize
=
False
,
to
=
'tests.Pet'
)),
(
'trained'
,
models
.
BooleanField
(
default
=
False
)),
],
options
=
{
...
...
@@ -262,7 +286,7 @@ class Migration(migrations.Migration):
migrations
.
AddField
(
model_name
=
'taggedcustompkfood'
,
name
=
'tag'
,
field
=
models
.
ForeignKey
(
to
=
'taggit.Tag'
),
field
=
models
.
ForeignKey
(
related_name
=
'tests_taggedcustompkfood_items'
,
to
=
'taggit.Tag'
),
preserve_default
=
True
,
),
migrations
.
CreateModel
(
...
...
@@ -290,7 +314,7 @@ class Migration(migrations.Migration):
migrations
.
AddField
(
model_name
=
'taggedcustompkpet'
,
name
=
'tag'
,
field
=
models
.
ForeignKey
(
to
=
'taggit.Tag'
),
field
=
models
.
ForeignKey
(
related_name
=
'tests_taggedcustompkpet_items'
,
to
=
'taggit.Tag'
),
preserve_default
=
True
,
),
migrations
.
CreateModel
(
...
...
@@ -318,7 +342,7 @@ class Migration(migrations.Migration):
migrations
.
AddField
(
model_name
=
'taggedfood'
,
name
=
'tag'
,
field
=
models
.
ForeignKey
(
to
=
'taggit.Tag'
),
field
=
models
.
ForeignKey
(
related_name
=
'tests_taggedfood_items'
,
to
=
'taggit.Tag'
),
preserve_default
=
True
,
),
migrations
.
CreateModel
(
...
...
@@ -346,7 +370,7 @@ class Migration(migrations.Migration):
migrations
.
AddField
(
model_name
=
'taggedpet'
,
name
=
'tag'
,
field
=
models
.
ForeignKey
(
to
=
'taggit.Tag'
),
field
=
models
.
ForeignKey
(
related_name
=
'tests_taggedpet_items'
,
to
=
'taggit.Tag'
),
preserve_default
=
True
,
),
migrations
.
CreateModel
(
...
...
@@ -374,7 +398,7 @@ class Migration(migrations.Migration):
migrations
.
AddField
(
model_name
=
'through1'
,
name
=
'tag'
,
field
=
models
.
ForeignKey
(
to
=
'taggit.Tag'
),
field
=
models
.
ForeignKey
(
related_name
=
'tests_through1_items'
,
to
=
'taggit.Tag'
),
preserve_default
=
True
,
),
migrations
.
CreateModel
(
...
...
@@ -402,7 +426,7 @@ class Migration(migrations.Migration):
migrations
.
AddField
(
model_name
=
'through2'
,
name
=
'tag'
,
field
=
models
.
ForeignKey
(
to
=
'taggit.Tag'
),
field
=
models
.
ForeignKey
(
related_name
=
'tests_through2_items'
,
to
=
'taggit.Tag'
),
preserve_default
=
True
,
),
migrations
.
CreateModel
(
...
...
@@ -410,8 +434,8 @@ class Migration(migrations.Migration):
fields
=
[
(
'id'
,
models
.
AutoField
(
verbose_name
=
'ID'
,
serialize
=
False
,
auto_created
=
True
,
primary_key
=
True
)),
(
'object_id'
,
models
.
IntegerField
(
verbose_name
=
'Object id'
,
db_index
=
True
)),
(
'content_type'
,
models
.
ForeignKey
(
verbose_name
=
'Content type'
,
to
=
'contenttypes.ContentType'
)),
(
'tag'
,
models
.
ForeignKey
(
to
=
'taggit.Tag'
)),
(
'content_type'
,
models
.
ForeignKey
(
related_name
=
'tests_throughgfk_tagged_items'
,
verbose_name
=
'Content type'
,
to
=
'contenttypes.ContentType'
)),
(
'tag'
,
models
.
ForeignKey
(
related_name
=
'tagged_items'
,
to
=
'taggit.Tag'
)),
],
options
=
{
'abstract'
:
False
,
...
...
tests/models.py
View file @
ed8af524
...
...
@@ -4,8 +4,8 @@ from django.db import models
from
django.utils.encoding
import
python_2_unicode_compatible
from
taggit.managers
import
TaggableManager
from
taggit.models
import
(
TaggedItemBase
,
GenericTaggedItem
Base
,
TaggedItem
,
TagBase
,
Tag
)
from
taggit.models
import
(
GenericTaggedItemBase
,
Tag
,
Tag
Base
,
TaggedItem
,
TaggedItemBase
)
# Ensure that two TaggableManagers with custom through model are allowed.
...
...
@@ -192,3 +192,11 @@ class CustomManager(models.Model):
pass
tags
=
TaggableManager
(
manager
=
Foo
)
class
Parent
(
models
.
Model
):
tags
=
TaggableManager
()
class
Child
(
Parent
):
pass
tests/tests.py
View file @
ed8af524
from
__future__
import
unicode_literals
,
absolute_import
from
__future__
import
absolute_import
,
unicode_literals
from
unittest
import
TestCase
as
UnitTestCase
try
:
from
unittest
import
skipIf
,
skipUnless
except
:
from
django.utils.unittest
import
skipIf
,
skipUnless
import
django
from
django.conf
import
settings
from
django.core.exceptions
import
ImproperlyConfigured
,
ValidationError
from
django.contrib.contenttypes.models
import
ContentType
from
django.core
import
serializers
from
django.
db
import
connection
from
django.
core.exceptions
import
ImproperlyConfigured
,
ValidationError
from
django.test
import
TestCase
,
TransactionTestCase
from
django.utils
import
six
from
django.utils.encoding
import
force_text
from
django.contrib.contenttypes.models
import
ContentType
from
.forms
import
CustomPKFoodForm
,
DirectFoodForm
,
FoodForm
,
OfficialFoodForm
from
.models
import
(
Article
,
Child
,
CustomManager
,
CustomPKFood
,
CustomPKHousePet
,
CustomPKPet
,
DirectFood
,
DirectHousePet
,
DirectPet
,
Food
,
HousePet
,
Movie
,
OfficialFood
,
OfficialHousePet
,
OfficialPet
,
OfficialTag
,
OfficialThroughModel
,
Pet
,
Photo
,
TaggedCustomPKFood
,
TaggedCustomPKPet
,
TaggedFood
,
TaggedPet
)
from
taggit.managers
import
TaggableManager
,
_TaggableManager
,
_model_name
from
taggit.managers
import
_model_name
,
_TaggableManager
,
TaggableManager
from
taggit.models
import
Tag
,
TaggedItem
from
.forms
import
(
FoodForm
,
DirectFoodForm
,
CustomPKFoodForm
,
OfficialFoodForm
)
from
.models
import
(
Food
,
Pet
,
HousePet
,
DirectFood
,
DirectPet
,
DirectHousePet
,
TaggedFood
,
CustomPKFood
,
CustomPKPet
,
CustomPKHousePet
,
TaggedCustomPKFood
,
OfficialFood
,
OfficialPet
,
OfficialHousePet
,
OfficialThroughModel
,
OfficialTag
,
Photo
,
Movie
,
Article
,
CustomManager
)
from
taggit.utils
import
parse_tags
,
edit_string_for_tag
s
from
taggit.utils
import
edit_string_for_tags
,
parse_tags
try
:
from
unittest
import
skipIf
,
skipUnless
except
ImportError
:
from
django.utils.unittest
import
skipIf
,
skipUnles
s
class
BaseTaggingTest
(
object
):
...
...
@@ -87,6 +88,14 @@ class TagModelTestCase(BaseTaggingTransactionTestCase):
"category-awesome-1"
],
attr
=
"slug"
)
def
test_integers
(
self
):
"""Adding an integer as a tag should raise a ValueError (#237)."""
apple
=
self
.
food_model
.
objects
.
create
(
name
=
"apple"
)
with
self
.
assertRaisesRegexp
(
ValueError
,
(
r"Cannot add 1 \(<(type|class) 'int'>\). "
r"Expected <class 'django.db.models.base.ModelBase'> or str."
)):
apple
.
tags
.
add
(
1
)
class
TagModelDirectTestCase
(
TagModelTestCase
):
food_model
=
DirectFood
tag_model
=
Tag
...
...
@@ -109,7 +118,7 @@ class TaggableManagerTestCase(BaseTaggingTestCase):
def
test_add_tag
(
self
):
apple
=
self
.
food_model
.
objects
.
create
(
name
=
"apple"
)
self
.
assertEqual
(
list
(
apple
.
tags
.
all
()),
[])
self
.
assertEqual
(
list
(
self
.
food_model
.
tags
.
all
()),
[])
self
.
assertEqual
(
list
(
self
.
food_model
.
tags
.
all
()),
[])
apple
.
tags
.
add
(
'green'
)
self
.
assert_tags_equal
(
apple
.
tags
.
all
(),
[
'green'
])
...
...
@@ -150,7 +159,7 @@ class TaggableManagerTestCase(BaseTaggingTestCase):
# make sure we don't double create.
# + 12 on Django 1.6 for save points.
queries
=
22
if
django
.
VERSION
<
(
1
,
6
):
if
django
.
VERSION
<
(
1
,
6
):
queries
-=
12
self
.
assertNumQueries
(
queries
,
apple
.
tags
.
add
,
"red"
,
"delicious"
,
"green"
)
...
...
@@ -160,7 +169,7 @@ class TaggableManagerTestCase(BaseTaggingTestCase):
# make sure we dont't double create.
# + 4 on Django 1.6 for save points.
queries
=
9
if
django
.
VERSION
<
(
1
,
6
):
if
django
.
VERSION
<
(
1
,
6
):
queries
-=
4
self
.
assertNumQueries
(
queries
,
pear
.
tags
.
add
,
"green"
,
"delicious"
)
...
...
@@ -181,7 +190,7 @@ class TaggableManagerTestCase(BaseTaggingTestCase):
def
test_delete_bulk
(
self
):
apple
=
self
.
food_model
.
objects
.
create
(
name
=
"apple"
)
kitty
=
self
.
pet_model
.
objects
.
create
(
pk
=
apple
.
pk
,
name
=
"kitty"
)
kitty
=
self
.
pet_model
.
objects
.
create
(
pk
=
apple
.
pk
,
name
=
"kitty"
)
apple
.
tags
.
add
(
"red"
,
"delicious"
,
"fruit"
)
kitty
.
tags
.
add
(
"feline"
)
...
...
@@ -222,9 +231,9 @@ class TaggableManagerTestCase(BaseTaggingTestCase):
pks
=
self
.
pet_model
.
objects
.
filter
(
tags__name__in
=
[
"fuzzy"
])
model_name
=
self
.
pet_model
.
__name__
self
.
assertQuerysetEqual
(
pks
,
[
'<{0}: kitty>'
.
format
(
model_name
),
'<{0}: cat>'
.
format
(
model_name
)],
ordered
=
False
)
[
'<{0}: kitty>'
.
format
(
model_name
),
'<{0}: cat>'
.
format
(
model_name
)],
ordered
=
False
)
def
test_lookup_bulk
(
self
):
apple
=
self
.
food_model
.
objects
.
create
(
name
=
"apple"
)
...
...
@@ -254,14 +263,14 @@ class TaggableManagerTestCase(BaseTaggingTestCase):
pear
=
self
.
food_model
.
objects
.
create
(
name
=
"pear"
)
pear
.
tags
.
add
(
"green"
,
"delicious"
)
guava
=
self
.
food_model
.
objects
.
create
(
name
=
"guava"
)
self
.
food_model
.
objects
.
create
(
name
=
"guava"
)
pks
=
self
.
food_model
.
objects
.
exclude
(
tags__name__in
=
[
"red"
])
model_name
=
self
.
food_model
.
__name__
self
.
assertQuerysetEqual
(
pks
,
[
'<{0}: pear>'
.
format
(
model_name
),
'<{0}: guava>'
.
format
(
model_name
)],
ordered
=
False
)
[
'<{0}: pear>'
.
format
(
model_name
),
'<{0}: guava>'
.
format
(
model_name
)],
ordered
=
False
)
def
test_similarity_by_tag
(
self
):
"""Test that pears are more similar to apples than watermelons"""
...
...
@@ -293,8 +302,8 @@ class TaggableManagerTestCase(BaseTaggingTestCase):
'
%
s__name'
%
_model_name
(
self
.
pet_model
):
'Spot'
}
self
.
assert_tags_equal
(
self
.
tag_model
.
objects
.
filter
(
**
lookup_kwargs
),
[
'scary'
]
self
.
tag_model
.
objects
.
filter
(
**
lookup_kwargs
),
[
'scary'
]
)
def
test_taggeditem_unicode
(
self
):
...
...
@@ -492,7 +501,7 @@ class TagStringParseTestCase(UnitTestCase):
self
.
assertEqual
(
parse_tags
(
',one two'
),
[
'one two'
])
self
.
assertEqual
(
parse_tags
(
',one two three'
),
[
'one two three'
])
self
.
assertEqual
(
parse_tags
(
'a-one, a-two and a-three'
),
[
'a-one'
,
'a-two and a-three'
])
[
'a-one'
,
'a-two and a-three'
])
def
test_with_double_quoted_multiple_words
(
self
):
"""
...
...
@@ -504,7 +513,7 @@ class TagStringParseTestCase(UnitTestCase):
self
.
assertEqual
(
parse_tags
(
'"one two three'
),
[
'one'
,
'three'
,
'two'
])
self
.
assertEqual
(
parse_tags
(
'"one two"'
),
[
'one two'
])
self
.
assertEqual
(
parse_tags
(
'a-one "a-two and a-three"'
),
[
'a-one'
,
'a-two and a-three'
])
[
'a-one'
,
'a-two and a-three'
])
def
test_with_no_loose_commas
(
self
):
"""
...
...
@@ -523,9 +532,9 @@ class TagStringParseTestCase(UnitTestCase):
Double quotes can contain commas
"""
self
.
assertEqual
(
parse_tags
(
'a-one "a-two, and a-three"'
),
[
'a-one'
,
'a-two, and a-three'
])
[
'a-one'
,
'a-two, and a-three'
])
self
.
assertEqual
(
parse_tags
(
'"two", one, one, two, "one"'
),
[
'one'
,
'two'
])
[
'one'
,
'two'
])
def
test_with_naughty_input
(
self
):
"""
...
...
@@ -540,7 +549,7 @@ class TagStringParseTestCase(UnitTestCase):
self
.
assertEqual
(
parse_tags
(
',,,,,,'
),
[])
self
.
assertEqual
(
parse_tags
(
'",",",",",",","'
),
[
','
])
self
.
assertEqual
(
parse_tags
(
'a-one "a-two" and "a-three'
),
[
'a-one'
,
'a-three'
,
'a-two'
,
'and'
])
[
'a-one'
,
'a-three'
,
'a-two'
,
'and'
])
def
test_recreation_of_tag_list_string_representations
(
self
):
plain
=
Tag
.
objects
.
create
(
name
=
'plain'
)
...
...
@@ -571,3 +580,20 @@ class SouthSupportTests(TestCase):
except
ImproperlyConfigured
as
e
:
exception
=
e
self
.
assertIn
(
"SOUTH_MIGRATION_MODULES"
,
exception
.
args
[
0
])
class
InheritedPrefetchTests
(
TestCase
):
def
test_inherited_tags_with_prefetch
(
self
):
child
=
Child
()
child
.
save
()
child
.
tags
.
add
(
'tag 1'
,
'tag 2'
,
'tag 3'
,
'tag 4'
)
child
=
Child
.
objects
.
get
()
no_prefetch_tags
=
child
.
tags
.
all
()
self
.
assertEquals
(
4
,
no_prefetch_tags
.
count
())
child
=
Child
.
objects
.
prefetch_related
(
'tags'
)
.
get
()
prefetch_tags
=
child
.
tags
.
all
()
self
.
assertEquals
(
4
,
prefetch_tags
.
count
())
self
.
assertEquals
(
set
([
t
.
name
for
t
in
no_prefetch_tags
]),
set
([
t
.
name
for
t
in
prefetch_tags
]))
tox.ini
View file @
ed8af524
...
...
@@ -13,7 +13,7 @@ deps17 =
https://github.com/django/django/archive/stable/1.7.x.zip
#egg=django
commands
=
python
./runtests.py
python
./runtests.py
{posargs}
[testenv:py26-1.4.x]
...
...
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