Commit 57fca9ab by Alex Gaynor

Merge branch 'master' into api-refactor

parents 6adfb70b 4b0d0dc4
......@@ -10,3 +10,5 @@ Rob Hudson <rob@cogit8.org>
Carl Meyer <carl@oddbird.net>
Frank Wiles
Jonathan Buchanan
idle sign <idlesign@yandex.ru>
Charles Leifer
......@@ -32,4 +32,4 @@ Tags will show up for you automatically in forms and the admin.
For more info checkout out the documentation. And for questions about usage or
development you can contact the
`mailinglist <http://groups.google.com/group/django-taggit>`_.
\ No newline at end of file
`mailinglist <http://groups.google.com/group/django-taggit>`_.
......@@ -4,53 +4,49 @@ The API
After you've got your ``TaggableManager`` added to your model you can start
playing around with the API.
add(*tags)
~~~~~~~~~~
This adds tags to an object. The tags can be either ``Tag`` instances, or
strings::
>>> apple.tags.all()
[]
>>> apple.tags.add("red", "green", "fruit")
remove(*tags)
~~~~~~~~~~~~~
Removes a tag from an object. No exception is raised if the object doesn't have
that tag.
clear()
~~~~~~~
Removes all tags from an object.
set(*tags)
~~~~~~~~~~
Removes all the current tags and then adds the specified tags to the object.
most_common()
~~~~~~~~~~~~~
Returns a ``QuerySet`` of all tags, annotated with the number of times they
appear, available as the ``num_times`` attribute on each tag. The ``QuerySet``
is ordered by ``num_times``, descending. The ``QuerySet`` is lazily evaluated,
and can be sliced efficiently.
similar_objects()
~~~~~~~~~~~~~~~~~
Returns a list (not a lazy ``QuerySet``) of other objects tagged similarly to
this one, ordered with most similar first. Each object in the list is decorated
with a ``similar_tags`` attribute, the number of tags it shares with this
object.
If using generic tagging (the default), this method searches all tagged
objects. If querying on a model with its own tagging through table, only other
instances of the same model will be returned.
.. class:: TaggableManager
.. method:: add(*tags)
This adds tags to an object. The tags can be either ``Tag`` instances, or
strings::
>>> apple.tags.all()
[]
>>> apple.tags.add("red", "green", "fruit")
.. method:: remove(*tags)
Removes a tag from an object. No exception is raised if the object
doesn't have that tag.
.. method:: clear()
Removes all tags from an object.
.. method:: set(*tags)
Removes all the current tags and then adds the specified tags to the
object.
.. method: most_common()
Returns a ``QuerySet`` of all tags, annotated with the number of times
they appear, available as the ``num_times`` attribute on each tag. The
``QuerySet``is ordered by ``num_times``, descending. The ``QuerySet``
is lazily evaluated, and can be sliced efficiently.
.. method:: similar_objects()
Returns a list (not a lazy ``QuerySet``) of other objects tagged
similarly to this one, ordered with most similar first. Each object in
the list is decorated with a ``similar_tags`` attribute, the number of
tags it shares with this object.
If the model is using generic tagging (the default), this method
searches tagged objects from all classes. If you are querying on a
model with its own tagging through table, only other instances of the
same model will be returned.
Filtering
~~~~~~~~~
......@@ -61,4 +57,14 @@ Django ORM API. For example if you had a ``Food`` model, whose
like so::
>>> Food.objects.filter(tags__in=["delicious"])
[<Food: apple>, <Food: pear>, <Food: plum>]
[<Food: apple>, <Food: pear>, <Food: plum>]
If you're filtering on multiple tags, it's very common to get duplicate
results, because of the way relational databases work. Often you'll want to
make use of the ``distinct()`` method on ``QuerySets``::
>>> Food.objects.filter(tags__in=["delicious", "red"])
[<Food: apple>, <Food: apple>]
>>> Food.objects.filter(tags__in=["delicious", "red"]).distinct()
[<Food: apple>]
Changelog
=========
0.9.0
~~~~~
Unreleased.
* Added a Hebrew locale.
* Added an index on the ``object_id`` field of ``TaggedItem``.
* When displaying tags always join them with commas, never spaces.
* The docs are now available `online <http://django-taggit.readthedocs.org/>`_.
0.8.0
~~~~~
......
......@@ -4,7 +4,7 @@ Tags in forms
=============
The ``TaggableManager`` will show up automatically as a field in a
``ModelForm`` or in the admin. Tag input via the form field is parsed
``ModelForm`` or in the admin. Tags input via the form field are parsed
as follows:
* If the input doesn't contain any commas or double quotes, it is simply
......@@ -33,3 +33,19 @@ apple "ball cat" dog ``["apple", "ball cat", "dog"]`` No commas, so space del
"apple" "ball dog ``["apple", "ball", "dog"]`` Unclosed double quote is ignored
====================== ================================= ================================================
``commit=False``
~~~~~~~~~~~~~~~~
If, when saving a form, you use the ``commit=False`` option you'll need to call
``save_m2m()`` on the form after you save the object, just as you would for a
form with normal many to many fields on it::
if request.method == "POST":
form = MyFormClass(requets.POST)
if form.is_valid():
obj = form.save(commit=False)
obj.user = request.user
obj.save()
# Without this next line the tags won't be saved.
form.save_m2m()
......@@ -11,6 +11,7 @@ if not settings.configured:
INSTALLED_APPS=[
'django.contrib.contenttypes',
'taggit',
'taggit.contrib.suggest',
'taggit.tests',
]
)
......@@ -20,7 +21,7 @@ from django.test.simple import run_tests
def runtests(*test_args):
if not test_args:
test_args = ['tests']
test_args = ['tests', 'suggest']
parent = dirname(abspath(__file__))
sys.path.insert(0, parent)
failures = run_tests(test_args, verbosity=1, interactive=True, failfast=True)
......
from taggit.contrib.suggest.tests.tests import SuggestCase
DATABASE_ENGINE = 'sqlite3'
INSTALLED_APPS = [
'django.contrib.contenttypes',
'taggit',
'taggit.tests',
'taggit.contrib.suggest',
]
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
msgid ""
msgstr ""
"Project-Id-Version: Django Taggit\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2010-06-26 12:47-0500\n"
"PO-Revision-Date: 2010-06-26 12:54-0600\n"
"Last-Translator: Alex <alex.gaynor@gmail.com>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: forms.py:20
msgid "Please provide a comma-separated list of tags."
msgstr "נא לספק רשימה של תגים מופרדת עם פסיקים."
#: managers.py:41
#: managers.py:113
#: models.py:18
msgid "Tags"
msgstr "תגיות"
#: managers.py:114
msgid "A comma-separated list of tags."
msgstr "רשימה של תגים מופרדת עם פסיקים."
#: models.py:10
msgid "Name"
msgstr "שם"
#: models.py:11
msgid "Slug"
msgstr ""
#: models.py:17
msgid "Tag"
msgstr "תג"
#: models.py:56
#, python-format
msgid "%(object)s tagged with %(tag)s"
msgstr "%(object)s מתויג עם %(tag)s"
#: models.py:86
msgid "Object id"
msgstr ""
#: models.py:87
msgid "Content type"
msgstr ""
#: models.py:92
msgid "Tagged Item"
msgstr ""
#: models.py:93
msgid "Tagged Items"
msgstr ""
#: contrib/suggest/models.py:57
msgid "Enter a valid Regular Expression. To make it case-insensitive include \"(?i)\" in your expression."
msgstr ""
......@@ -83,7 +83,7 @@ class TaggedItemBase(models.Model):
class TaggedItem(TaggedItemBase):
object_id = models.IntegerField(verbose_name=_('Object id'))
object_id = models.IntegerField(verbose_name=_('Object id'), db_index=True)
content_type = models.ForeignKey(ContentType, verbose_name=_('Content type'),
related_name="tagged_items")
content_object = GenericForeignKey()
......
......@@ -235,11 +235,11 @@ class TaggableFormTestCase(BaseTaggingTestCase):
self.assert_tags_equal(raspberry.tags.all(), [])
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="delicious green red yummy" id="id_tags" /><br />A comma-separated list of tags.</td></tr>""")
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="delicious, green, red, yummy" id="id_tags" /><br />A comma-separated list of tags.</td></tr>""")
apple.tags.add('has,comma')
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="&quot;has,comma&quot; delicious green red yummy" id="id_tags" /><br />A comma-separated list of tags.</td></tr>""")
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="&quot;has,comma&quot;, delicious, green, red, yummy" id="id_tags" /><br />A comma-separated list of tags.</td></tr>""")
class TaggableFormDirectTestCase(TaggableFormTestCase):
......@@ -332,5 +332,5 @@ class TagStringParseTestCase(UnitTestCase):
self.assertEqual(edit_string_for_tags([plain]), u'plain')
self.assertEqual(edit_string_for_tags([plain, spaces]), u'plain, spa ces')
self.assertEqual(edit_string_for_tags([plain, spaces, comma]), u'"com,ma", plain, spa ces')
self.assertEqual(edit_string_for_tags([plain, comma]), u'"com,ma" plain')
self.assertEqual(edit_string_for_tags([plain, comma]), u'"com,ma", plain')
self.assertEqual(edit_string_for_tags([comma, spaces]), u'"com,ma", spa ces')
......@@ -108,21 +108,13 @@ def edit_string_for_tags(tags):
<http://django-tagging.googlecode.com/>`_
"""
names = []
use_commas = False
for tag in tags:
name = tag.name
if u',' in name:
names.append('"%s"' % name)
continue
elif u' ' in name:
if not use_commas:
use_commas = True
names.append(name)
if use_commas:
glue = u', '
else:
glue = u' '
return glue.join(sorted(names))
else:
names.append(name)
return u', '.join(sorted(names))
def require_instance_manager(func):
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment