Commit 3c27a56b by Alex Gaynor

Resolve merge conflicts

parents ea52a9f1 3e130b2e
Changelog Changelog
========= =========
master (unreleased) 0.8.0
~~~~~~~~~~~~~~~~~~~ ~~~~~
* Fixed querying for objects using ``exclude(tags__in=tags)``.
* Marked strings as translatable.
* Added a Russian translation.
* Created a `mailing list <http://groups.google.com/group/django-taggit>`_.
* Smarter tagstring parsing for form field; ported from Jonathan
Buchanan's `django-tagging
<http://django-tagging.googlecode.com>`_. Now supports tags
containing commas. See :ref:`tags-in-forms` for details.
* Switched to using savepoints around the slug generation for tags. This
ensures that it works fine on databases (such as Postgres) which dirty a
transaction with an ``IntegrityError``.
* Added Python 2.4 compatibility.
* Added Django 1.1 compatibility.
* Smarter tagstring parsing for form field; ported from Jonathan
Buchanan's `django-tagging
<http://django-tagging.googlecode.com>`_. Now supports tags
containing commas. See :ref:`tags-in-forms` for details.
...@@ -45,9 +45,9 @@ copyright = u'2010, Alex Gaynor' ...@@ -45,9 +45,9 @@ copyright = u'2010, Alex Gaynor'
# built documents. # built documents.
# #
# The short X.Y version. # The short X.Y version.
version = '0.6' version = '0.8'
# The full version, including alpha/beta/rc tags. # The full version, including alpha/beta/rc tags.
release = '0.6' release = '0.8.0'
# The language for content autogenerated by Sphinx. Refer to documentation # The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages. # for a list of supported languages.
......
VERSION = (0, 6, 0) VERSION = (0, 8, 0)
# 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-11 11:28+0700\n"
"PO-Revision-Date: 2010-06-11 11:30+0700\n"
"Last-Translator: Igor 'idle sign' Starikov <idlesign@yandex.ru>\n"
"Language-Team: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;\n"
"X-Poedit-Language: Russian\n"
#: forms.py:20
msgid "Please provide a comma-separated list of tags."
msgstr "Укажите метки через запятую."
#: managers.py:41
#: managers.py:101
#: models.py:17
msgid "Tags"
msgstr "Метки"
#: managers.py:102
msgid "A comma-separated list of tags."
msgstr "Список меток через запятую."
#: models.py:9
msgid "Name"
msgstr "Название"
#: models.py:10
msgid "Slug"
msgstr "Слаг"
#: models.py:16
msgid "Tag"
msgstr "Метка"
#: models.py:55
#, python-format
msgid "%(object)s tagged with %(tag)s"
msgstr "элемент «%(object)s» с меткой «%(tag)s»"
#: models.py:82
msgid "Object id"
msgstr "ID объекта"
#: models.py:83
msgid "Content type"
msgstr "Тип содержимого"
#: models.py:87
msgid "Tagged Item"
msgstr "Элемент с меткой"
#: models.py:88
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 "Введите регулярное выражение. Чтобы сделать его чувствительным к регистру укажите \"(?i)\"."
...@@ -49,6 +49,7 @@ class TaggableManager(object): ...@@ -49,6 +49,7 @@ class TaggableManager(object):
self.db_column = None self.db_column = None
self.choices = None self.choices = None
self.serialize = False self.serialize = False
self.null = True
self.creation_counter = models.Field.creation_counter self.creation_counter = models.Field.creation_counter
models.Field.creation_counter += 1 models.Field.creation_counter += 1
...@@ -79,7 +80,7 @@ class TaggableManager(object): ...@@ -79,7 +80,7 @@ class TaggableManager(object):
"help_text": _("A comma-separated list of tags.") "help_text": _("A comma-separated list of tags.")
} }
defaults.update(kwargs) defaults.update(kwargs)
return form_class(**kwargs) return form_class(**defaults)
def value_from_object(self, instance): def value_from_object(self, instance):
if instance.pk: if instance.pk:
......
...@@ -3,15 +3,20 @@ from django.contrib.contenttypes.models import ContentType ...@@ -3,15 +3,20 @@ from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes.generic import GenericForeignKey from django.contrib.contenttypes.generic import GenericForeignKey
from django.db import models, IntegrityError, transaction from django.db import models, IntegrityError, transaction
from django.template.defaultfilters import slugify from django.template.defaultfilters import slugify
from django.utils.translation import ugettext_lazy as _, ugettext
class Tag(models.Model): class Tag(models.Model):
name = models.CharField(max_length=100) name = models.CharField(verbose_name=_('Name'), max_length=100)
slug = models.SlugField(unique=True, max_length=100) slug = models.SlugField(verbose_name=_('Slug'), unique=True, max_length=100)
def __unicode__(self): def __unicode__(self):
return self.name return self.name
class Meta:
verbose_name = _("Tag")
verbose_name_plural = _("Tags")
def save(self, *args, **kwargs): def save(self, *args, **kwargs):
if not self.pk and not self.slug: if not self.pk and not self.slug:
self.slug = slug = slugify(self.name) self.slug = slug = slugify(self.name)
...@@ -48,7 +53,10 @@ class TaggedItemBase(models.Model): ...@@ -48,7 +53,10 @@ class TaggedItemBase(models.Model):
tag = models.ForeignKey(Tag, related_name="%(app_label)s_%(class)s_items") tag = models.ForeignKey(Tag, related_name="%(app_label)s_%(class)s_items")
def __unicode__(self): def __unicode__(self):
return u"%s tagged with %s" % (self.content_object, self.tag) return ugettext("%(object)s tagged with %(tag)s") % {
"object": self.content_object,
"tag": self.tag
}
class Meta: class Meta:
abstract = True abstract = True
...@@ -75,10 +83,15 @@ class TaggedItemBase(models.Model): ...@@ -75,10 +83,15 @@ class TaggedItemBase(models.Model):
class TaggedItem(TaggedItemBase): class TaggedItem(TaggedItemBase):
object_id = models.IntegerField() object_id = models.IntegerField(verbose_name=_('Object id'))
content_type = models.ForeignKey(ContentType, related_name="tagged_items") content_type = models.ForeignKey(ContentType, verbose_name=_('Content type'),
related_name="tagged_items")
content_object = GenericForeignKey() content_object = GenericForeignKey()
class Meta:
verbose_name = _("Tagged Item")
verbose_name_plural = _("Tagged Items")
@classmethod @classmethod
def lookup_kwargs(cls, instance): def lookup_kwargs(cls, instance):
return { return {
......
...@@ -15,6 +15,10 @@ class Pet(models.Model): ...@@ -15,6 +15,10 @@ class Pet(models.Model):
name = models.CharField(max_length=50) name = models.CharField(max_length=50)
tags = TaggableManager() tags = TaggableManager()
def __unicode__(self):
return self.name
class HousePet(Pet): class HousePet(Pet):
trained = models.BooleanField() trained = models.BooleanField()
...@@ -36,6 +40,10 @@ class DirectPet(models.Model): ...@@ -36,6 +40,10 @@ 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):
return self.name
class DirectHousePet(DirectPet): class DirectHousePet(DirectPet):
trained = models.BooleanField() trained = models.BooleanField()
...@@ -60,6 +68,9 @@ class CustomPKPet(models.Model): ...@@ -60,6 +68,9 @@ 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):
return self.name
class CustomPKHousePet(CustomPKPet): class CustomPKHousePet(CustomPKPet):
trained = models.BooleanField() trained = models.BooleanField()
...@@ -137,6 +137,20 @@ class TaggableManagerTestCase(BaseTaggingTestCase): ...@@ -137,6 +137,20 @@ class TaggableManagerTestCase(BaseTaggingTestCase):
map(lambda o: o.pk, self.pet_model.objects.filter(tags__name__in=["fuzzy"])), map(lambda o: o.pk, self.pet_model.objects.filter(tags__name__in=["fuzzy"])),
[kitty.pk, cat.pk] [kitty.pk, cat.pk]
) )
def test_exclude(self):
apple = self.food_model.objects.create(name="apple")
apple.tags.add("red", "green", "delicious")
pear = self.food_model.objects.create(name="pear")
pear.tags.add("green", "delicious")
guava = self.food_model.objects.create(name="guava")
self.assertEqual(
map(lambda o: o.pk, self.food_model.objects.exclude(tags__in=["red"])),
[pear.pk, guava.pk],
)
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"""
...@@ -168,6 +182,16 @@ class TaggableManagerTestCase(BaseTaggingTestCase): ...@@ -168,6 +182,16 @@ class TaggableManagerTestCase(BaseTaggingTestCase):
# [i.tag for i in self.taggeditem_model.objects.filter(**lookup_kwargs)], # [i.tag for i in self.taggeditem_model.objects.filter(**lookup_kwargs)],
# ['scary'] # ['scary']
# ) # )
def test_taggeditem_unicode(self):
ross = self.pet_model.objects.create(name="ross")
# I keep Ross Perot for a pet, what's it to you?
ross.tags.add("president")
self.assertEqual(
unicode(self.taggeditem_model.objects.all()[0]),
"ross tagged with president"
)
class TaggableManagerDirectTestCase(TaggableManagerTestCase): class TaggableManagerDirectTestCase(TaggableManagerTestCase):
...@@ -195,7 +219,7 @@ class TaggableFormTestCase(BaseTaggingTestCase): ...@@ -195,7 +219,7 @@ class TaggableFormTestCase(BaseTaggingTestCase):
self.assertEqual(self.form_class.base_fields.keys(), ['name', 'tags']) self.assertEqual(self.form_class.base_fields.keys(), ['name', 'tags'])
f = self.form_class({'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>""") 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" /><br />A comma-separated list of tags.</td></tr>""")
f.save() f.save()
apple = self.food_model.objects.get(name='apple') apple = self.food_model.objects.get(name='apple')
self.assert_tags_equal(apple.tags.all(), ['green', 'red', 'yummy']) self.assert_tags_equal(apple.tags.all(), ['green', 'red', 'yummy'])
...@@ -211,11 +235,11 @@ class TaggableFormTestCase(BaseTaggingTestCase): ...@@ -211,11 +235,11 @@ class TaggableFormTestCase(BaseTaggingTestCase):
self.assert_tags_equal(raspberry.tags.all(), []) self.assert_tags_equal(raspberry.tags.all(), [])
f = self.form_class(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="delicious green red yummy" id="id_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') apple.tags.add('has,comma')
f = self.form_class(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="&quot;has,comma&quot; delicious green red yummy" id="id_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): class TaggableFormDirectTestCase(TaggableFormTestCase):
......
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