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
fb2d1cc4
authored
Mar 23, 2013
by
Christopher Grebs
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
We now run on py26, py27, pypy, py32, py33 using django14 and django15.
parent
20a1b6bc
Show whitespace changes
Inline
Side-by-side
Showing
11 changed files
with
164 additions
and
122 deletions
+164
-122
taggit/admin.py
+2
-0
taggit/forms.py
+4
-1
taggit/managers.py
+20
-19
taggit/migrations/0001_initial.py
+20
-18
taggit/migrations/0002_unique_tagnames.py
+12
-10
taggit/models.py
+4
-6
taggit/tests/forms.py
+2
-0
taggit/tests/models.py
+28
-7
taggit/tests/tests.py
+48
-42
taggit/utils.py
+22
-19
taggit/views.py
+2
-0
No files found.
taggit/admin.py
View file @
fb2d1cc4
from
__future__
import
unicode_literals
from
django.contrib
import
admin
from
django.contrib
import
admin
from
taggit.models
import
Tag
,
TaggedItem
from
taggit.models
import
Tag
,
TaggedItem
...
...
taggit/forms.py
View file @
fb2d1cc4
from
__future__
import
unicode_literals
from
django
import
forms
from
django
import
forms
from
django.utils.translation
import
ugettext
as
_
from
django.utils.translation
import
ugettext
as
_
from
django.utils
import
six
from
taggit.utils
import
parse_tags
,
edit_string_for_tags
from
taggit.utils
import
parse_tags
,
edit_string_for_tags
class
TagWidget
(
forms
.
TextInput
):
class
TagWidget
(
forms
.
TextInput
):
def
render
(
self
,
name
,
value
,
attrs
=
None
):
def
render
(
self
,
name
,
value
,
attrs
=
None
):
if
value
is
not
None
and
not
isinstance
(
value
,
basestring
):
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
)
return
super
(
TagWidget
,
self
)
.
render
(
name
,
value
,
attrs
)
...
...
taggit/managers.py
View file @
fb2d1cc4
from
__future__
import
unicode_literals
from
django.contrib.contenttypes.generic
import
GenericRelation
from
django.contrib.contenttypes.generic
import
GenericRelation
from
django.contrib.contenttypes.models
import
ContentType
from
django.contrib.contenttypes.models
import
ContentType
from
django.db
import
models
from
django.db
import
models
...
@@ -5,27 +7,13 @@ from django.db.models.fields.related import ManyToManyRel, RelatedField, add_laz
...
@@ -5,27 +7,13 @@ from django.db.models.fields.related import ManyToManyRel, RelatedField, add_laz
from
django.db.models.related
import
RelatedObject
from
django.db.models.related
import
RelatedObject
from
django.utils.text
import
capfirst
from
django.utils.text
import
capfirst
from
django.utils.translation
import
ugettext_lazy
as
_
from
django.utils.translation
import
ugettext_lazy
as
_
from
django.utils
import
six
from
taggit.forms
import
TagField
from
taggit.forms
import
TagField
from
taggit.models
import
TaggedItem
,
GenericTaggedItemBase
from
taggit.models
import
TaggedItem
,
GenericTaggedItemBase
from
taggit.utils
import
require_instance_manager
from
taggit.utils
import
require_instance_manager
try
:
all
except
NameError
:
# 2.4 compat
try
:
from
django.utils.itercompat
import
all
except
ImportError
:
# 1.1.X compat
def
all
(
iterable
):
for
item
in
iterable
:
if
not
item
:
return
False
return
True
class
TaggableRel
(
ManyToManyRel
):
class
TaggableRel
(
ManyToManyRel
):
def
__init__
(
self
):
def
__init__
(
self
):
self
.
related_name
=
None
self
.
related_name
=
None
...
@@ -68,7 +56,7 @@ class TaggableManager(RelatedField):
...
@@ -68,7 +56,7 @@ class TaggableManager(RelatedField):
cls
.
_meta
.
add_field
(
self
)
cls
.
_meta
.
add_field
(
self
)
setattr
(
cls
,
name
,
self
)
setattr
(
cls
,
name
,
self
)
if
not
cls
.
_meta
.
abstract
:
if
not
cls
.
_meta
.
abstract
:
if
isinstance
(
self
.
through
,
basestring
):
if
isinstance
(
self
.
through
,
six
.
string_types
):
def
resolve_related_class
(
field
,
model
,
cls
):
def
resolve_related_class
(
field
,
model
,
cls
):
self
.
through
=
model
self
.
through
=
model
self
.
post_through_setup
(
cls
)
self
.
post_through_setup
(
cls
)
...
@@ -78,6 +66,9 @@ class TaggableManager(RelatedField):
...
@@ -78,6 +66,9 @@ class TaggableManager(RelatedField):
else
:
else
:
self
.
post_through_setup
(
cls
)
self
.
post_through_setup
(
cls
)
def
__lt__
(
self
,
other
):
return
False
def
post_through_setup
(
self
,
cls
):
def
post_through_setup
(
self
,
cls
):
self
.
use_gfk
=
(
self
.
use_gfk
=
(
self
.
through
is
None
or
issubclass
(
self
.
through
,
GenericTaggedItemBase
)
self
.
through
is
None
or
issubclass
(
self
.
through
,
GenericTaggedItemBase
)
...
@@ -132,7 +123,8 @@ class TaggableManager(RelatedField):
...
@@ -132,7 +123,8 @@ class TaggableManager(RelatedField):
if
negate
or
not
self
.
use_gfk
:
if
negate
or
not
self
.
use_gfk
:
return
[]
return
[]
prefix
=
"__"
.
join
([
"tagged_items"
]
+
pieces
[:
pos
-
2
])
prefix
=
"__"
.
join
([
"tagged_items"
]
+
pieces
[:
pos
-
2
])
cts
=
map
(
ContentType
.
objects
.
get_for_model
,
_get_subclasses
(
self
.
model
))
get
=
ContentType
.
objects
.
get_for_model
cts
=
[
get
(
obj
)
for
obj
in
_get_subclasses
(
self
.
model
)]
if
len
(
cts
)
==
1
:
if
len
(
cts
)
==
1
:
return
[(
"
%
s__content_type"
%
prefix
,
cts
[
0
])]
return
[(
"
%
s__content_type"
%
prefix
,
cts
[
0
])]
return
[(
"
%
s__content_type__in"
%
prefix
,
cts
)]
return
[(
"
%
s__content_type__in"
%
prefix
,
cts
)]
...
@@ -197,7 +189,7 @@ class _TaggableManager(models.Manager):
...
@@ -197,7 +189,7 @@ class _TaggableManager(models.Manager):
def
similar_objects
(
self
):
def
similar_objects
(
self
):
lookup_kwargs
=
self
.
_lookup_kwargs
()
lookup_kwargs
=
self
.
_lookup_kwargs
()
lookup_keys
=
sorted
(
lookup_kwargs
)
lookup_keys
=
sorted
(
lookup_kwargs
)
qs
=
self
.
through
.
objects
.
values
(
*
lookup_kwargs
.
keys
(
))
qs
=
self
.
through
.
objects
.
values
(
*
six
.
iterkeys
(
lookup_kwargs
))
qs
=
qs
.
annotate
(
n
=
models
.
Count
(
'pk'
))
qs
=
qs
.
annotate
(
n
=
models
.
Count
(
'pk'
))
qs
=
qs
.
exclude
(
**
lookup_kwargs
)
qs
=
qs
.
exclude
(
**
lookup_kwargs
)
qs
=
qs
.
filter
(
tag__in
=
self
.
all
())
qs
=
qs
.
filter
(
tag__in
=
self
.
all
())
...
@@ -220,7 +212,7 @@ class _TaggableManager(models.Manager):
...
@@ -220,7 +212,7 @@ class _TaggableManager(models.Manager):
preload
.
setdefault
(
result
[
'content_type'
],
set
())
preload
.
setdefault
(
result
[
'content_type'
],
set
())
preload
[
result
[
"content_type"
]]
.
add
(
result
[
"object_id"
])
preload
[
result
[
"content_type"
]]
.
add
(
result
[
"object_id"
])
for
ct
,
obj_ids
in
preload
.
ite
rite
ms
():
for
ct
,
obj_ids
in
preload
.
items
():
ct
=
ContentType
.
objects
.
get_for_id
(
ct
)
ct
=
ContentType
.
objects
.
get_for_id
(
ct
)
for
obj
in
ct
.
model_class
()
.
_default_manager
.
filter
(
pk__in
=
obj_ids
):
for
obj
in
ct
.
model_class
()
.
_default_manager
.
filter
(
pk__in
=
obj_ids
):
items
[(
ct
.
pk
,
obj
.
pk
)]
=
obj
items
[(
ct
.
pk
,
obj
.
pk
)]
=
obj
...
@@ -243,3 +235,11 @@ def _get_subclasses(model):
...
@@ -243,3 +235,11 @@ def _get_subclasses(model):
getattr
(
field
.
field
.
rel
,
"parent_link"
,
None
)):
getattr
(
field
.
field
.
rel
,
"parent_link"
,
None
)):
subclasses
.
extend
(
_get_subclasses
(
field
.
model
))
subclasses
.
extend
(
_get_subclasses
(
field
.
model
))
return
subclasses
return
subclasses
# `total_ordering` does not exist in Django 1.4, as such
# we special case this import to be py3k specific which
# is not supported by Django 1.4
if
six
.
PY3
:
from
django.utils.functional
import
total_ordering
TaggableManager
=
total_ordering
(
TaggableManager
)
\ No newline at end of file
taggit/migrations/0001_initial.py
View file @
fb2d1cc4
# -*- coding: utf-8 -*-
# -*- coding: utf-8 -*-
from
__future__
import
unicode_literals
import
datetime
import
datetime
from
south.db
import
db
from
south.db
import
db
from
south.v2
import
SchemaMigration
from
south.v2
import
SchemaMigration
...
@@ -9,51 +11,51 @@ class Migration(SchemaMigration):
...
@@ -9,51 +11,51 @@ class Migration(SchemaMigration):
def
forwards
(
self
,
orm
):
def
forwards
(
self
,
orm
):
# Adding model 'Tag'
# Adding model 'Tag'
db
.
create_table
(
u
'taggit_tag'
,
(
db
.
create_table
(
'taggit_tag'
,
(
(
u
'id'
,
self
.
gf
(
'django.db.models.fields.AutoField'
)(
primary_key
=
True
)),
(
'id'
,
self
.
gf
(
'django.db.models.fields.AutoField'
)(
primary_key
=
True
)),
(
'name'
,
self
.
gf
(
'django.db.models.fields.CharField'
)(
max_length
=
100
)),
(
'name'
,
self
.
gf
(
'django.db.models.fields.CharField'
)(
max_length
=
100
)),
(
'slug'
,
self
.
gf
(
'django.db.models.fields.SlugField'
)(
unique
=
True
,
max_length
=
100
)),
(
'slug'
,
self
.
gf
(
'django.db.models.fields.SlugField'
)(
unique
=
True
,
max_length
=
100
)),
))
))
db
.
send_create_signal
(
u
'taggit'
,
[
'Tag'
])
db
.
send_create_signal
(
'taggit'
,
[
'Tag'
])
# Adding model 'TaggedItem'
# Adding model 'TaggedItem'
db
.
create_table
(
u
'taggit_taggeditem'
,
(
db
.
create_table
(
'taggit_taggeditem'
,
(
(
u
'id'
,
self
.
gf
(
'django.db.models.fields.AutoField'
)(
primary_key
=
True
)),
(
'id'
,
self
.
gf
(
'django.db.models.fields.AutoField'
)(
primary_key
=
True
)),
(
'tag'
,
self
.
gf
(
'django.db.models.fields.related.ForeignKey'
)(
related_name
=
u
'taggit_taggeditem_items'
,
to
=
orm
[
'taggit.Tag'
])),
(
'tag'
,
self
.
gf
(
'django.db.models.fields.related.ForeignKey'
)(
related_name
=
'taggit_taggeditem_items'
,
to
=
orm
[
'taggit.Tag'
])),
(
'object_id'
,
self
.
gf
(
'django.db.models.fields.IntegerField'
)(
db_index
=
True
)),
(
'object_id'
,
self
.
gf
(
'django.db.models.fields.IntegerField'
)(
db_index
=
True
)),
(
'content_type'
,
self
.
gf
(
'django.db.models.fields.related.ForeignKey'
)(
related_name
=
u
'taggit_taggeditem_tagged_items'
,
to
=
orm
[
'contenttypes.ContentType'
])),
(
'content_type'
,
self
.
gf
(
'django.db.models.fields.related.ForeignKey'
)(
related_name
=
'taggit_taggeditem_tagged_items'
,
to
=
orm
[
'contenttypes.ContentType'
])),
))
))
db
.
send_create_signal
(
u
'taggit'
,
[
'TaggedItem'
])
db
.
send_create_signal
(
'taggit'
,
[
'TaggedItem'
])
def
backwards
(
self
,
orm
):
def
backwards
(
self
,
orm
):
# Deleting model 'Tag'
# Deleting model 'Tag'
db
.
delete_table
(
u
'taggit_tag'
)
db
.
delete_table
(
'taggit_tag'
)
# Deleting model 'TaggedItem'
# Deleting model 'TaggedItem'
db
.
delete_table
(
u
'taggit_taggeditem'
)
db
.
delete_table
(
'taggit_taggeditem'
)
models
=
{
models
=
{
u
'contenttypes.contenttype'
:
{
'contenttypes.contenttype'
:
{
'Meta'
:
{
'ordering'
:
"('name',)"
,
'unique_together'
:
"(('app_label', 'model'),)"
,
'object_name'
:
'ContentType'
,
'db_table'
:
"'django_content_type'"
},
'Meta'
:
{
'ordering'
:
"('name',)"
,
'unique_together'
:
"(('app_label', 'model'),)"
,
'object_name'
:
'ContentType'
,
'db_table'
:
"'django_content_type'"
},
'app_label'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'100'
}),
'app_label'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'100'
}),
u
'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
}),
'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
}),
'model'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'100'
}),
'model'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'100'
}),
'name'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'100'
})
'name'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'100'
})
},
},
u
'taggit.tag'
:
{
'taggit.tag'
:
{
'Meta'
:
{
'object_name'
:
'Tag'
},
'Meta'
:
{
'object_name'
:
'Tag'
},
u
'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
}),
'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
}),
'name'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'100'
}),
'name'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'100'
}),
'slug'
:
(
'django.db.models.fields.SlugField'
,
[],
{
'unique'
:
'True'
,
'max_length'
:
'100'
})
'slug'
:
(
'django.db.models.fields.SlugField'
,
[],
{
'unique'
:
'True'
,
'max_length'
:
'100'
})
},
},
u
'taggit.taggeditem'
:
{
'taggit.taggeditem'
:
{
'Meta'
:
{
'object_name'
:
'TaggedItem'
},
'Meta'
:
{
'object_name'
:
'TaggedItem'
},
'content_type'
:
(
'django.db.models.fields.related.ForeignKey'
,
[],
{
'related_name'
:
"
u'taggit_taggeditem_tagged_items'"
,
'to'
:
u
"orm['contenttypes.ContentType']"
}),
'content_type'
:
(
'django.db.models.fields.related.ForeignKey'
,
[],
{
'related_name'
:
"
'taggit_taggeditem_tagged_items'"
,
'to'
:
"orm['contenttypes.ContentType']"
}),
u
'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
}),
'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
}),
'object_id'
:
(
'django.db.models.fields.IntegerField'
,
[],
{
'db_index'
:
'True'
}),
'object_id'
:
(
'django.db.models.fields.IntegerField'
,
[],
{
'db_index'
:
'True'
}),
'tag'
:
(
'django.db.models.fields.related.ForeignKey'
,
[],
{
'related_name'
:
"
u'taggit_taggeditem_items'"
,
'to'
:
u
"orm['taggit.Tag']"
})
'tag'
:
(
'django.db.models.fields.related.ForeignKey'
,
[],
{
'related_name'
:
"
'taggit_taggeditem_items'"
,
'to'
:
"orm['taggit.Tag']"
})
}
}
}
}
...
...
taggit/migrations/0002_unique_tagnames.py
View file @
fb2d1cc4
# -*- coding: utf-8 -*-
# -*- coding: utf-8 -*-
from
__future__
import
unicode_literals
import
datetime
import
datetime
from
south.db
import
db
from
south.db
import
db
from
south.v2
import
SchemaMigration
from
south.v2
import
SchemaMigration
...
@@ -9,34 +11,34 @@ class Migration(SchemaMigration):
...
@@ -9,34 +11,34 @@ class Migration(SchemaMigration):
def
forwards
(
self
,
orm
):
def
forwards
(
self
,
orm
):
# Adding unique constraint on 'Tag', fields ['name']
# Adding unique constraint on 'Tag', fields ['name']
db
.
create_unique
(
u
'taggit_tag'
,
[
'name'
])
db
.
create_unique
(
'taggit_tag'
,
[
'name'
])
def
backwards
(
self
,
orm
):
def
backwards
(
self
,
orm
):
# Removing unique constraint on 'Tag', fields ['name']
# Removing unique constraint on 'Tag', fields ['name']
db
.
delete_unique
(
u
'taggit_tag'
,
[
'name'
])
db
.
delete_unique
(
'taggit_tag'
,
[
'name'
])
models
=
{
models
=
{
u
'contenttypes.contenttype'
:
{
'contenttypes.contenttype'
:
{
'Meta'
:
{
'ordering'
:
"('name',)"
,
'unique_together'
:
"(('app_label', 'model'),)"
,
'object_name'
:
'ContentType'
,
'db_table'
:
"'django_content_type'"
},
'Meta'
:
{
'ordering'
:
"('name',)"
,
'unique_together'
:
"(('app_label', 'model'),)"
,
'object_name'
:
'ContentType'
,
'db_table'
:
"'django_content_type'"
},
'app_label'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'100'
}),
'app_label'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'100'
}),
u
'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
}),
'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
}),
'model'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'100'
}),
'model'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'100'
}),
'name'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'100'
})
'name'
:
(
'django.db.models.fields.CharField'
,
[],
{
'max_length'
:
'100'
})
},
},
u
'taggit.tag'
:
{
'taggit.tag'
:
{
'Meta'
:
{
'object_name'
:
'Tag'
},
'Meta'
:
{
'object_name'
:
'Tag'
},
u
'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
}),
'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
}),
'name'
:
(
'django.db.models.fields.CharField'
,
[],
{
'unique'
:
'True'
,
'max_length'
:
'100'
}),
'name'
:
(
'django.db.models.fields.CharField'
,
[],
{
'unique'
:
'True'
,
'max_length'
:
'100'
}),
'slug'
:
(
'django.db.models.fields.SlugField'
,
[],
{
'unique'
:
'True'
,
'max_length'
:
'100'
})
'slug'
:
(
'django.db.models.fields.SlugField'
,
[],
{
'unique'
:
'True'
,
'max_length'
:
'100'
})
},
},
u
'taggit.taggeditem'
:
{
'taggit.taggeditem'
:
{
'Meta'
:
{
'object_name'
:
'TaggedItem'
},
'Meta'
:
{
'object_name'
:
'TaggedItem'
},
'content_type'
:
(
'django.db.models.fields.related.ForeignKey'
,
[],
{
'related_name'
:
"
u'taggit_taggeditem_tagged_items'"
,
'to'
:
u
"orm['contenttypes.ContentType']"
}),
'content_type'
:
(
'django.db.models.fields.related.ForeignKey'
,
[],
{
'related_name'
:
"
'taggit_taggeditem_tagged_items'"
,
'to'
:
"orm['contenttypes.ContentType']"
}),
u
'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
}),
'id'
:
(
'django.db.models.fields.AutoField'
,
[],
{
'primary_key'
:
'True'
}),
'object_id'
:
(
'django.db.models.fields.IntegerField'
,
[],
{
'db_index'
:
'True'
}),
'object_id'
:
(
'django.db.models.fields.IntegerField'
,
[],
{
'db_index'
:
'True'
}),
'tag'
:
(
'django.db.models.fields.related.ForeignKey'
,
[],
{
'related_name'
:
"
u'taggit_taggeditem_items'"
,
'to'
:
u
"orm['taggit.Tag']"
})
'tag'
:
(
'django.db.models.fields.related.ForeignKey'
,
[],
{
'related_name'
:
"
'taggit_taggeditem_items'"
,
'to'
:
"orm['taggit.Tag']"
})
}
}
}
}
...
...
taggit/models.py
View file @
fb2d1cc4
from
__future__
import
unicode_literals
import
django
import
django
from
django.contrib.contenttypes.models
import
ContentType
from
django.contrib.contenttypes.models
import
ContentType
from
django.contrib.contenttypes.generic
import
GenericForeignKey
from
django.contrib.contenttypes.generic
import
GenericForeignKey
...
@@ -10,7 +12,7 @@ class TagBase(models.Model):
...
@@ -10,7 +12,7 @@ class TagBase(models.Model):
name
=
models
.
CharField
(
verbose_name
=
_
(
'Name'
),
unique
=
True
,
max_length
=
100
)
name
=
models
.
CharField
(
verbose_name
=
_
(
'Name'
),
unique
=
True
,
max_length
=
100
)
slug
=
models
.
SlugField
(
verbose_name
=
_
(
'Slug'
),
unique
=
True
,
max_length
=
100
)
slug
=
models
.
SlugField
(
verbose_name
=
_
(
'Slug'
),
unique
=
True
,
max_length
=
100
)
def
__
unicode
__
(
self
):
def
__
str
__
(
self
):
return
self
.
name
return
self
.
name
class
Meta
:
class
Meta
:
...
@@ -57,9 +59,8 @@ class Tag(TagBase):
...
@@ -57,9 +59,8 @@ class Tag(TagBase):
verbose_name_plural
=
_
(
"Tags"
)
verbose_name_plural
=
_
(
"Tags"
)
class
ItemBase
(
models
.
Model
):
class
ItemBase
(
models
.
Model
):
def
__
unicode
__
(
self
):
def
__
str
__
(
self
):
return
ugettext
(
"
%(object)
s tagged with
%(tag)
s"
)
%
{
return
ugettext
(
"
%(object)
s tagged with
%(tag)
s"
)
%
{
"object"
:
self
.
content_object
,
"object"
:
self
.
content_object
,
"tag"
:
self
.
tag
"tag"
:
self
.
tag
...
@@ -90,9 +91,6 @@ class ItemBase(models.Model):
...
@@ -90,9 +91,6 @@ class ItemBase(models.Model):
class
TaggedItemBase
(
ItemBase
):
class
TaggedItemBase
(
ItemBase
):
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"
)
tag
=
models
.
ForeignKey
(
Tag
,
related_name
=
"
%(app_label)
s_
%(class)
s_items"
)
class
Meta
:
class
Meta
:
...
...
taggit/tests/forms.py
View file @
fb2d1cc4
from
__future__
import
unicode_literals
from
django
import
forms
from
django
import
forms
from
taggit.tests.models
import
Food
,
DirectFood
,
CustomPKFood
,
OfficialFood
from
taggit.tests.models
import
Food
,
DirectFood
,
CustomPKFood
,
OfficialFood
...
...
taggit/tests/models.py
View file @
fb2d1cc4
from
__future__
import
unicode_literals
from
django.db
import
models
from
django.db
import
models
from
django.utils.encoding
import
python_2_unicode_compatible
from
taggit.managers
import
TaggableManager
from
taggit.managers
import
TaggableManager
from
taggit.models
import
(
TaggedItemBase
,
GenericTaggedItemBase
,
TaggedItem
,
from
taggit.models
import
(
TaggedItemBase
,
GenericTaggedItemBase
,
TaggedItem
,
TagBase
,
Tag
)
TagBase
,
Tag
)
@python_2_unicode_compatible
class
Food
(
models
.
Model
):
class
Food
(
models
.
Model
):
name
=
models
.
CharField
(
max_length
=
50
)
name
=
models
.
CharField
(
max_length
=
50
)
tags
=
TaggableManager
()
tags
=
TaggableManager
()
def
__
unicode
__
(
self
):
def
__
str
__
(
self
):
return
self
.
name
return
self
.
name
@python_2_unicode_compatible
class
Pet
(
models
.
Model
):
class
Pet
(
models
.
Model
):
name
=
models
.
CharField
(
max_length
=
50
)
name
=
models
.
CharField
(
max_length
=
50
)
tags
=
TaggableManager
()
tags
=
TaggableManager
()
def
__
unicode
__
(
self
):
def
__
str
__
(
self
):
return
self
.
name
return
self
.
name
class
HousePet
(
Pet
):
class
HousePet
(
Pet
):
trained
=
models
.
BooleanField
()
trained
=
models
.
BooleanField
()
...
@@ -30,22 +36,31 @@ class HousePet(Pet):
...
@@ -30,22 +36,31 @@ class HousePet(Pet):
class
TaggedFood
(
TaggedItemBase
):
class
TaggedFood
(
TaggedItemBase
):
content_object
=
models
.
ForeignKey
(
'DirectFood'
)
content_object
=
models
.
ForeignKey
(
'DirectFood'
)
class
TaggedPet
(
TaggedItemBase
):
class
TaggedPet
(
TaggedItemBase
):
content_object
=
models
.
ForeignKey
(
'DirectPet'
)
content_object
=
models
.
ForeignKey
(
'DirectPet'
)
@python_2_unicode_compatible
class
DirectFood
(
models
.
Model
):
class
DirectFood
(
models
.
Model
):
name
=
models
.
CharField
(
max_length
=
50
)
name
=
models
.
CharField
(
max_length
=
50
)
tags
=
TaggableManager
(
through
=
"TaggedFood"
)
tags
=
TaggableManager
(
through
=
"TaggedFood"
)
def
__str__
(
self
):
return
self
.
name
@python_2_unicode_compatible
class
DirectPet
(
models
.
Model
):
class
DirectPet
(
models
.
Model
):
name
=
models
.
CharField
(
max_length
=
50
)
name
=
models
.
CharField
(
max_length
=
50
)
tags
=
TaggableManager
(
through
=
TaggedPet
)
tags
=
TaggableManager
(
through
=
TaggedPet
)
def
__
unicode
__
(
self
):
def
__
str
__
(
self
):
return
self
.
name
return
self
.
name
class
DirectHousePet
(
DirectPet
):
class
DirectHousePet
(
DirectPet
):
trained
=
models
.
BooleanField
()
trained
=
models
.
BooleanField
()
...
@@ -58,20 +73,22 @@ class TaggedCustomPKFood(TaggedItemBase):
...
@@ -58,20 +73,22 @@ class TaggedCustomPKFood(TaggedItemBase):
class
TaggedCustomPKPet
(
TaggedItemBase
):
class
TaggedCustomPKPet
(
TaggedItemBase
):
content_object
=
models
.
ForeignKey
(
'CustomPKPet'
)
content_object
=
models
.
ForeignKey
(
'CustomPKPet'
)
@python_2_unicode_compatible
class
CustomPKFood
(
models
.
Model
):
class
CustomPKFood
(
models
.
Model
):
name
=
models
.
CharField
(
max_length
=
50
,
primary_key
=
True
)
name
=
models
.
CharField
(
max_length
=
50
,
primary_key
=
True
)
tags
=
TaggableManager
(
through
=
TaggedCustomPKFood
)
tags
=
TaggableManager
(
through
=
TaggedCustomPKFood
)
def
__
unicode
__
(
self
):
def
__
str
__
(
self
):
return
self
.
name
return
self
.
name
@python_2_unicode_compatible
class
CustomPKPet
(
models
.
Model
):
class
CustomPKPet
(
models
.
Model
):
name
=
models
.
CharField
(
max_length
=
50
,
primary_key
=
True
)
name
=
models
.
CharField
(
max_length
=
50
,
primary_key
=
True
)
tags
=
TaggableManager
(
through
=
TaggedCustomPKPet
)
tags
=
TaggableManager
(
through
=
TaggedCustomPKPet
)
def
__
unicode
__
(
self
):
def
__
str
__
(
self
):
return
self
.
name
return
self
.
name
class
CustomPKHousePet
(
CustomPKPet
):
class
CustomPKHousePet
(
CustomPKPet
):
...
@@ -85,20 +102,22 @@ class OfficialTag(TagBase):
...
@@ -85,20 +102,22 @@ class OfficialTag(TagBase):
class
OfficialThroughModel
(
GenericTaggedItemBase
):
class
OfficialThroughModel
(
GenericTaggedItemBase
):
tag
=
models
.
ForeignKey
(
OfficialTag
,
related_name
=
"tagged_items"
)
tag
=
models
.
ForeignKey
(
OfficialTag
,
related_name
=
"tagged_items"
)
@python_2_unicode_compatible
class
OfficialFood
(
models
.
Model
):
class
OfficialFood
(
models
.
Model
):
name
=
models
.
CharField
(
max_length
=
50
)
name
=
models
.
CharField
(
max_length
=
50
)
tags
=
TaggableManager
(
through
=
OfficialThroughModel
)
tags
=
TaggableManager
(
through
=
OfficialThroughModel
)
def
__
unicode
__
(
self
):
def
__
str
__
(
self
):
return
self
.
name
return
self
.
name
@python_2_unicode_compatible
class
OfficialPet
(
models
.
Model
):
class
OfficialPet
(
models
.
Model
):
name
=
models
.
CharField
(
max_length
=
50
)
name
=
models
.
CharField
(
max_length
=
50
)
tags
=
TaggableManager
(
through
=
OfficialThroughModel
)
tags
=
TaggableManager
(
through
=
OfficialThroughModel
)
def
__
unicode
__
(
self
):
def
__
str
__
(
self
):
return
self
.
name
return
self
.
name
class
OfficialHousePet
(
OfficialPet
):
class
OfficialHousePet
(
OfficialPet
):
...
@@ -129,6 +148,7 @@ class ArticleTag(Tag):
...
@@ -129,6 +148,7 @@ class ArticleTag(Tag):
slug
+=
"-
%
d"
%
i
slug
+=
"-
%
d"
%
i
return
slug
return
slug
class
ArticleTaggedItem
(
TaggedItem
):
class
ArticleTaggedItem
(
TaggedItem
):
class
Meta
:
class
Meta
:
proxy
=
True
proxy
=
True
...
@@ -137,6 +157,7 @@ class ArticleTaggedItem(TaggedItem):
...
@@ -137,6 +157,7 @@ class ArticleTaggedItem(TaggedItem):
def
tag_model
(
self
):
def
tag_model
(
self
):
return
ArticleTag
return
ArticleTag
class
Article
(
models
.
Model
):
class
Article
(
models
.
Model
):
title
=
models
.
CharField
(
max_length
=
100
)
title
=
models
.
CharField
(
max_length
=
100
)
...
...
taggit/tests/tests.py
View file @
fb2d1cc4
from
__future__
import
unicode_literals
from
unittest
import
TestCase
as
UnitTestCase
from
unittest
import
TestCase
as
UnitTestCase
import
django
import
django
...
@@ -5,6 +7,8 @@ from django.conf import settings
...
@@ -5,6 +7,8 @@ from django.conf import settings
from
django.core.exceptions
import
ValidationError
from
django.core.exceptions
import
ValidationError
from
django.db
import
connection
from
django.db
import
connection
from
django.test
import
TestCase
,
TransactionTestCase
from
django.test
import
TestCase
,
TransactionTestCase
from
django.utils
import
six
from
django.utils.encoding
import
force_text
from
taggit.managers
import
TaggableManager
from
taggit.managers
import
TaggableManager
from
taggit.models
import
Tag
,
TaggedItem
from
taggit.models
import
Tag
,
TaggedItem
...
@@ -19,7 +23,7 @@ from taggit.utils import parse_tags, edit_string_for_tags
...
@@ -19,7 +23,7 @@ from taggit.utils import parse_tags, edit_string_for_tags
class
BaseTaggingTest
(
object
):
class
BaseTaggingTest
(
object
):
def
assert_tags_equal
(
self
,
qs
,
tags
,
sort
=
True
,
attr
=
"name"
):
def
assert_tags_equal
(
self
,
qs
,
tags
,
sort
=
True
,
attr
=
"name"
):
got
=
map
(
lambda
tag
:
getattr
(
tag
,
attr
),
qs
)
got
=
[
getattr
(
obj
,
attr
)
for
obj
in
qs
]
if
sort
:
if
sort
:
got
.
sort
()
got
.
sort
()
tags
.
sort
()
tags
.
sort
()
...
@@ -52,7 +56,7 @@ class BaseTaggingTest(object):
...
@@ -52,7 +56,7 @@ class BaseTaggingTest(object):
return
form_str
return
form_str
def
assert_form_renders
(
self
,
form
,
html
):
def
assert_form_renders
(
self
,
form
,
html
):
self
.
assertEqual
(
str
(
form
),
self
.
_get_form_str
(
html
))
self
.
assert
HTML
Equal
(
str
(
form
),
self
.
_get_form_str
(
html
))
class
BaseTaggingTestCase
(
TestCase
,
BaseTaggingTest
):
class
BaseTaggingTestCase
(
TestCase
,
BaseTaggingTest
):
pass
pass
...
@@ -210,10 +214,12 @@ class TaggableManagerTestCase(BaseTaggingTestCase):
...
@@ -210,10 +214,12 @@ class TaggableManagerTestCase(BaseTaggingTestCase):
cat
=
self
.
housepet_model
.
objects
.
create
(
name
=
"cat"
,
trained
=
True
)
cat
=
self
.
housepet_model
.
objects
.
create
(
name
=
"cat"
,
trained
=
True
)
cat
.
tags
.
add
(
"fuzzy"
)
cat
.
tags
.
add
(
"fuzzy"
)
self
.
assertEqual
(
pks
=
self
.
pet_model
.
objects
.
filter
(
tags__name__in
=
[
"fuzzy"
])
map
(
lambda
o
:
o
.
pk
,
self
.
pet_model
.
objects
.
filter
(
tags__name__in
=
[
"fuzzy"
])),
model_name
=
self
.
pet_model
.
__name__
[
kitty
.
pk
,
cat
.
pk
]
self
.
assertQuerysetEqual
(
pks
,
)
[
'<{0}: kitty>'
.
format
(
model_name
),
'<{0}: cat>'
.
format
(
model_name
)],
ordered
=
False
)
def
test_exclude
(
self
):
def
test_exclude
(
self
):
apple
=
self
.
food_model
.
objects
.
create
(
name
=
"apple"
)
apple
=
self
.
food_model
.
objects
.
create
(
name
=
"apple"
)
...
@@ -224,10 +230,12 @@ class TaggableManagerTestCase(BaseTaggingTestCase):
...
@@ -224,10 +230,12 @@ class TaggableManagerTestCase(BaseTaggingTestCase):
guava
=
self
.
food_model
.
objects
.
create
(
name
=
"guava"
)
guava
=
self
.
food_model
.
objects
.
create
(
name
=
"guava"
)
self
.
assertEqual
(
pks
=
self
.
food_model
.
objects
.
exclude
(
tags__name__in
=
[
"red"
])
map
(
lambda
o
:
o
.
pk
,
self
.
food_model
.
objects
.
exclude
(
tags__name__in
=
[
"red"
])),
model_name
=
self
.
food_model
.
__name__
[
pear
.
pk
,
guava
.
pk
],
self
.
assertQuerysetEqual
(
pks
,
)
[
'<{0}: pear>'
.
format
(
model_name
),
'<{0}: guava>'
.
format
(
model_name
)],
ordered
=
False
)
def
test_similarity_by_tag
(
self
):
def
test_similarity_by_tag
(
self
):
"""Test that pears are more similar to apples than watermelons"""
"""Test that pears are more similar to apples than watermelons"""
...
@@ -242,7 +250,8 @@ class TaggableManagerTestCase(BaseTaggingTestCase):
...
@@ -242,7 +250,8 @@ class TaggableManagerTestCase(BaseTaggingTestCase):
similar_objs
=
apple
.
tags
.
similar_objects
()
similar_objs
=
apple
.
tags
.
similar_objects
()
self
.
assertEqual
(
similar_objs
,
[
pear
,
watermelon
])
self
.
assertEqual
(
similar_objs
,
[
pear
,
watermelon
])
self
.
assertEqual
(
map
(
lambda
x
:
x
.
similar_tags
,
similar_objs
),
[
3
,
2
])
self
.
assertEqual
([
obj
.
similar_tags
for
obj
in
similar_objs
],
[
3
,
2
])
def
test_tag_reuse
(
self
):
def
test_tag_reuse
(
self
):
apple
=
self
.
food_model
.
objects
.
create
(
name
=
"apple"
)
apple
=
self
.
food_model
.
objects
.
create
(
name
=
"apple"
)
...
@@ -268,7 +277,7 @@ class TaggableManagerTestCase(BaseTaggingTestCase):
...
@@ -268,7 +277,7 @@ class TaggableManagerTestCase(BaseTaggingTestCase):
ross
.
tags
.
add
(
"president"
)
ross
.
tags
.
add
(
"president"
)
self
.
assertEqual
(
self
.
assertEqual
(
unicode
(
self
.
taggeditem_model
.
objects
.
all
()[
0
]),
force_text
(
self
.
taggeditem_model
.
objects
.
all
()[
0
]),
"ross tagged with president"
"ross tagged with president"
)
)
...
@@ -328,10 +337,7 @@ class TaggableManagerOfficialTestCase(TaggableManagerTestCase):
...
@@ -328,10 +337,7 @@ class TaggableManagerOfficialTestCase(TaggableManagerTestCase):
pear
=
self
.
food_model
.
objects
.
create
(
name
=
"Pear"
)
pear
=
self
.
food_model
.
objects
.
create
(
name
=
"Pear"
)
pear
.
tags
.
add
(
"delicious"
)
pear
.
tags
.
add
(
"delicious"
)
self
.
assertEqual
(
self
.
assertEqual
(
apple
,
self
.
food_model
.
objects
.
get
(
tags__official
=
False
))
map
(
lambda
o
:
o
.
pk
,
self
.
food_model
.
objects
.
filter
(
tags__official
=
False
)),
[
apple
.
pk
],
)
class
TaggableFormTestCase
(
BaseTaggingTestCase
):
class
TaggableFormTestCase
(
BaseTaggingTestCase
):
...
@@ -339,7 +345,7 @@ class TaggableFormTestCase(BaseTaggingTestCase):
...
@@ -339,7 +345,7 @@ class TaggableFormTestCase(BaseTaggingTestCase):
food_model
=
Food
food_model
=
Food
def
test_form
(
self
):
def
test_form
(
self
):
self
.
assertEqual
(
self
.
form_class
.
base_fields
.
keys
(
),
[
'name'
,
'tags'
])
self
.
assertEqual
(
list
(
self
.
form_class
.
base_fields
),
[
'name'
,
'tags'
])
f
=
self
.
form_class
({
'name'
:
'apple'
,
'tags'
:
'green, red, yummy'
})
f
=
self
.
form_class
({
'name'
:
'apple'
,
'tags'
:
'green, red, yummy'
})
self
.
assert_form_renders
(
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>
self
.
assert_form_renders
(
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>
...
@@ -375,7 +381,7 @@ class TaggableFormTestCase(BaseTaggingTestCase):
...
@@ -375,7 +381,7 @@ class TaggableFormTestCase(BaseTaggingTestCase):
tm
=
TaggableManager
(
verbose_name
=
'categories'
,
help_text
=
'Add some categories'
,
blank
=
True
)
tm
=
TaggableManager
(
verbose_name
=
'categories'
,
help_text
=
'Add some categories'
,
blank
=
True
)
ff
=
tm
.
formfield
()
ff
=
tm
.
formfield
()
self
.
assertEqual
(
ff
.
label
,
'Categories'
)
self
.
assertEqual
(
ff
.
label
,
'Categories'
)
self
.
assertEqual
(
ff
.
help_text
,
u
'Add some categories'
)
self
.
assertEqual
(
ff
.
help_text
,
'Add some categories'
)
self
.
assertEqual
(
ff
.
required
,
False
)
self
.
assertEqual
(
ff
.
required
,
False
)
self
.
assertEqual
(
ff
.
clean
(
""
),
[])
self
.
assertEqual
(
ff
.
clean
(
""
),
[])
...
@@ -407,54 +413,54 @@ class TagStringParseTestCase(UnitTestCase):
...
@@ -407,54 +413,54 @@ class TagStringParseTestCase(UnitTestCase):
"""
"""
Test with simple space-delimited tags.
Test with simple space-delimited tags.
"""
"""
self
.
assertEqual
(
parse_tags
(
'one'
),
[
u
'one'
])
self
.
assertEqual
(
parse_tags
(
'one'
),
[
'one'
])
self
.
assertEqual
(
parse_tags
(
'one two'
),
[
u'one'
,
u
'two'
])
self
.
assertEqual
(
parse_tags
(
'one two'
),
[
'one'
,
'two'
])
self
.
assertEqual
(
parse_tags
(
'one two three'
),
[
u'one'
,
u'three'
,
u
'two'
])
self
.
assertEqual
(
parse_tags
(
'one two three'
),
[
'one'
,
'three'
,
'two'
])
self
.
assertEqual
(
parse_tags
(
'one one two two'
),
[
u'one'
,
u
'two'
])
self
.
assertEqual
(
parse_tags
(
'one one two two'
),
[
'one'
,
'two'
])
def
test_with_comma_delimited_multiple_words
(
self
):
def
test_with_comma_delimited_multiple_words
(
self
):
"""
"""
Test with comma-delimited multiple words.
Test with comma-delimited multiple words.
An unquoted comma in the input will trigger this.
An unquoted comma in the input will trigger this.
"""
"""
self
.
assertEqual
(
parse_tags
(
',one'
),
[
u
'one'
])
self
.
assertEqual
(
parse_tags
(
',one'
),
[
'one'
])
self
.
assertEqual
(
parse_tags
(
',one two'
),
[
u
'one two'
])
self
.
assertEqual
(
parse_tags
(
',one two'
),
[
'one two'
])
self
.
assertEqual
(
parse_tags
(
',one two three'
),
[
u
'one two three'
])
self
.
assertEqual
(
parse_tags
(
',one two three'
),
[
'one two three'
])
self
.
assertEqual
(
parse_tags
(
'a-one, a-two and a-three'
),
self
.
assertEqual
(
parse_tags
(
'a-one, a-two and a-three'
),
[
u'a-one'
,
u
'a-two and a-three'
])
[
'a-one'
,
'a-two and a-three'
])
def
test_with_double_quoted_multiple_words
(
self
):
def
test_with_double_quoted_multiple_words
(
self
):
"""
"""
Test with double-quoted multiple words.
Test with double-quoted multiple words.
A completed quote will trigger this. Unclosed quotes are ignored.
A completed quote will trigger this. Unclosed quotes are ignored.
"""
"""
self
.
assertEqual
(
parse_tags
(
'"one'
),
[
u
'one'
])
self
.
assertEqual
(
parse_tags
(
'"one'
),
[
'one'
])
self
.
assertEqual
(
parse_tags
(
'"one two'
),
[
u'one'
,
u
'two'
])
self
.
assertEqual
(
parse_tags
(
'"one two'
),
[
'one'
,
'two'
])
self
.
assertEqual
(
parse_tags
(
'"one two three'
),
[
u'one'
,
u'three'
,
u
'two'
])
self
.
assertEqual
(
parse_tags
(
'"one two three'
),
[
'one'
,
'three'
,
'two'
])
self
.
assertEqual
(
parse_tags
(
'"one two"'
),
[
u
'one two'
])
self
.
assertEqual
(
parse_tags
(
'"one two"'
),
[
'one two'
])
self
.
assertEqual
(
parse_tags
(
'a-one "a-two and a-three"'
),
self
.
assertEqual
(
parse_tags
(
'a-one "a-two and a-three"'
),
[
u'a-one'
,
u
'a-two and a-three'
])
[
'a-one'
,
'a-two and a-three'
])
def
test_with_no_loose_commas
(
self
):
def
test_with_no_loose_commas
(
self
):
"""
"""
Test with no loose commas -- split on spaces.
Test with no loose commas -- split on spaces.
"""
"""
self
.
assertEqual
(
parse_tags
(
'one two "thr,ee"'
),
[
u'one'
,
u'thr,ee'
,
u
'two'
])
self
.
assertEqual
(
parse_tags
(
'one two "thr,ee"'
),
[
'one'
,
'thr,ee'
,
'two'
])
def
test_with_loose_commas
(
self
):
def
test_with_loose_commas
(
self
):
"""
"""
Loose commas - split on commas
Loose commas - split on commas
"""
"""
self
.
assertEqual
(
parse_tags
(
'"one", two three'
),
[
u'one'
,
u
'two three'
])
self
.
assertEqual
(
parse_tags
(
'"one", two three'
),
[
'one'
,
'two three'
])
def
test_tags_with_double_quotes_can_contain_commas
(
self
):
def
test_tags_with_double_quotes_can_contain_commas
(
self
):
"""
"""
Double quotes can contain commas
Double quotes can contain commas
"""
"""
self
.
assertEqual
(
parse_tags
(
'a-one "a-two, and a-three"'
),
self
.
assertEqual
(
parse_tags
(
'a-one "a-two, and a-three"'
),
[
u'a-one'
,
u
'a-two, and a-three'
])
[
'a-one'
,
'a-two, and a-three'
])
self
.
assertEqual
(
parse_tags
(
'"two", one, one, two, "one"'
),
self
.
assertEqual
(
parse_tags
(
'"two", one, one, two, "one"'
),
[
u'one'
,
u
'two'
])
[
'one'
,
'two'
])
def
test_with_naughty_input
(
self
):
def
test_with_naughty_input
(
self
):
"""
"""
...
@@ -467,16 +473,16 @@ class TagStringParseTestCase(UnitTestCase):
...
@@ -467,16 +473,16 @@ class TagStringParseTestCase(UnitTestCase):
self
.
assertEqual
(
parse_tags
(
'""'
),
[])
self
.
assertEqual
(
parse_tags
(
'""'
),
[])
self
.
assertEqual
(
parse_tags
(
'"'
*
7
),
[])
self
.
assertEqual
(
parse_tags
(
'"'
*
7
),
[])
self
.
assertEqual
(
parse_tags
(
',,,,,,'
),
[])
self
.
assertEqual
(
parse_tags
(
',,,,,,'
),
[])
self
.
assertEqual
(
parse_tags
(
'",",",",",",","'
),
[
u
','
])
self
.
assertEqual
(
parse_tags
(
'",",",",",",","'
),
[
','
])
self
.
assertEqual
(
parse_tags
(
'a-one "a-two" and "a-three'
),
self
.
assertEqual
(
parse_tags
(
'a-one "a-two" and "a-three'
),
[
u'a-one'
,
u'a-three'
,
u'a-two'
,
u
'and'
])
[
'a-one'
,
'a-three'
,
'a-two'
,
'and'
])
def
test_recreation_of_tag_list_string_representations
(
self
):
def
test_recreation_of_tag_list_string_representations
(
self
):
plain
=
Tag
.
objects
.
create
(
name
=
'plain'
)
plain
=
Tag
.
objects
.
create
(
name
=
'plain'
)
spaces
=
Tag
.
objects
.
create
(
name
=
'spa ces'
)
spaces
=
Tag
.
objects
.
create
(
name
=
'spa ces'
)
comma
=
Tag
.
objects
.
create
(
name
=
'com,ma'
)
comma
=
Tag
.
objects
.
create
(
name
=
'com,ma'
)
self
.
assertEqual
(
edit_string_for_tags
([
plain
]),
u
'plain'
)
self
.
assertEqual
(
edit_string_for_tags
([
plain
]),
'plain'
)
self
.
assertEqual
(
edit_string_for_tags
([
plain
,
spaces
]),
u
'"spa ces", plain'
)
self
.
assertEqual
(
edit_string_for_tags
([
plain
,
spaces
]),
'"spa ces", plain'
)
self
.
assertEqual
(
edit_string_for_tags
([
plain
,
spaces
,
comma
]),
u
'"com,ma", "spa ces", plain'
)
self
.
assertEqual
(
edit_string_for_tags
([
plain
,
spaces
,
comma
]),
'"com,ma", "spa ces", plain'
)
self
.
assertEqual
(
edit_string_for_tags
([
plain
,
comma
]),
u
'"com,ma", plain'
)
self
.
assertEqual
(
edit_string_for_tags
([
plain
,
comma
]),
'"com,ma", plain'
)
self
.
assertEqual
(
edit_string_for_tags
([
comma
,
spaces
]),
u
'"com,ma", "spa ces"'
)
self
.
assertEqual
(
edit_string_for_tags
([
comma
,
spaces
]),
'"com,ma", "spa ces"'
)
taggit/utils.py
View file @
fb2d1cc4
from
django.utils.encoding
import
force_unicode
from
__future__
import
unicode_literals
from
django.utils.encoding
import
force_text
from
django.utils.functional
import
wraps
from
django.utils.functional
import
wraps
from
django.utils
import
six
def
parse_tags
(
tagstring
):
def
parse_tags
(
tagstring
):
...
@@ -16,13 +19,13 @@ def parse_tags(tagstring):
...
@@ -16,13 +19,13 @@ def parse_tags(tagstring):
if
not
tagstring
:
if
not
tagstring
:
return
[]
return
[]
tagstring
=
force_
unicode
(
tagstring
)
tagstring
=
force_
text
(
tagstring
)
# Special case - if there are no commas or double quotes in the
# Special case - if there are no commas or double quotes in the
# input, we don't *do* a recall... I mean, we know we only need to
# input, we don't *do* a recall... I mean, we know we only need to
# split on spaces.
# split on spaces.
if
u','
not
in
tagstring
and
u
'"'
not
in
tagstring
:
if
','
not
in
tagstring
and
'"'
not
in
tagstring
:
words
=
list
(
set
(
split_strip
(
tagstring
,
u
' '
)))
words
=
list
(
set
(
split_strip
(
tagstring
,
' '
)))
words
.
sort
()
words
.
sort
()
return
words
return
words
...
@@ -36,39 +39,39 @@ def parse_tags(tagstring):
...
@@ -36,39 +39,39 @@ def parse_tags(tagstring):
i
=
iter
(
tagstring
)
i
=
iter
(
tagstring
)
try
:
try
:
while
True
:
while
True
:
c
=
i
.
next
(
)
c
=
six
.
next
(
i
)
if
c
==
u
'"'
:
if
c
==
'"'
:
if
buffer
:
if
buffer
:
to_be_split
.
append
(
u
''
.
join
(
buffer
))
to_be_split
.
append
(
''
.
join
(
buffer
))
buffer
=
[]
buffer
=
[]
# Find the matching quote
# Find the matching quote
open_quote
=
True
open_quote
=
True
c
=
i
.
next
(
)
c
=
six
.
next
(
i
)
while
c
!=
u
'"'
:
while
c
!=
'"'
:
buffer
.
append
(
c
)
buffer
.
append
(
c
)
c
=
i
.
next
(
)
c
=
six
.
next
(
i
)
if
buffer
:
if
buffer
:
word
=
u
''
.
join
(
buffer
)
.
strip
()
word
=
''
.
join
(
buffer
)
.
strip
()
if
word
:
if
word
:
words
.
append
(
word
)
words
.
append
(
word
)
buffer
=
[]
buffer
=
[]
open_quote
=
False
open_quote
=
False
else
:
else
:
if
not
saw_loose_comma
and
c
==
u
','
:
if
not
saw_loose_comma
and
c
==
','
:
saw_loose_comma
=
True
saw_loose_comma
=
True
buffer
.
append
(
c
)
buffer
.
append
(
c
)
except
StopIteration
:
except
StopIteration
:
# If we were parsing an open quote which was never closed treat
# If we were parsing an open quote which was never closed treat
# the buffer as unquoted.
# the buffer as unquoted.
if
buffer
:
if
buffer
:
if
open_quote
and
u
','
in
buffer
:
if
open_quote
and
','
in
buffer
:
saw_loose_comma
=
True
saw_loose_comma
=
True
to_be_split
.
append
(
u
''
.
join
(
buffer
))
to_be_split
.
append
(
''
.
join
(
buffer
))
if
to_be_split
:
if
to_be_split
:
if
saw_loose_comma
:
if
saw_loose_comma
:
delimiter
=
u
','
delimiter
=
','
else
:
else
:
delimiter
=
u
' '
delimiter
=
' '
for
chunk
in
to_be_split
:
for
chunk
in
to_be_split
:
words
.
extend
(
split_strip
(
chunk
,
delimiter
))
words
.
extend
(
split_strip
(
chunk
,
delimiter
))
words
=
list
(
set
(
words
))
words
=
list
(
set
(
words
))
...
@@ -76,7 +79,7 @@ def parse_tags(tagstring):
...
@@ -76,7 +79,7 @@ def parse_tags(tagstring):
return
words
return
words
def
split_strip
(
string
,
delimiter
=
u
','
):
def
split_strip
(
string
,
delimiter
=
','
):
"""
"""
Splits ``string`` on ``delimiter``, stripping each resulting string
Splits ``string`` on ``delimiter``, stripping each resulting string
and returning a list of non-empty strings.
and returning a list of non-empty strings.
...
@@ -110,11 +113,11 @@ def edit_string_for_tags(tags):
...
@@ -110,11 +113,11 @@ def edit_string_for_tags(tags):
names
=
[]
names
=
[]
for
tag
in
tags
:
for
tag
in
tags
:
name
=
tag
.
name
name
=
tag
.
name
if
u','
in
name
or
u
' '
in
name
:
if
','
in
name
or
' '
in
name
:
names
.
append
(
'"
%
s"'
%
name
)
names
.
append
(
'"
%
s"'
%
name
)
else
:
else
:
names
.
append
(
name
)
names
.
append
(
name
)
return
u
', '
.
join
(
sorted
(
names
))
return
', '
.
join
(
sorted
(
names
))
def
require_instance_manager
(
func
):
def
require_instance_manager
(
func
):
...
...
taggit/views.py
View file @
fb2d1cc4
from
__future__
import
unicode_literals
from
django.contrib.contenttypes.models
import
ContentType
from
django.contrib.contenttypes.models
import
ContentType
from
django.shortcuts
import
get_object_or_404
from
django.shortcuts
import
get_object_or_404
from
django.views.generic.list_detail
import
object_list
from
django.views.generic.list_detail
import
object_list
...
...
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