django-taggit-0.24.0/0000775000175000017500000000000013433132635016315 5ustar jdufresnejdufresne00000000000000django-taggit-0.24.0/AUTHORS0000664000175000017500000000104513376364411017372 0ustar jdufresnejdufresne00000000000000django-taggit was originally created by Alex Gaynor. The following is a list of much appreciated contributors: Nathan Borror fakeempire Ben Firshman Alex Gaynor Rob Hudson Carl Meyer Frank Wiles Jonathan Buchanan idle sign Charles Leifer Florian Apolloner Andrew Pryde John Whitlock Jon Dufresne django-taggit-0.24.0/CHANGELOG.txt0000664000175000017500000002527113433130472020351 0ustar jdufresnejdufresne00000000000000Changelog ========= 0.24.0 (2019-02-19) ~~~~~~~~~~~~~~~~~~~ * The project has moved to `Jazzband `_. This is the first release under the new organization. The new repository URL is ``_. * Added support for Django 2.2. * Fixed a race condition in ``TaggableManager``. * Removed method ``ItemBase.bulk_lookup_kwargs()``. * Fixed view ``tagged_object_list`` to set ``queryset.model`` as ``ListView.model`` (was previously set as a ``ContentType`` instance). * ``_TaggableManager`` and ``TaggableManager`` now always call the parent class ``__init__``. * Removed ``TaggableRel`` and replaced uses with ``ManyToManyRel``. 0.23.0 (2018-08-07) ~~~~~~~~~~~~~~~~~~~ * **Backwards incompatible:** Remove support for Django < 1.11 * https://github.com/jazzband/django-taggit/pull/518 * Added support for Django 2.1 and Python 3.7 * https://github.com/jazzband/django-taggit/pull/536 * Moved TagWidget value conversion from TagWidget.render() to TagWidget.format_value() * https://github.com/jazzband/django-taggit/pull/534 0.22.2 (2017-12-27) ~~~~~~~~~~~~~~~~~~~ * Added support for Django 2.0 * https://github.com/jazzband/django-taggit/pull/509 * **Backwards incompatible:** Dropped support for EOL Python 3.3 * https://github.com/jazzband/django-taggit/pull/512 0.22.1 (2017-04-22) ~~~~~~~~~~~~~~~~~~~ * Update spanish translation * https://github.com/jazzband/django-taggit/pull/473 * Add testing for Django 1.11 and Python 3.6 * https://github.com/jazzband/django-taggit/pull/475 * introduce isort and flake8 in the CI * https://github.com/jazzband/django-taggit/pull/476 * [docs] Fixed links to external apps * https://github.com/jazzband/django-taggit/pull/481 * Improved auto-slug in TagBase to support UUID pk * https://github.com/jazzband/django-taggit/pull/482 * [docs] Added contribution guidelines * https://github.com/jazzband/django-taggit/pull/480 0.22.0 (2017-01-29) ~~~~~~~~~~~~~~~~~~~ * **Backwards incompatible:** Drop support for Django 1.7 * https://github.com/jazzband/django-taggit/pull/465 0.21.6 (2017-01-25) ~~~~~~~~~~~~~~~~~~~ * Fix case-insensitive tag creation when setting to a mix of new and existing tags are used * https://github.com/jazzband/django-taggit/pull/464 0.21.5 (2017-01-21) ~~~~~~~~~~~~~~~~~~~ * Check for case-insensitive duplicates when creating new tags * https://github.com/jazzband/django-taggit/pull/461 0.21.4 (2017-01-10) ~~~~~~~~~~~~~~~~~~~ * Support __gt__ and __lt__ ordering on Tags * https://github.com/jazzband/django-taggit/pull/456 0.21.3 (2016-10-07) ~~~~~~~~~~~~~~~~~~~ * Fix list view * https://github.com/jazzband/django-taggit/pull/444 0.21.2 (2016-08-31) ~~~~~~~~~~~~~~~~~~~ * Update Python version classifiers in setup.py * https://github.com/jazzband/django-taggit/pull/438 * Add Greek translation * https://github.com/jazzband/django-taggit/pull/439 0.21.1 (2016-08-25) ~~~~~~~~~~~~~~~~~~~ * Document supported versions of Django; fix Travis to test these versions. * https://github.com/jazzband/django-taggit/pull/435 0.21.0 (2016-08-22) ~~~~~~~~~~~~~~~~~~~ * Fix form tests on Django 1.10 * https://github.com/jazzband/django-taggit/pull/433 * Address list_display and fieldsets in admin docs * https://github.com/jazzband/django-taggit/pull/429 * external_apps.txt improvements * https://github.com/jazzband/django-taggit/pull/428 * Remove support for Django 1.4-1.6, again. * https://github.com/jazzband/django-taggit/pull/427 0.20.2 (2016-07-11) ~~~~~~~~~~~~~~~~~~~ * Add extra_filters argument to the manager's most_common method * https://github.com/jazzband/django-taggit/pull/422 0.20.1 (2016-06-23) ~~~~~~~~~~~~~~~~~~~ * Specify `app_label` for `Tag` and `TaggedItem` * https://github.com/jazzband/django-taggit/pull/420 0.20.0 (2016-06-19) ~~~~~~~~~~~~~~~~~~~ * Fix UnboundLocalError in _TaggableManager.set(..) * https://github.com/jazzband/django-taggit/pull/418 * Update doc links to reflect RTD domain changes * https://github.com/jazzband/django-taggit/pull/417 * Improve Russian translations * https://github.com/jazzband/django-taggit/pull/416 0.19.1 (2016-05-25) ~~~~~~~~~~~~~~~~~~~ * Add app config, add simplified Chinese translation file * https://github.com/jazzband/django-taggit/pull/410 0.19.0 (2016-05-23) ~~~~~~~~~~~~~~~~~~~ * Implementation of m2m_changed signal sending * https://github.com/jazzband/django-taggit/pull/409 * Code and tooling improvements * https://github.com/jazzband/django-taggit/pull/408 0.18.3 (2016-05-12) ~~~~~~~~~~~~~~~~~~~ * Added Spanish and Turkish translations * https://github.com/jazzband/django-taggit/pull/402 0.18.2 (2016-05-08) ~~~~~~~~~~~~~~~~~~~ * Add the min_count parameter to managers.most_common function * https://github.com/jazzband/django-taggit/pull/400 0.18.1 (2016-03-30) ~~~~~~~~~~~~~~~~~~~ * Address deprecation warnings * https://github.com/jazzband/django-taggit/pull/385 0.18.0 (2016-01-18) ~~~~~~~~~~~~~~~~~~~ * Add option to override default tag string parsing * https://github.com/jazzband/django-taggit/pull/232 * Drop support for Python 2.6 * https://github.com/jazzband/django-taggit/pull/373 0.17.6 (2015-12-09) ~~~~~~~~~~~~~~~~~~~ * Silence Django 1.9 warning * https://github.com/jazzband/django-taggit/pull/366 0.17.5 (2015-11-27) ~~~~~~~~~~~~~~~~~~~ * Django 1.9 compatibility fix * https://github.com/jazzband/django-taggit/pull/364 0.17.4 (2015-11-25) ~~~~~~~~~~~~~~~~~~~ * Allows custom Through Model with GenericForeignKey * https://github.com/jazzband/django-taggit/pull/359 0.17.3 (2015-10-26) ~~~~~~~~~~~~~~~~~~~ * Silence Django 1.9 warning about on_delete * https://github.com/jazzband/django-taggit/pull/338 0.17.2 (2015-10-25) ~~~~~~~~~~~~~~~~~~~ * Django 1.9 beta compatibility * https://github.com/jazzband/django-taggit/pull/352 0.17.1 (2015-09-10) ~~~~~~~~~~~~~~~~~~~ * Fix unknown column `object_id` issue with Django 1.6+ * https://github.com/jazzband/django-taggit/pull/305 0.17.0 (2015-08-14) ~~~~~~~~~~~~~~~~~~~ * Database index added on TaggedItem fields content_type & object_id * https://github.com/jazzband/django-taggit/pull/319 0.16.4 (2015-08-13) ~~~~~~~~~~~~~~~~~~~ * Access default manager via class instead of instance * https://github.com/jazzband/django-taggit/pull/335 0.16.3 (2015-08-08) ~~~~~~~~~~~~~~~~~~~ * Prevent IntegrityError with custom TagBase classes * https://github.com/jazzband/django-taggit/pull/334 0.16.2 (2015-07-13) ~~~~~~~~~~~~~~~~~~~ * Fix an admin bug related to the `Manager` property `through_fields` * https://github.com/jazzband/django-taggit/pull/328 0.16.1 (2015-07-09) ~~~~~~~~~~~~~~~~~~~ * Fix bug that assumed all primary keys are named 'id' * https://github.com/jazzband/django-taggit/pull/322 0.16.0 (2015-07-04) ~~~~~~~~~~~~~~~~~~~ * Add option to allow case-insensitive tags * https://github.com/jazzband/django-taggit/pull/325 0.15.0 (2015-06-23) ~~~~~~~~~~~~~~~~~~~ * Fix wrong slugs for non-latin chars * Only works if optional GPL dependency (unidecode) is installed * https://github.com/jazzband/django-taggit/pull/315 * https://github.com/jazzband/django-taggit/pull/273 0.14.0 (2015-04-26) ~~~~~~~~~~~~~~~~~~~ * Prevent extra JOIN when prefetching * https://github.com/jazzband/django-taggit/pull/275 * Prevent _meta warnings with Django 1.8 * https://github.com/jazzband/django-taggit/pull/299 0.13.0 (2015-04-02) ~~~~~~~~~~~~~~~~~~~ * Django 1.8 support * https://github.com/jazzband/django-taggit/pull/297 0.12.3 (2015-03-03) ~~~~~~~~~~~~~~~~~~~ * Specify that the internal type of the TaggitManager is a ManyToManyField 0.12.2 (2014-21-09) ~~~~~~~~~~~~~~~~~~~ * Fixed 1.7 migrations. 0.12.1 (2014-10-08) ~~~~~~~~~~~~~~~~~~~ * Final (hopefully) fixes for the upcoming Django 1.7 release. * Added Japanese translation. 0.12.0 (2014-20-04) ~~~~~~~~~~~~~~~~~~~ * **Backwards incompatible:** Support for Django 1.7 migrations. South users have to set ``SOUTH_MIGRATION_MODULES`` to use ``taggit.south_migrations`` for taggit. * **Backwards incompatible:** Django's new transaction handling is used on Django 1.6 and newer. * **Backwards incompatible:** ``Tag.save`` got changed to opportunistically try to save the tag and if that fails fall back to selecting existing similar tags and retry -- if that fails too an ``IntegrityError`` is raised by the database, your app will have to handle that. * Added Italian and Esperanto translations. 0.11.2 (2013-13-12) ~~~~~~~~~~~~~~~~~~~ * Forbid multiple TaggableManagers via generic foreign keys. 0.11.1 (2013-25-11) ~~~~~~~~~~~~~~~~~~~ * Fixed support for Django 1.4 and 1.5. 0.11.0 (2013-25-11) ~~~~~~~~~~~~~~~~~~~ * Added support for prefetch_related on tags fields. * Fixed support for Django 1.7. * Made the tagging relations unserializeable again. * Allow more than one TaggableManager on models (assuming concrete FKs are used for the relations). 0.10.0 (2013-17-08) ~~~~~~~~~~~~~~~~~~~ * Support for Django 1.6 and 1.7. * Python3 support * **Backwards incompatible:** Dropped support for Django < 1.4.5. * Tag names are unique now, use the provided South migrations to upgrade. 0.9.2 (2011-01-17) ~~~~~~~~~~~~~~~~~~ * **Backwards incompatible:** Forms containing a :class:`TaggableManager` by default now require tags, to change this provide ``blank=True`` to the :class:`TaggableManager`. * Now works with Django 1.3 (as of beta-1). 0.9.0 (2010-09-22) ~~~~~~~~~~~~~~~~~~ * 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 `_. * Custom ``Tag`` models are now allowed. * **Backwards incompatible:** Filtering on tags is no longer ``filter(tags__in=["foo"])``, it is written ``filter(tags__name__in=["foo"])``. * Added a German locale. * Added a Dutch locale. * Removed ``taggit.contrib.suggest``, it now lives in an external application, see :doc:`external_apps` for more information. 0.8.0 (2010-06-22) ~~~~~~~~~~~~~~~~~~ * Fixed querying for objects using ``exclude(tags__in=tags)``. * Marked strings as translatable. * Added a Russian translation. * Created a `mailing list `_. * Smarter tagstring parsing for form field; ported from Jonathan Buchanan's `django-tagging `_. 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. django-taggit-0.24.0/LICENSE0000664000175000017500000000301013376364411017321 0ustar jdufresnejdufresne00000000000000Copyright (c) Alex Gaynor and individual contributors. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of django-taggit nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. django-taggit-0.24.0/MANIFEST.in0000664000175000017500000000025613376364411020063 0ustar jdufresnejdufresne00000000000000include AUTHORS include CHANGELOG.txt include LICENSE include README.rst include tox.ini recursive-include docs * recursive-include taggit/locale * recursive-include tests * django-taggit-0.24.0/PKG-INFO0000664000175000017500000000645613433132635017425 0ustar jdufresnejdufresne00000000000000Metadata-Version: 1.2 Name: django-taggit Version: 0.24.0 Summary: django-taggit is a reusable Django application for simple tagging. Home-page: https://github.com/jazzband/django-taggit/tree/master Author: Alex Gaynor Author-email: alex.gaynor@gmail.com License: BSD Description: django-taggit ============= .. image:: https://jazzband.co/static/img/badge.svg :target: https://jazzband.co/ :alt: Jazzband .. image:: https://travis-ci.org/jazzband/django-taggit.svg?branch=master :target: https://travis-ci.org/jazzband/django-taggit .. image:: https://codecov.io/gh/jazzband/django-taggit/coverage.svg?branch=master :target: https://codecov.io/gh/jazzband/django-taggit?branch=master This is a `Jazzband `_ project. By contributing you agree to abide by the `Contributor Code of Conduct `_ and follow the `guidelines `_. ``django-taggit`` a simpler approach to tagging with Django. Add ``"taggit"`` to your ``INSTALLED_APPS`` then just add a TaggableManager to your model and go: .. code:: python from django.db import models from taggit.managers import TaggableManager class Food(models.Model): # ... fields here tags = TaggableManager() Then you can use the API like so: .. code:: python >>> apple = Food.objects.create(name="apple") >>> apple.tags.add("red", "green", "delicious") >>> apple.tags.all() [, , ] >>> apple.tags.remove("green") >>> apple.tags.all() [, ] >>> Food.objects.filter(tags__name__in=["red"]) [, ] Tags will show up for you automatically in forms and the admin. ``django-taggit`` requires Django 1.11 or greater. For more info check out the `documentation `_. And for questions about usage or development you can contact the `mailinglist `_. Platform: UNKNOWN Classifier: Development Status :: 5 - Production/Stable Classifier: Environment :: Web Environment Classifier: Framework :: Django Classifier: Framework :: Django :: 1.11 Classifier: Framework :: Django :: 2.0 Classifier: Framework :: Django :: 2.1 Classifier: Framework :: Django :: 2.2 Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: BSD License Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.4 Classifier: Programming Language :: Python :: 3.5 Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: 3.7 Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.* django-taggit-0.24.0/README.rst0000664000175000017500000000334413432313636020011 0ustar jdufresnejdufresne00000000000000django-taggit ============= .. image:: https://jazzband.co/static/img/badge.svg :target: https://jazzband.co/ :alt: Jazzband .. image:: https://travis-ci.org/jazzband/django-taggit.svg?branch=master :target: https://travis-ci.org/jazzband/django-taggit .. image:: https://codecov.io/gh/jazzband/django-taggit/coverage.svg?branch=master :target: https://codecov.io/gh/jazzband/django-taggit?branch=master This is a `Jazzband `_ project. By contributing you agree to abide by the `Contributor Code of Conduct `_ and follow the `guidelines `_. ``django-taggit`` a simpler approach to tagging with Django. Add ``"taggit"`` to your ``INSTALLED_APPS`` then just add a TaggableManager to your model and go: .. code:: python from django.db import models from taggit.managers import TaggableManager class Food(models.Model): # ... fields here tags = TaggableManager() Then you can use the API like so: .. code:: python >>> apple = Food.objects.create(name="apple") >>> apple.tags.add("red", "green", "delicious") >>> apple.tags.all() [, , ] >>> apple.tags.remove("green") >>> apple.tags.all() [, ] >>> Food.objects.filter(tags__name__in=["red"]) [, ] Tags will show up for you automatically in forms and the admin. ``django-taggit`` requires Django 1.11 or greater. For more info check out the `documentation `_. And for questions about usage or development you can contact the `mailinglist `_. django-taggit-0.24.0/django_taggit.egg-info/0000775000175000017500000000000013433132635022610 5ustar jdufresnejdufresne00000000000000django-taggit-0.24.0/django_taggit.egg-info/PKG-INFO0000664000175000017500000000645613433132635023720 0ustar jdufresnejdufresne00000000000000Metadata-Version: 1.2 Name: django-taggit Version: 0.24.0 Summary: django-taggit is a reusable Django application for simple tagging. Home-page: https://github.com/jazzband/django-taggit/tree/master Author: Alex Gaynor Author-email: alex.gaynor@gmail.com License: BSD Description: django-taggit ============= .. image:: https://jazzband.co/static/img/badge.svg :target: https://jazzband.co/ :alt: Jazzband .. image:: https://travis-ci.org/jazzband/django-taggit.svg?branch=master :target: https://travis-ci.org/jazzband/django-taggit .. image:: https://codecov.io/gh/jazzband/django-taggit/coverage.svg?branch=master :target: https://codecov.io/gh/jazzband/django-taggit?branch=master This is a `Jazzband `_ project. By contributing you agree to abide by the `Contributor Code of Conduct `_ and follow the `guidelines `_. ``django-taggit`` a simpler approach to tagging with Django. Add ``"taggit"`` to your ``INSTALLED_APPS`` then just add a TaggableManager to your model and go: .. code:: python from django.db import models from taggit.managers import TaggableManager class Food(models.Model): # ... fields here tags = TaggableManager() Then you can use the API like so: .. code:: python >>> apple = Food.objects.create(name="apple") >>> apple.tags.add("red", "green", "delicious") >>> apple.tags.all() [, , ] >>> apple.tags.remove("green") >>> apple.tags.all() [, ] >>> Food.objects.filter(tags__name__in=["red"]) [, ] Tags will show up for you automatically in forms and the admin. ``django-taggit`` requires Django 1.11 or greater. For more info check out the `documentation `_. And for questions about usage or development you can contact the `mailinglist `_. Platform: UNKNOWN Classifier: Development Status :: 5 - Production/Stable Classifier: Environment :: Web Environment Classifier: Framework :: Django Classifier: Framework :: Django :: 1.11 Classifier: Framework :: Django :: 2.0 Classifier: Framework :: Django :: 2.1 Classifier: Framework :: Django :: 2.2 Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: BSD License Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.4 Classifier: Programming Language :: Python :: 3.5 Classifier: Programming Language :: Python :: 3.6 Classifier: Programming Language :: Python :: 3.7 Requires-Python: >=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.* django-taggit-0.24.0/django_taggit.egg-info/SOURCES.txt0000664000175000017500000000416113433132635024476 0ustar jdufresnejdufresne00000000000000AUTHORS CHANGELOG.txt LICENSE MANIFEST.in README.rst setup.cfg setup.py tox.ini django_taggit.egg-info/PKG-INFO django_taggit.egg-info/SOURCES.txt django_taggit.egg-info/dependency_links.txt django_taggit.egg-info/not-zip-safe django_taggit.egg-info/requires.txt django_taggit.egg-info/top_level.txt docs/Makefile docs/admin.txt docs/api.txt docs/changelog.txt docs/conf.py docs/contributing.txt docs/custom_tagging.txt docs/external_apps.txt docs/forms.txt docs/getting_started.txt docs/index.txt taggit/__init__.py taggit/admin.py taggit/apps.py taggit/forms.py taggit/managers.py taggit/models.py taggit/utils.py taggit/views.py taggit/locale/cs/LC_MESSAGES/django.mo taggit/locale/cs/LC_MESSAGES/django.po taggit/locale/de/LC_MESSAGES/django.mo taggit/locale/de/LC_MESSAGES/django.po taggit/locale/el/LC_MESSAGES/django.mo taggit/locale/el/LC_MESSAGES/django.po taggit/locale/en/LC_MESSAGES/django.po taggit/locale/eo/LC_MESSAGES/django.mo taggit/locale/eo/LC_MESSAGES/django.po taggit/locale/es/LC_MESSAGES/django.mo taggit/locale/es/LC_MESSAGES/django.po taggit/locale/he/LC_MESSAGES/django.mo taggit/locale/he/LC_MESSAGES/django.po taggit/locale/it/LC_MESSAGES/django.mo taggit/locale/it/LC_MESSAGES/django.po taggit/locale/ja/LC_MESSAGES/django.mo taggit/locale/ja/LC_MESSAGES/django.po taggit/locale/nb/LC_MESSAGES/django.mo taggit/locale/nb/LC_MESSAGES/django.po taggit/locale/nl/LC_MESSAGES/django.mo taggit/locale/nl/LC_MESSAGES/django.po taggit/locale/pt_BR/LC_MESSAGES/django.mo taggit/locale/pt_BR/LC_MESSAGES/django.po taggit/locale/ru/LC_MESSAGES/django.mo taggit/locale/ru/LC_MESSAGES/django.po taggit/locale/tr/LC_MESSAGES/django.mo taggit/locale/tr/LC_MESSAGES/django.po taggit/locale/zh_Hans/LC_MESSAGES/django.mo taggit/locale/zh_Hans/LC_MESSAGES/django.po taggit/migrations/0001_initial.py taggit/migrations/0002_auto_20150616_2121.py taggit/migrations/__init__.py tests/__init__.py tests/custom_parser.py tests/forms.py tests/models.py tests/settings.py tests/tests.py tests/urls.py tests/views.py tests/migrations/0001_initial.py tests/migrations/0002_uuid_models.py tests/migrations/__init__.py tests/templates/tests/food_tag_list.htmldjango-taggit-0.24.0/django_taggit.egg-info/dependency_links.txt0000664000175000017500000000000113433132635026656 0ustar jdufresnejdufresne00000000000000 django-taggit-0.24.0/django_taggit.egg-info/not-zip-safe0000664000175000017500000000000113433132635025036 0ustar jdufresnejdufresne00000000000000 django-taggit-0.24.0/django_taggit.egg-info/requires.txt0000664000175000017500000000001513433132635025204 0ustar jdufresnejdufresne00000000000000Django>=1.11 django-taggit-0.24.0/django_taggit.egg-info/top_level.txt0000664000175000017500000000000713433132635025337 0ustar jdufresnejdufresne00000000000000taggit django-taggit-0.24.0/docs/0000775000175000017500000000000013433132635017245 5ustar jdufresnejdufresne00000000000000django-taggit-0.24.0/docs/Makefile0000664000175000017500000000610413376364411020713 0ustar jdufresnejdufresne00000000000000# Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build PAPER = BUILDDIR = _build # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . .PHONY: help clean html dirhtml pickle json htmlhelp qthelp latex changes linkcheck doctest help: @echo "Please use \`make ' where is one of" @echo " html to make standalone HTML files" @echo " dirhtml to make HTML files named index.html in directories" @echo " pickle to make pickle files" @echo " json to make JSON files" @echo " htmlhelp to make HTML files and a HTML help project" @echo " qthelp to make HTML files and a qthelp project" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " changes to make an overview of all changed/added/deprecated items" @echo " linkcheck to check all external links for integrity" @echo " doctest to run all doctests embedded in the documentation (if enabled)" clean: -rm -rf $(BUILDDIR)/* html: $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." dirhtml: $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." pickle: $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle @echo @echo "Build finished; now you can process the pickle files." json: $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json @echo @echo "Build finished; now you can process the JSON files." htmlhelp: $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in $(BUILDDIR)/htmlhelp." qthelp: $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp @echo @echo "Build finished; now you can run "qcollectiongenerator" with the" \ ".qhcp project file in $(BUILDDIR)/qthelp, like this:" @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/django-taggit.qhcp" @echo "To view the help file:" @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/django-taggit.qhc" latex: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." @echo "Run \`make all-pdf' or \`make all-ps' in that directory to" \ "run these through (pdf)latex." changes: $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes @echo @echo "The overview file is in $(BUILDDIR)/changes." linkcheck: $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in $(BUILDDIR)/linkcheck/output.txt." doctest: $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest @echo "Testing of doctests in the sources finished, look at the " \ "results in $(BUILDDIR)/doctest/output.txt." django-taggit-0.24.0/docs/admin.txt0000664000175000017500000000272213376364411021106 0ustar jdufresnejdufresne00000000000000Using tags in the admin ======================= By default if you have a :class:`TaggableManager` on your model it will show up in the admin, just as it will in any other form. If you are specifying :attr:`ModelAdmin.fieldsets`, include the name of the :class:`TaggableManager` as a field:: fieldsets = ( (None, {'fields': ('tags',)}), ) Including tags in :attr:`ModelAdmin.list_display` ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ One important thing to note is that you *cannot* include a :class:`TaggableManager` in :attr:`ModelAdmin.list_display`. If you do you'll see an exception that looks like:: AttributeError: '_TaggableManager' object has no attribute 'name' This is for the same reason that you cannot include a :class:`ManyToManyField`: it would result in an unreasonable number of queries being executed. If you want to show tags in :attr:`ModelAdmin.list_display`, you can add a custom display method to the :class:`ModelAdmin`, using ``prefetch_related`` to minimize queries:: class MyModelAdmin(admin.ModelAdmin): list_display = ['tag_list'] def get_queryset(self, request): return super(MyModelAdmin, self).get_queryset(request).prefetch_related('tags') def tag_list(self, obj): return u", ".join(o.name for o in obj.tags.all()) For details, see the `Django documentation `_. django-taggit-0.24.0/docs/api.txt0000664000175000017500000000763213376364411020574 0ustar jdufresnejdufresne00000000000000The API ======= After you've got your ``TaggableManager`` added to your model you can start playing around with the API. .. class:: TaggableManager([verbose_name="Tags", help_text="A comma-separated list of tags.", through=None, blank=False]) :param verbose_name: The verbose_name for this field. :param help_text: The help_text to be used in forms (including the admin). :param through: The through model, see :doc:`custom_tagging` for more information. :param blank: Controls whether this field is required. .. 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, clear=False) If ``clear = True`` removes all the current tags and then adds the specified tags to the object. Otherwise sets the object's tags to those specified, removing only the missing tags and adding only the new tags. .. 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. :param min_count: Specify a min count to limit the returned queryset .. 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. .. method:: names() Convenience method, returning a ``ValuesListQuerySet`` (basically just an iterable) containing the name of each tag as a string:: >>> apple.tags.names() [u'green and juicy', u'red'] .. method:: slugs() Convenience method, returning a ``ValuesListQuerySet`` (basically just an iterable) containing the slug of each tag as a string:: >>> apple.tags.slugs() [u'green-and-juicy', u'red'] .. hint:: You can subclass ``_TaggableManager`` (note the underscore) to add methods or functionality. ``TaggableManager`` takes an optional manager keyword argument for your custom class, like this:: class Food(models.Model): # ... fields here tags = TaggableManager(manager=_CustomTaggableManager) Filtering ~~~~~~~~~ To find all of a model with a specific tags you can filter, using the normal Django ORM API. For example if you had a ``Food`` model, whose ``TaggableManager`` was named ``tags``, you could find all the delicious fruit like so:: >>> Food.objects.filter(tags__name__in=["delicious"]) [, , ] 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__name__in=["delicious", "red"]) [, ] >>> Food.objects.filter(tags__name__in=["delicious", "red"]).distinct() [] You can also filter by the slug on tags. If you're using a custom ``Tag`` model you can use this API to filter on any fields it has. django-taggit-0.24.0/docs/changelog.txt0000664000175000017500000000003613376364411021741 0ustar jdufresnejdufresne00000000000000.. include:: ../CHANGELOG.txt django-taggit-0.24.0/docs/conf.py0000664000175000017500000001467513433130472020556 0ustar jdufresnejdufresne00000000000000# -*- coding: utf-8 -*- # # django-taggit documentation build configuration file, created by # sphinx-quickstart on Mon May 3 22:22:47 2010. # # This file is execfile()d with the current directory set to its containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. import os import sys # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. # sys.path.append(os.path.abspath('.')) # -- General configuration ----------------------------------------------------- # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. extensions = ["sphinx.ext.intersphinx"] # Add any paths that contain templates here, relative to this directory. templates_path = ["_templates"] # The suffix of source filenames. source_suffix = ".txt" # The encoding of source files. # source_encoding = 'utf-8' # The master toctree document. master_doc = "index" # General information about the project. project = u"django-taggit" copyright = u"2010-2014, Alex Gaynor and others." # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. version = "0.24" # The full version, including alpha/beta/rc tags. release = "0.24.0" # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. # language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: # today = '' # Else, today_fmt is used as the format for a strftime call. # today_fmt = '%B %d, %Y' # List of documents that shouldn't be included in the build. # unused_docs = [] # List of directories, relative to source directory, that shouldn't be searched # for source files. exclude_trees = ["_build"] # The reST default role (used for this markup: `text`) to use for all documents. # default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. # add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). # add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. # show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = "sphinx" # A list of ignored prefixes for module index sorting. # modindex_common_prefix = [] # -- Options for HTML output --------------------------------------------------- # The theme to use for HTML and HTML Help pages. Major themes that come with # Sphinx are currently 'default' and 'sphinxdoc'. html_theme = "default" # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. # html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. # html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". # html_title = None # A shorter title for the navigation bar. Default is the same as html_title. # html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. # html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. # html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". # html_static_path = ['_static'] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. # html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. # html_use_smartypants = True # Custom sidebar templates, maps document names to template names. # html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. # html_additional_pages = {} # If false, no module index is generated. # html_use_modindex = True # If false, no index is generated. # html_use_index = True # If true, the index is split into individual pages for each letter. # html_split_index = False # If true, links to the reST sources are added to the pages. # html_show_sourcelink = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. # html_use_opensearch = '' # If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml"). # html_file_suffix = '' # Output file base name for HTML help builder. htmlhelp_basename = "django-taggitdoc" # -- Options for LaTeX output -------------------------------------------------- # The paper size ('letter' or 'a4'). # latex_paper_size = 'letter' # The font size ('10pt', '11pt' or '12pt'). # latex_font_size = '10pt' # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass [howto/manual]). latex_documents = [ ( "index", "django-taggit.tex", u"django-taggit Documentation", u"Alex Gaynor", "manual", ) ] # The name of an image file (relative to this directory) to place at the top of # the title page. # latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. # latex_use_parts = False # Additional stuff for the LaTeX preamble. # latex_preamble = '' # Documents to append as an appendix to all manuals. # latex_appendices = [] # If false, no module index is generated. # latex_use_modindex = True # Example configuration for intersphinx: refer to the Python standard library. intersphinx_mapping = {"https://docs.python.org/": None} django-taggit-0.24.0/docs/contributing.txt0000664000175000017500000000004113432313636022511 0ustar jdufresnejdufresne00000000000000.. include:: ../CONTRIBUTING.rst django-taggit-0.24.0/docs/custom_tagging.txt0000664000175000017500000001670713376364411023040 0ustar jdufresnejdufresne00000000000000Customizing taggit ================== Using a Custom Tag or Through Model ----------------------------------- By default ``django-taggit`` uses a "through model" with a ``GenericForeignKey`` on it, that has another ``ForeignKey`` to an included ``Tag`` model. However, there are some cases where this isn't desirable, for example if you want the speed and referential guarantees of a real ``ForeignKey``, if you have a model with a non-integer primary key, or if you want to store additional data about a tag, such as whether it is official. In these cases ``django-taggit`` makes it easy to substitute your own through model, or ``Tag`` model. To change the behavior there are a number of classes you can subclass to obtain different behavior: =============================== ======================================================================= Class name Behavior =============================== ======================================================================= ``TaggedItemBase`` Allows custom ``ForeignKeys`` to models. ``GenericTaggedItemBase`` Allows custom ``Tag`` models. Tagged models use an integer primary key. ``GenericUUIDTaggedItemBase`` Allows custom ``Tag`` models. Tagged models use a UUID primary key. ``CommonGenericTaggedItemBase`` Allows custom ``Tag`` models and ``GenericForeignKeys`` to models. ``ItemBase`` Allows custom ``Tag`` models and ``ForeignKeys`` to models. =============================== ======================================================================= Custom ForeignKeys ~~~~~~~~~~~~~~~~~~ Your intermediary model must be a subclass of ``taggit.models.TaggedItemBase`` with a foreign key to your content model named ``content_object``. Pass this intermediary model as the ``through`` argument to ``TaggableManager``:: from django.db import models from taggit.managers import TaggableManager from taggit.models import TaggedItemBase class TaggedFood(TaggedItemBase): content_object = models.ForeignKey('Food') class Food(models.Model): # ... fields here tags = TaggableManager(through=TaggedFood) Once this is done, the API works the same as for GFK-tagged models. Custom GenericForeignKeys ~~~~~~~~~~~~~~~~~~~~~~~~~ The default ``GenericForeignKey`` used by ``django-taggit`` assume your tagged object use an integer primary key. For non-integer primary key, your intermediary model must be a subclass of ``taggit.models.CommonGenericTaggedItemBase`` with a field named ``"object_id"`` of the type of your primary key. For example, if your primary key is a string:: from django.db import models from taggit.managers import TaggableManager from taggit.models import CommonGenericTaggedItemBase, TaggedItemBase class GenericStringTaggedItem(CommonGenericTaggedItemBase, TaggedItemBase): object_id = models.CharField(max_length=50, verbose_name=_('Object id'), db_index=True) class Food(models.Model): food_id = models.CharField(primary_key=True) # ... fields here tags = TaggableManager(through=GenericStringTaggedItem) GenericUUIDTaggedItemBase ~~~~~~~~~~~~~~~~~~~~~~~~~ A common use case of a non-integer primary key, is UUID primary key. ``django-taggit`` provides a base class ``GenericUUIDTaggedItemBase`` ready to use with models using an UUID primary key:: from django.db import models from django.utils.translation import ugettext_lazy as _ from taggit.managers import TaggableManager from taggit.models import GenericUUIDTaggedItemBase, TaggedItemBase class UUIDTaggedItem(GenericUUIDTaggedItemBase, TaggedItemBase): # If you only inherit GenericUUIDTaggedItemBase, you need to define # a tag field. e.g. # tag = models.ForeignKey(Tag, related_name="uuid_tagged_items", on_delete=models.CASCADE) class Meta: verbose_name = _("Tag") verbose_name_plural = _("Tags") class Food(models.Model): id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) # ... fields here tags = TaggableManager(through=UUIDTaggedItem) Custom tag ~~~~~~~~~~ When providing a custom ``Tag`` model it should be a ``ForeignKey`` to your tag model named ``"tag"``: .. code-block:: python from django.db import models from django.utils.translation import ugettext_lazy as _ from taggit.managers import TaggableManager from taggit.models import TagBase, GenericTaggedItemBase class MyCustomTag(TagBase): # ... fields here class Meta: verbose_name = _("Tag") verbose_name_plural = _("Tags") # ... methods (if any) here class TaggedWhatever(GenericTaggedItemBase): # TaggedWhatever can also extend TaggedItemBase or a combination of # both TaggedItemBase and GenericTaggedItemBase. GenericTaggedItemBase # allows using the same tag for different kinds of objects, in this # example Food and Drink. # Here is where you provide your custom Tag class. tag = models.ForeignKey(MyCustomTag, related_name="%(app_label)s_%(class)s_items") class Food(models.Model): # ... fields here tags = TaggableManager(through=TaggedWhatever) class Drink(models.Model): # ... fields here tags = TaggableManager(through=TaggedWhatever) .. class:: TagBase .. method:: slugify(tag, i=None) By default ``taggit`` uses :func:`django.utils.text.slugify` to calculate a slug for a given tag. However, if you want to implement your own logic you can override this method, which receives the ``tag`` (a string), and ``i``, which is either ``None`` or an integer, which signifies how many times the slug for this tag has been attempted to be calculated, it is ``None`` on the first time, and the counting begins at ``1`` thereafter. Using a custom tag string parser -------------------------------- By default ``django-taggit`` uses :func:`taggit.utils._parse_tags` which accepts a string which may contain one or more tags and returns a list of tag names. This parser is quite intelligent and can handle a number of edge cases; however, you may wish to provide your own parser for various reasons (e.g. you can do some preprocessing on the tags so that they are converted to lowercase, reject certain tags, disallow certain characters, split only on commas rather than commas and whitespace, etc.). To provide your own parser, write a function that takes a tag string and returns a list of tag names. For example, a simple function to split on comma and convert to lowercase:: def comma_splitter(tag_string): return [t.strip().lower() for t in tag_string.split(',') if t.strip()] You need to tell ``taggit`` to use this function instead of the default by adding a new setting, ``TAGGIT_TAGS_FROM_STRING`` and providing it with the dotted path to your function. Likewise, you can provide a function to convert a list of tags to a string representation and use the setting ``TAGGIT_STRING_FROM_TAGS`` to override the default value (which is :func:`taggit.utils._edit_string_for_tags`):: def comma_joiner(tags): return ', '.join(t.name for t in tags) If the functions above were defined in a module, ``appname.utils``, then your project settings.py file should contain the following:: TAGGIT_TAGS_FROM_STRING = 'appname.utils.comma_splitter' TAGGIT_STRING_FROM_TAGS = 'appname.utils.comma_joiner' django-taggit-0.24.0/docs/external_apps.txt0000664000175000017500000000322313432313636022654 0ustar jdufresnejdufresne00000000000000External Applications ===================== In addition to the features included in ``django-taggit`` directly, there are a number of external applications which provide additional features that may be of interest. .. note:: Despite their mention here, the following applications are in no way official, nor have they in any way been reviewed or tested. If you have an application that you'd like to see listed here, simply fork `django-taggit on github `_, add it to this list, and send a pull request. * `django-taggit-helpers `_: Makes it easier to work with admin pages of models associated with ``taggit`` tags by adding helper classes: ``TaggitCounter``, ``TaggitListFilter``, ``TaggitStackedInline``, ``TaggitTabularInline``. * `django-taggit-labels `_: Provides a clickable label widget for the Django admin for user friendly selection from managed tag sets. * `django-taggit-serializer `_: Adds functionality for using ``taggit`` with ``django-rest-framework``. * `django-taggit-suggest `_: Provides support for defining keyword and regular expression rules for suggesting new tags for content. This used to be available at ``taggit.contrib.suggest``. * `django-taggit-templatetags `_: Provides several templatetags, including one for tag clouds, to expose various ``taggit`` APIs directly to templates. django-taggit-0.24.0/docs/forms.txt0000664000175000017500000000425213376364411021144 0ustar jdufresnejdufresne00000000000000.. _tags-in-forms: Tags in forms ============= The ``TaggableManager`` will show up automatically as a field in a ``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 treated as a space-delimited list of tag names. * If the input does contain either of these characters: * Groups of characters which appear between double quotes take precedence as multi-word tags (so double quoted tag names may contain commas). An unclosed double quote will be ignored. * Otherwise, if there are any unquoted commas in the input, it will be treated as comma-delimited. If not, it will be treated as space-delimited. Examples: ====================== ================================= ================================================ Tag input string Resulting tags Notes ====================== ================================= ================================================ apple ball cat ``["apple", "ball", "cat"]`` No commas, so space delimited apple, ball cat ``["apple", "ball cat"]`` Comma present, so comma delimited "apple, ball" cat dog ``["apple, ball", "cat", "dog"]`` All commas are quoted, so space delimited "apple, ball", cat dog ``["apple, ball", "cat dog"]`` Contains an unquoted comma, so comma delimited apple "ball cat" dog ``["apple", "ball cat", "dog"]`` No commas, so space delimited "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(request.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() django-taggit-0.24.0/docs/getting_started.txt0000664000175000017500000000127513376364411023207 0ustar jdufresnejdufresne00000000000000Getting Started =============== To get started using ``django-taggit`` simply install it with ``pip``:: $ pip install django-taggit Add ``"taggit"`` to your project's ``INSTALLED_APPS`` setting. Run `./manage.py migrate`. And then to any model you want tagging on do the following:: from django.db import models from taggit.managers import TaggableManager class Food(models.Model): # ... fields here tags = TaggableManager() .. note:: If you want ``django-taggit`` to be **CASE INSENSITIVE** when looking up existing tags, you'll have to set to ``True`` the TAGGIT_CASE_INSENSITIVE setting (by default ``False``):: TAGGIT_CASE_INSENSITIVE = True django-taggit-0.24.0/docs/index.txt0000664000175000017500000000075713376364411021133 0ustar jdufresnejdufresne00000000000000Welcome to django-taggit's documentation! ========================================= ``django-taggit`` is a reusable Django application designed to make adding tagging to your project easy and fun. ``django-taggit`` works with Django 1.11+ and Python 2.7 / 3.4+. .. toctree:: :maxdepth: 2 getting_started forms admin api custom_tagging contributing external_apps changelog Indices and tables ================== * :ref:`genindex` * :ref:`modindex` * :ref:`search` django-taggit-0.24.0/setup.cfg0000664000175000017500000000051713433132635020141 0ustar jdufresnejdufresne00000000000000[metadata] license-file = LICENSE [bdist_wheel] universal = 1 [flake8] ignore = E501 exclude = .tox docs [isort] combine_as_imports = True force_grid_wrap = 0 forced_separate = taggit include_trailing_comma = True line_length = 88 multi_line_output = 3 not_skip = __init__.py skip = .tox [egg_info] tag_build = tag_date = 0 django-taggit-0.24.0/setup.py0000664000175000017500000000302413433126115020022 0ustar jdufresnejdufresne00000000000000from setuptools import find_packages, setup import taggit with open("README.rst") as f: readme = f.read() setup( name="django-taggit", version=".".join(str(i) for i in taggit.VERSION), description="django-taggit is a reusable Django application for simple tagging.", long_description=readme, author="Alex Gaynor", author_email="alex.gaynor@gmail.com", url="https://github.com/jazzband/django-taggit/tree/master", packages=find_packages(exclude=("tests*",)), package_data={"taggit": ["locale/*/LC_MESSAGES/*"]}, license="BSD", python_requires=">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*", install_requires=["Django>=1.11"], classifiers=[ "Development Status :: 5 - Production/Stable", "Environment :: Web Environment", "Framework :: Django", "Framework :: Django :: 1.11", "Framework :: Django :: 2.0", "Framework :: Django :: 2.1", "Framework :: Django :: 2.2", "Intended Audience :: Developers", "License :: OSI Approved :: BSD License", "Operating System :: OS Independent", "Programming Language :: Python", "Programming Language :: Python :: 2", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.5", "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", ], include_package_data=True, zip_safe=False, ) django-taggit-0.24.0/taggit/0000775000175000017500000000000013433132635017574 5ustar jdufresnejdufresne00000000000000django-taggit-0.24.0/taggit/__init__.py0000664000175000017500000000011113433130472021673 0ustar jdufresnejdufresne00000000000000VERSION = (0, 24, 0) default_app_config = "taggit.apps.TaggitAppConfig" django-taggit-0.24.0/taggit/admin.py0000664000175000017500000000064313433126115021235 0ustar jdufresnejdufresne00000000000000from __future__ import unicode_literals from django.contrib import admin from taggit.models import Tag, TaggedItem class TaggedItemInline(admin.StackedInline): model = TaggedItem @admin.register(Tag) class TagAdmin(admin.ModelAdmin): inlines = [TaggedItemInline] list_display = ["name", "slug"] ordering = ["name", "slug"] search_fields = ["name"] prepopulated_fields = {"slug": ["name"]} django-taggit-0.24.0/taggit/apps.py0000664000175000017500000000030013433126115021076 0ustar jdufresnejdufresne00000000000000from django.apps import AppConfig as BaseConfig from django.utils.translation import ugettext_lazy as _ class TaggitAppConfig(BaseConfig): name = "taggit" verbose_name = _("Taggit") django-taggit-0.24.0/taggit/forms.py0000664000175000017500000000150213433126115021266 0ustar jdufresnejdufresne00000000000000from __future__ import unicode_literals from django import forms from django.utils import six from django.utils.translation import ugettext as _ from taggit.utils import edit_string_for_tags, parse_tags class TagWidget(forms.TextInput): def format_value(self, value): 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")]) return super(TagWidget, self).format_value(value) class TagField(forms.CharField): widget = TagWidget def clean(self, value): value = super(TagField, self).clean(value) try: return parse_tags(value) except ValueError: raise forms.ValidationError( _("Please provide a comma-separated list of tags.") ) django-taggit-0.24.0/taggit/locale/0000775000175000017500000000000013433132635021033 5ustar jdufresnejdufresne00000000000000django-taggit-0.24.0/taggit/locale/cs/0000775000175000017500000000000013433132635021440 5ustar jdufresnejdufresne00000000000000django-taggit-0.24.0/taggit/locale/cs/LC_MESSAGES/0000775000175000017500000000000013433132635023225 5ustar jdufresnejdufresne00000000000000django-taggit-0.24.0/taggit/locale/cs/LC_MESSAGES/django.mo0000664000175000017500000000204513376364411025032 0ustar jdufresnejdufresne00000000000000Þ• |Ü !@ `m r.|«° ´ À͆Ò!Y!{ ¨ ¯)ºäéí    %(object)s tagged with %(tag)sA comma-separated list of tags.Content typeNameObject idPlease provide a comma-separated list of tags.SlugTagTagged ItemTagged ItemsTagsProject-Id-Version: PACKAGE VERSION Report-Msgid-Bugs-To: POT-Creation-Date: 2013-08-01 16:52+0200 PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE Last-Translator: FULL NAME Language-Team: LANGUAGE Language: MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Plural-Forms: nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2; %(object)s oznaÄen tagem %(tag)sČárkami oddÄ›lený seznam tagůTyp obsahuJménoID objektuVložte Äárkami oddÄ›lený seznam tagůSlugTagTagem oznaÄená položkaTagy oznaÄené položkyTagydjango-taggit-0.24.0/taggit/locale/cs/LC_MESSAGES/django.po0000664000175000017500000000264613376364411025044 0ustar jdufresnejdufresne00000000000000# 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 , YEAR. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2013-08-01 16:52+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "Language: \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==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;\n" #: forms.py:24 msgid "Please provide a comma-separated list of tags." msgstr "Vložte Äárkami oddÄ›lený seznam tagů" #: managers.py:59 models.py:59 msgid "Tags" msgstr "Tagy" #: managers.py:60 msgid "A comma-separated list of tags." msgstr "Čárkami oddÄ›lený seznam tagů" #: models.py:15 msgid "Name" msgstr "Jméno" #: models.py:16 msgid "Slug" msgstr "Slug" #: models.py:58 msgid "Tag" msgstr "Tag" #: models.py:65 #, python-format msgid "%(object)s tagged with %(tag)s" msgstr "%(object)s oznaÄen tagem %(tag)s" #: models.py:112 msgid "Object id" msgstr "ID objektu" #: models.py:115 msgid "Content type" msgstr "Typ obsahu" #: models.py:158 msgid "Tagged Item" msgstr "Tagem oznaÄená položka" #: models.py:159 msgid "Tagged Items" msgstr "Tagy oznaÄené položky" django-taggit-0.24.0/taggit/locale/de/0000775000175000017500000000000013433132635021423 5ustar jdufresnejdufresne00000000000000django-taggit-0.24.0/taggit/locale/de/LC_MESSAGES/0000775000175000017500000000000013433132635023210 5ustar jdufresnejdufresne00000000000000django-taggit-0.24.0/taggit/locale/de/LC_MESSAGES/django.mo0000664000175000017500000000246313376364411025021 0ustar jdufresnejdufresne00000000000000Þ• „ì01P p`}Þ ã.í! % 1>dC&¨+Ï û– ›:¥à èó  %   %(object)s tagged with %(tag)sA comma-separated list of tags.Content typeEnter a valid Regular Expression. To make it case-insensitive include "(?i)" in your expression.NameObject idPlease provide a comma-separated list of tags.SlugTagTagged ItemTagged ItemsTagsProject-Id-Version: django-taggit Report-Msgid-Bugs-To: POT-Creation-Date: 2010-09-07 09:26-0700 PO-Revision-Date: 2010-09-07 09:26-0700 Last-Translator: Jannis Leidel Language-Team: German MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Plural-Forms: nplurals=2; plural=(n != 1) %(object)s verschlagwortet mit %(tag)sEine durch Komma getrennte Schlagwortliste.InhaltstypBitte einen regulären Ausdruck eingeben. Fügen Sie "(?i) " dem Ausdruck hinzu, um nicht zwischen Groß- und Kleinschreibung zu unterscheiden.NameObjekt-IDBitte eine durch Komma getrennte Schlagwortliste eingeben.KürzelSchlagwortVerschlagwortetes ObjektVerschlagwortete ObjekteSchlagwörterdjango-taggit-0.24.0/taggit/locale/de/LC_MESSAGES/django.po0000664000175000017500000000310213376364411025013 0ustar jdufresnejdufresne00000000000000#, fuzzy msgid "" msgstr "" "Project-Id-Version: django-taggit\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2010-09-07 09:26-0700\n" "PO-Revision-Date: 2010-09-07 09:26-0700\n" "Last-Translator: Jannis Leidel \n" "Language-Team: German \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 "Bitte eine durch Komma getrennte Schlagwortliste eingeben." #: managers.py:39 managers.py:83 models.py:50 msgid "Tags" msgstr "Schlagwörter" #: managers.py:84 msgid "A comma-separated list of tags." msgstr "Eine durch Komma getrennte Schlagwortliste." #: models.py:10 msgid "Name" msgstr "Name" #: models.py:11 msgid "Slug" msgstr "Kürzel" #: models.py:49 msgid "Tag" msgstr "Schlagwort" #: models.py:56 #, python-format msgid "%(object)s tagged with %(tag)s" msgstr "%(object)s verschlagwortet mit %(tag)s" #: models.py:100 msgid "Object id" msgstr "Objekt-ID" #: models.py:104 models.py:110 msgid "Content type" msgstr "Inhaltstyp" #: models.py:138 msgid "Tagged Item" msgstr "Verschlagwortetes Objekt" #: models.py:139 msgid "Tagged Items" msgstr "Verschlagwortete Objekte" #: contrib/suggest/models.py:57 msgid "" "Enter a valid Regular Expression. To make it case-insensitive include \"(?i)" "\" in your expression." msgstr "" "Bitte einen regulären Ausdruck eingeben. Fügen Sie \"(?i) \" dem " "Ausdruck hinzu, um nicht zwischen Groß- und Kleinschreibung zu " "unterscheiden." django-taggit-0.24.0/taggit/locale/el/0000775000175000017500000000000013433132635021433 5ustar jdufresnejdufresne00000000000000django-taggit-0.24.0/taggit/locale/el/LC_MESSAGES/0000775000175000017500000000000013433132635023220 5ustar jdufresnejdufresne00000000000000django-taggit-0.24.0/taggit/locale/el/LC_MESSAGES/django.mo0000664000175000017500000000241613376364411025027 0ustar jdufresnejdufresne00000000000000Þ• |Ü !@ `m r.|«° ´ ÀÍjÒ0=Pn#¿ ã'î|“˜*§*Òý   %(object)s tagged with %(tag)sA comma-separated list of tags.Content typeNameObject idPlease provide a comma-separated list of tags.SlugTagTagged ItemTagged ItemsTagsProject-Id-Version: django-taggit Report-Msgid-Bugs-To: POT-Creation-Date: 2016-08-31 14:31+0300 PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE Last-Translator: Serafeim Papastefanos Language-Team: Greek MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Plural-Forms: nplurals=2; plural=(n != 1); %(object)s μαÏκαÏισμένα με %(tag)sΜια χωÏισμένη με κόμματα λίστα από ετικέτεςΕίδος πεÏιεχομένουΌνομαΚωδικός αντικειμένουΠαÏακαλοÏμε συπληÏώστε μια λίστα από ετικέτες χωÏισμένη με κόμματαSlugΕτικέταΑντικείμενο με ετικέταΑντικείμενα με ετικέταΕτικέτεςdjango-taggit-0.24.0/taggit/locale/el/LC_MESSAGES/django.po0000664000175000017500000000303613376364411025031 0ustar jdufresnejdufresne00000000000000#, fuzzy msgid "" msgstr "" "Project-Id-Version: django-taggit\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2016-08-31 14:31+0300\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: Serafeim Papastefanos \n" "Language-Team: Greek \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" #: .\apps.py:7 msgid "Taggit" msgstr "" #: .\forms.py:27 msgid "Please provide a comma-separated list of tags." msgstr "ΠαÏακαλοÏμε συπληÏώστε μια λίστα από ετικέτες χωÏισμένη με κόμματα" #: .\managers.py:387 .\models.py:79 msgid "Tags" msgstr "Ετικέτες" #: .\managers.py:388 msgid "A comma-separated list of tags." msgstr "Μια χωÏισμένη με κόμματα λίστα από ετικέτες" #: .\models.py:24 msgid "Name" msgstr "Όνομα" #: .\models.py:25 msgid "Slug" msgstr "Slug" #: .\models.py:78 msgid "Tag" msgstr "Ετικέτα" #: .\models.py:86 #, python-format msgid "%(object)s tagged with %(tag)s" msgstr "%(object)s μαÏκαÏισμένα με %(tag)s" #: .\models.py:141 msgid "Content type" msgstr "Είδος πεÏιεχομένου" #: .\models.py:185 .\models.py:194 msgid "Object id" msgstr "Κωδικός αντικειμένου" #: .\models.py:202 msgid "Tagged Item" msgstr "Αντικείμενο με ετικέτα" #: .\models.py:203 msgid "Tagged Items" msgstr "Αντικείμενα με ετικέτα" django-taggit-0.24.0/taggit/locale/en/0000775000175000017500000000000013433132635021435 5ustar jdufresnejdufresne00000000000000django-taggit-0.24.0/taggit/locale/en/LC_MESSAGES/0000775000175000017500000000000013433132635023222 5ustar jdufresnejdufresne00000000000000django-taggit-0.24.0/taggit/locale/en/LC_MESSAGES/django.po0000664000175000017500000000250513376364411025033 0ustar jdufresnejdufresne00000000000000# 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 , YEAR. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2010-09-07 09:45-0700\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" #: forms.py:20 msgid "Please provide a comma-separated list of tags." msgstr "" #: managers.py:39 managers.py:83 models.py:50 msgid "Tags" msgstr "" #: managers.py:84 msgid "A comma-separated list of tags." msgstr "" #: models.py:10 msgid "Name" msgstr "" #: models.py:11 msgid "Slug" msgstr "" #: models.py:49 msgid "Tag" msgstr "" #: models.py:56 #, python-format msgid "%(object)s tagged with %(tag)s" msgstr "" #: models.py:100 msgid "Object id" msgstr "" #: models.py:104 models.py:110 msgid "Content type" msgstr "" #: models.py:138 msgid "Tagged Item" msgstr "" #: models.py:139 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 "" django-taggit-0.24.0/taggit/locale/eo/0000775000175000017500000000000013433132635021436 5ustar jdufresnejdufresne00000000000000django-taggit-0.24.0/taggit/locale/eo/LC_MESSAGES/0000775000175000017500000000000013433132635023223 5ustar jdufresnejdufresne00000000000000django-taggit-0.24.0/taggit/locale/eo/LC_MESSAGES/django.mo0000664000175000017500000000245213376364411025032 0ustar jdufresnejdufresne00000000000000Þ• „ì01P p`}Þ ã.í! % 1>±Cõ& 9ZCž £6® åñù !   %(object)s tagged with %(tag)sA comma-separated list of tags.Content typeEnter a valid Regular Expression. To make it case-insensitive include "(?i)" in your expression.NameObject idPlease provide a comma-separated list of tags.SlugTagTagged ItemTagged ItemsTagsProject-Id-Version: django-taggit Report-Msgid-Bugs-To: POT-Creation-Date: 2010-09-07 09:26-0700 PO-Revision-Date: 2014-03-29 18:57+0100 Last-Translator: Baptiste Darthenay Language-Team: Esperanto MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Plural-Forms: nplurals=2; plural=(n != 1); Language: eo X-Generator: Poedit 1.5.4 %(object)s etikedita %(tag)sListo da etikedoj apartitaj per komoj.EnhavtipoEntajpu validan regulan esprimon. Por ke estu usklecoblinda, enmetu "(?i)" en via esprimo.NomoObjekto IDBonvolu enmeti liston da etikedoj apartitaj per komoj.Ä´etonvortoEtikedoEtikedita elementoEtikeditaj elementojEtikedojdjango-taggit-0.24.0/taggit/locale/eo/LC_MESSAGES/django.po0000664000175000017500000000306513376364411025036 0ustar jdufresnejdufresne00000000000000msgid "" msgstr "" "Project-Id-Version: django-taggit\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2010-09-07 09:26-0700\n" "PO-Revision-Date: 2014-03-29 18:57+0100\n" "Last-Translator: Baptiste Darthenay \n" "Language-Team: Esperanto \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" "Language: eo\n" "X-Generator: Poedit 1.5.4\n" #: forms.py:20 msgid "Please provide a comma-separated list of tags." msgstr "Bonvolu enmeti liston da etikedoj apartitaj per komoj." #: managers.py:39 managers.py:83 models.py:50 msgid "Tags" msgstr "Etikedoj" #: managers.py:84 msgid "A comma-separated list of tags." msgstr "Listo da etikedoj apartitaj per komoj." #: models.py:10 msgid "Name" msgstr "Nomo" #: models.py:11 msgid "Slug" msgstr "Ä´etonvorto" #: models.py:49 msgid "Tag" msgstr "Etikedo" #: models.py:56 #, python-format msgid "%(object)s tagged with %(tag)s" msgstr "%(object)s etikedita %(tag)s" #: models.py:100 msgid "Object id" msgstr "Objekto ID" #: models.py:104 models.py:110 msgid "Content type" msgstr "Enhavtipo" #: models.py:138 msgid "Tagged Item" msgstr "Etikedita elemento" #: models.py:139 msgid "Tagged Items" msgstr "Etikeditaj elementoj" #: contrib/suggest/models.py:57 msgid "" "Enter a valid Regular Expression. To make it case-insensitive include \"(?" "i)\" in your expression." msgstr "" "Entajpu validan regulan esprimon. Por ke estu usklecoblinda, enmetu \"(?i)\" " "en via esprimo." django-taggit-0.24.0/taggit/locale/es/0000775000175000017500000000000013433132635021442 5ustar jdufresnejdufresne00000000000000django-taggit-0.24.0/taggit/locale/es/LC_MESSAGES/0000775000175000017500000000000013433132635023227 5ustar jdufresnejdufresne00000000000000django-taggit-0.24.0/taggit/locale/es/LC_MESSAGES/django.mo0000664000175000017500000000206613376364411025037 0ustar jdufresnejdufresne00000000000000Þ• |Ü !@ `m r.|«° ´ ÀÍkÒ">*aŒž ¥;³ ïù ,   %(object)s tagged with %(tag)sA comma-separated list of tags.Content typeNameObject idPlease provide a comma-separated list of tags.SlugTagTagged ItemTagged ItemsTagsProject-Id-Version: PACKAGE VERSION Report-Msgid-Bugs-To: POT-Creation-Date: 2016-05-10 19:55-0500 PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE Last-Translator: FULL NAME Language-Team: LANGUAGE Language: MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Plural-Forms: nplurals=2; plural=(n != 1); %(object)s etiquetados con %(tag)sUna lista de etiquetas separadas por coma.Tipo de contenidoNombreId del objetoPor favor provea una lista de etiquetas separadas por coma.MarquillaEtiquetaElemento etiquetadoElementos etiquetadosEtiquetasdjango-taggit-0.24.0/taggit/locale/es/LC_MESSAGES/django.po0000664000175000017500000000271113376364411025037 0ustar jdufresnejdufresne00000000000000# 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 , YEAR. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2016-05-10 19:55-0500\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "Language: \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:27 msgid "Please provide a comma-separated list of tags." msgstr "Por favor introduzca una lista de etiquetas separadas por coma." #: managers.py:297 models.py:101 msgid "Tags" msgstr "Etiquetas" #: managers.py:298 msgid "A comma-separated list of tags." msgstr "Una lista de etiquetas separadas por coma." #: models.py:46 msgid "Name" msgstr "Nombre" #: models.py:47 msgid "Slug" msgstr "Slug" #: models.py:100 msgid "Tag" msgstr "Etiqueta" #: models.py:107 #, python-format msgid "%(object)s tagged with %(tag)s" msgstr "%(object)s etiquetados con %(tag)s" #: models.py:162 msgid "Content type" msgstr "Tipo de contenido" #: models.py:206 models.py:215 msgid "Object id" msgstr "Id del objeto" #: models.py:223 msgid "Tagged Item" msgstr "Elemento etiquetado" #: models.py:224 msgid "Tagged Items" msgstr "Elementos etiquetados" django-taggit-0.24.0/taggit/locale/he/0000775000175000017500000000000013433132635021427 5ustar jdufresnejdufresne00000000000000django-taggit-0.24.0/taggit/locale/he/LC_MESSAGES/0000775000175000017500000000000013433132635023214 5ustar jdufresnejdufresne00000000000000django-taggit-0.24.0/taggit/locale/he/LC_MESSAGES/django.mo0000664000175000017500000000151713376364411025024 0ustar jdufresnejdufresne00000000000000Þ•T Œ¸¹Øø.ý,0a5"—8ºóFø? D%(object)s tagged with %(tag)sA comma-separated list of tags.NamePlease provide a comma-separated list of tags.TagTagsProject-Id-Version: Django Taggit Report-Msgid-Bugs-To: POT-Creation-Date: 2010-06-26 12:47-0500 PO-Revision-Date: 2010-06-26 12:54-0600 Last-Translator: Alex Language-Team: LANGUAGE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Plural-Forms: nplurals=2; plural=(n != 1); %(object)s מתויג ×¢× %(tag)sרשימה של ×ª×’×™× ×ž×•×¤×¨×“×ª ×¢× ×¤×¡×™×§×™×.ש×× × ×œ×¡×¤×§ רשימה של ×ª×’×™× ×ž×•×¤×¨×“×ª ×¢× ×¤×¡×™×§×™×.תגתגיותdjango-taggit-0.24.0/taggit/locale/he/LC_MESSAGES/django.po0000664000175000017500000000301613376364411025023 0ustar jdufresnejdufresne00000000000000# 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 , 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 \n" "Language-Team: LANGUAGE \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 "" django-taggit-0.24.0/taggit/locale/it/0000775000175000017500000000000013433132635021447 5ustar jdufresnejdufresne00000000000000django-taggit-0.24.0/taggit/locale/it/LC_MESSAGES/0000775000175000017500000000000013433132635023234 5ustar jdufresnejdufresne00000000000000django-taggit-0.24.0/taggit/locale/it/LC_MESSAGES/django.mo0000664000175000017500000000225313376364411025042 0ustar jdufresnejdufresne00000000000000Þ• „ì01P p`}Þ ã.í! % 1>5Cy%”º€¿@ E-P~ƒ‡—§   %(object)s tagged with %(tag)sA comma-separated list of tags.Content typeEnter a valid Regular Expression. To make it case-insensitive include "(?i)" in your expression.NameObject idPlease provide a comma-separated list of tags.SlugTagTagged ItemTagged ItemsTagsProject-Id-Version: PACKAGE VERSION Report-Msgid-Bugs-To: POT-Creation-Date: 2014-04-13 15:57+0200 PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE Last-Translator: FULL NAME Language-Team: LANGUAGE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit %(object)s con tag %(tag)sUna lista di tag separati da virgola.TipoInserire un'espressione regolare valida. Per renderla indipendente dal maiuscolo e minuscolo, includere "(?i)" nell'espressione.NomeId OggettoFornire una lista di tag separati da virgola.SlugTagOggetto con tagOggetti con tagTagdjango-taggit-0.24.0/taggit/locale/it/LC_MESSAGES/django.po0000664000175000017500000000316313376364411025046 0ustar jdufresnejdufresne00000000000000# 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 , YEAR. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2014-04-13 15:57+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" #: forms.py:20 msgid "Please provide a comma-separated list of tags." msgstr "Fornire una lista di tag separati da virgola." #: managers.py:39 managers.py:83 models.py:50 msgid "Tags" msgstr "Tag" #: managers.py:84 msgid "A comma-separated list of tags." msgstr "Una lista di tag separati da virgola." #: models.py:10 msgid "Name" msgstr "Nome" #: models.py:11 msgid "Slug" msgstr "Slug" #: models.py:49 msgid "Tag" msgstr "Tag" #: models.py:56 #, python-format msgid "%(object)s tagged with %(tag)s" msgstr "%(object)s con tag %(tag)s" #: models.py:100 msgid "Object id" msgstr "Id Oggetto" #: models.py:104 models.py:110 msgid "Content type" msgstr "Tipo" #: models.py:138 msgid "Tagged Item" msgstr "Oggetto con tag" #: models.py:139 msgid "Tagged Items" msgstr "Oggetti con tag" #: contrib/suggest/models.py:57 msgid "" "Enter a valid Regular Expression. To make it case-insensitive include \"(?i)" "\" in your expression." msgstr "" "Inserire un'espressione regolare valida. Per renderla indipendente dal maiuscolo e minuscolo, includere \"(?i)" "\" nell'espressione." django-taggit-0.24.0/taggit/locale/ja/0000775000175000017500000000000013433132635021425 5ustar jdufresnejdufresne00000000000000django-taggit-0.24.0/taggit/locale/ja/LC_MESSAGES/0000775000175000017500000000000013433132635023212 5ustar jdufresnejdufresne00000000000000django-taggit-0.24.0/taggit/locale/ja/LC_MESSAGES/django.mo0000664000175000017500000000263513376364411025024 0ustar jdufresnejdufresne00000000000000Þ• „ì01P p`}Þ ã.í! % 1>|CÀ0ߢ)ÌÓHé 2?!F'h    %(object)s tagged with %(tag)sA comma-separated list of tags.Content typeEnter a valid Regular Expression. To make it case-insensitive include "(?i)" in your expression.NameObject idPlease provide a comma-separated list of tags.SlugTagTagged ItemTagged ItemsTagsProject-Id-Version: django-taggit Report-Msgid-Bugs-To: POT-Creation-Date: 2010-09-07 09:26-0700 PO-Revision-Date: 2014-04-23 08:05+0900 Last-Translator: Tatsuo Ikeda Language-Team: MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Plural-Forms: nplurals=2; plural=(n != 1); Language: ja X-Generator: Poedit 1.6.4 %(object)s tagged with %(tag)s複数タグã¯ã‚«ãƒ³ãƒžåŒºåˆ‡ã‚Šã®ãƒªã‚¹ãƒˆã€‚コンテンツタイプ正ã—ã„æ­£è¦è¡¨ç¾ã‚’入力ã—ã¦ãã ã•ã„。 大文字ã¨å°æ–‡å­—を区別ã—ãªã„よã†ã«ã™ã‚‹ã«ã¯ "(?i)" ã‚’æ­£è¦è¡¨ç¾ã«å«ã‚ã¦ãã ã•ã„。å称オブジェクト ID複数タグã¯ã‚«ãƒ³ãƒžåŒºåˆ‡ã‚Šã®ãƒªã‚¹ãƒˆã‚’入れã¦ãã ã•ã„ã€‚ã‚¹ãƒ©ãƒƒã‚°ã‚¿ã‚°ã‚¿ã‚°ä»˜ã‘æ¸ˆã¿ã®ã‚¢ã‚¤ãƒ†ãƒ ã‚¿ã‚°ä»˜ã‘済ã¿ã®ã‚¢ã‚¤ãƒ†ãƒ ä¸€è¦§ã‚¿ã‚°ä¸€è¦§django-taggit-0.24.0/taggit/locale/ja/LC_MESSAGES/django.po0000664000175000017500000000325013376364411025021 0ustar jdufresnejdufresne00000000000000msgid "" msgstr "" "Project-Id-Version: django-taggit\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2010-09-07 09:26-0700\n" "PO-Revision-Date: 2014-04-23 08:05+0900\n" "Last-Translator: Tatsuo Ikeda \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=2; plural=(n != 1);\n" "Language: ja\n" "X-Generator: Poedit 1.6.4\n" #: forms.py:20 msgid "Please provide a comma-separated list of tags." msgstr "複数タグã¯ã‚«ãƒ³ãƒžåŒºåˆ‡ã‚Šã®ãƒªã‚¹ãƒˆã‚’入れã¦ãã ã•ã„。" #: managers.py:39 managers.py:83 models.py:50 msgid "Tags" msgstr "タグ一覧" #: managers.py:84 msgid "A comma-separated list of tags." msgstr "複数タグã¯ã‚«ãƒ³ãƒžåŒºåˆ‡ã‚Šã®ãƒªã‚¹ãƒˆã€‚" #: models.py:10 msgid "Name" msgstr "åç§°" #: models.py:11 msgid "Slug" msgstr "スラッグ" #: models.py:49 msgid "Tag" msgstr "ã‚¿ã‚°" #: models.py:56 #, python-format msgid "%(object)s tagged with %(tag)s" msgstr "%(object)s tagged with %(tag)s" #: models.py:100 msgid "Object id" msgstr "オブジェクト ID" #: models.py:104 models.py:110 msgid "Content type" msgstr "コンテンツタイプ" #: models.py:138 msgid "Tagged Item" msgstr "ã‚¿ã‚°ä»˜ã‘æ¸ˆã¿ã®ã‚¢ã‚¤ãƒ†ãƒ " #: models.py:139 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)\" ã‚’æ­£è¦è¡¨ç¾ã«å«ã‚ã¦ãã ã•ã„。" django-taggit-0.24.0/taggit/locale/nb/0000775000175000017500000000000013433132635021432 5ustar jdufresnejdufresne00000000000000django-taggit-0.24.0/taggit/locale/nb/LC_MESSAGES/0000775000175000017500000000000013433132635023217 5ustar jdufresnejdufresne00000000000000django-taggit-0.24.0/taggit/locale/nb/LC_MESSAGES/django.mo0000664000175000017500000000237613376364411025033 0ustar jdufresnejdufresne00000000000000Þ• „ì01P p`}Þ ã.í! % 1>kC¯Í ê˜÷ •,ŸÌÑÖå÷   %(object)s tagged with %(tag)sA comma-separated list of tags.Content typeEnter a valid Regular Expression. To make it case-insensitive include "(?i)" in your expression.NameObject idPlease provide a comma-separated list of tags.SlugTagTagged ItemTagged ItemsTagsProject-Id-Version: 0.9.3 Report-Msgid-Bugs-To: POT-Creation-Date: 2010-09-07 09:45-0700 PO-Revision-Date: 2012-12-08 14:42+0100 Last-Translator: Bjørn Pettersen Language-Team: Norwegian MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-Generator: Poedit 1.5.4 Language: Norwegian %(object)s tagget med %(tag)sEn kommaseparert tagg-liste.InnholdstypeSkriv et gyldig regulært utrykk (regex). For Ã¥ gjøre det uavhengig av forskjellen mellom store og smÃ¥ bokstaver mÃ¥ du inkludere "(?i)" i din regex.NavnObjekt-idVennligst oppgi en kommaseparert tagg-liste.SlugTaggTagget ElementTaggede ElementerTaggerdjango-taggit-0.24.0/taggit/locale/nb/LC_MESSAGES/django.po0000664000175000017500000000331013376364411025023 0ustar jdufresnejdufresne00000000000000# 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 , YEAR. # msgid "" msgstr "" "Project-Id-Version: 0.9.3\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2010-09-07 09:45-0700\n" "PO-Revision-Date: 2012-12-08 14:42+0100\n" "Last-Translator: Bjørn Pettersen \n" "Language-Team: Norwegian \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "X-Generator: Poedit 1.5.4\n" "Language: Norwegian\n" #: forms.py:20 msgid "Please provide a comma-separated list of tags." msgstr "Vennligst oppgi en kommaseparert tagg-liste." #: managers.py:39 managers.py:83 models.py:50 msgid "Tags" msgstr "Tagger" #: managers.py:84 msgid "A comma-separated list of tags." msgstr "En kommaseparert tagg-liste." #: models.py:10 msgid "Name" msgstr "Navn" #: models.py:11 msgid "Slug" msgstr "Slug" #: models.py:49 msgid "Tag" msgstr "Tagg" #: models.py:56 #, python-format msgid "%(object)s tagged with %(tag)s" msgstr "%(object)s tagget med %(tag)s" #: models.py:100 msgid "Object id" msgstr "Objekt-id" #: models.py:104 models.py:110 msgid "Content type" msgstr "Innholdstype" #: models.py:138 msgid "Tagged Item" msgstr "Tagget Element" #: models.py:139 msgid "Tagged Items" msgstr "Taggede Elementer" #: contrib/suggest/models.py:57 msgid "" "Enter a valid Regular Expression. To make it case-insensitive include \"(?" "i)\" in your expression." msgstr "" "Skriv et gyldig regulært utrykk (regex). For Ã¥ gjøre det uavhengig av " "forskjellen mellom store og smÃ¥ bokstaver mÃ¥ du inkludere \"(?i)\" i din " "regex." django-taggit-0.24.0/taggit/locale/nl/0000775000175000017500000000000013433132635021444 5ustar jdufresnejdufresne00000000000000django-taggit-0.24.0/taggit/locale/nl/LC_MESSAGES/0000775000175000017500000000000013433132635023231 5ustar jdufresnejdufresne00000000000000django-taggit-0.24.0/taggit/locale/nl/LC_MESSAGES/django.mo0000664000175000017500000000230113376364411025031 0ustar jdufresnejdufresne00000000000000Þ• „ì01P p`}Þ ã.í! % 1>GC‹)« ÕqáS X.b‘–šª¼   %(object)s tagged with %(tag)sA comma-separated list of tags.Content typeEnter a valid Regular Expression. To make it case-insensitive include "(?i)" in your expression.NameObject idPlease provide a comma-separated list of tags.SlugTagTagged ItemTagged ItemsTagsProject-Id-Version: django-taggit Report-Msgid-Bugs-To: POT-Creation-Date: 2010-09-07 09:45-0700 PO-Revision-Date: 2010-09-07 23:04+0100 Last-Translator: Jeffrey Gelens Language-Team: Dutch MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit X-Poedit-Language: Dutch %(object)s getagged met %(tag)sEen door komma gescheiden lijst van tags.InhoudstypeVoer een valide reguliere expressie in. Voeg "(?i)" aan de expressie toe om deze hoofdletter ongevoelig te maken.NaamObject-idGeef een door komma gescheiden lijst van tags.SlugTagObject getaggedObjecten getaggedTagsdjango-taggit-0.24.0/taggit/locale/nl/LC_MESSAGES/django.po0000664000175000017500000000264413376364411025046 0ustar jdufresnejdufresne00000000000000msgid "" msgstr "" "Project-Id-Version: django-taggit\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2010-09-07 09:45-0700\n" "PO-Revision-Date: 2010-09-07 23:04+0100\n" "Last-Translator: Jeffrey Gelens \n" "Language-Team: Dutch\n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" #: forms.py:20 msgid "Please provide a comma-separated list of tags." msgstr "Geef een door komma gescheiden lijst van tags." #: managers.py:39 #: managers.py:83 #: models.py:50 msgid "Tags" msgstr "Tags" #: managers.py:84 msgid "A comma-separated list of tags." msgstr "Een door komma gescheiden lijst van tags." #: models.py:10 msgid "Name" msgstr "Naam" #: models.py:11 msgid "Slug" msgstr "Slug" #: models.py:49 msgid "Tag" msgstr "Tag" #: models.py:56 #, python-format msgid "%(object)s tagged with %(tag)s" msgstr "%(object)s getagged met %(tag)s" #: models.py:100 msgid "Object id" msgstr "Object-id" #: models.py:104 #: models.py:110 msgid "Content type" msgstr "Inhoudstype" #: models.py:138 msgid "Tagged Item" msgstr "Object getagged" #: models.py:139 msgid "Tagged Items" msgstr "Objecten getagged" #: contrib/suggest/models.py:57 msgid "Enter a valid Regular Expression. To make it case-insensitive include \"(?i)\" in your expression." msgstr "Voer een valide reguliere expressie in. Voeg \"(?i)\" aan de expressie toe om deze hoofdletter ongevoelig te maken." django-taggit-0.24.0/taggit/locale/pt_BR/0000775000175000017500000000000013433132635022041 5ustar jdufresnejdufresne00000000000000django-taggit-0.24.0/taggit/locale/pt_BR/LC_MESSAGES/0000775000175000017500000000000013433132635023626 5ustar jdufresnejdufresne00000000000000django-taggit-0.24.0/taggit/locale/pt_BR/LC_MESSAGES/django.mo0000664000175000017500000000205013376364411025427 0ustar jdufresnejdufresne00000000000000Þ• |Ü !@ `m r.|«° ´ ÀÍmÒ@/`¢ §>´óø     %(object)s tagged with %(tag)sA comma-separated list of tags.Content typeNameObject idPlease provide a comma-separated list of tags.SlugTagTagged ItemTagged ItemsTagsProject-Id-Version: django-taggit Report-Msgid-Bugs-To: POT-Creation-Date: 2013-01-15 22:25-0200 PO-Revision-Date: 2013-01-12 18:11-0200 Last-Translator: RPB Language-Team: Portuguese (Brazil) Language: pt_BR MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Plural-Forms: nplurals=2; plural=(n > 1) %(object)s marcados com %(tag)sUma lista de marcadores separados por vírgula.Tipo de conteúdoNomeId do objetoFavor fornecer uma lista de marcadores separados por vírgula.SlugMarcadorItem marcadoItens marcadosMarcadoresdjango-taggit-0.24.0/taggit/locale/pt_BR/LC_MESSAGES/django.po0000664000175000017500000000251013376364411025433 0ustar jdufresnejdufresne00000000000000# This file is distributed under WTFPL license. # # Translators: # RPB , 2013. msgid "" msgstr "" "Project-Id-Version: django-taggit\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2013-01-15 22:25-0200\n" "PO-Revision-Date: 2013-01-12 18:11-0200\n" "Last-Translator: RPB \n" "Language-Team: Portuguese (Brazil) \n" "Language: pt_BR\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:21 msgid "Please provide a comma-separated list of tags." msgstr "Favor fornecer uma lista de marcadores separados por vírgula." #: managers.py:39 models.py:57 msgid "Tags" msgstr "Marcadores" #: managers.py:40 msgid "A comma-separated list of tags." msgstr "Uma lista de marcadores separados por vírgula." #: models.py:10 msgid "Name" msgstr "Nome" #: models.py:11 msgid "Slug" msgstr "Slug" #: models.py:56 msgid "Tag" msgstr "Marcador" #: models.py:63 #, python-format msgid "%(object)s tagged with %(tag)s" msgstr "%(object)s marcados com %(tag)s" #: models.py:113 msgid "Object id" msgstr "Id do objeto" #: models.py:117 models.py:123 msgid "Content type" msgstr "Tipo de conteúdo" #: models.py:159 msgid "Tagged Item" msgstr "Item marcado" #: models.py:160 msgid "Tagged Items" msgstr "Itens marcados" django-taggit-0.24.0/taggit/locale/ru/0000775000175000017500000000000013433132635021461 5ustar jdufresnejdufresne00000000000000django-taggit-0.24.0/taggit/locale/ru/LC_MESSAGES/0000775000175000017500000000000013433132635023246 5ustar jdufresnejdufresne00000000000000django-taggit-0.24.0/taggit/locale/ru/LC_MESSAGES/django.mo0000664000175000017500000000302313376364411025050 0ustar jdufresnejdufresne00000000000000Þ•ŒüHIh ˆ`•ö û.49 = IV]Âb9%2_’ °Qb4t© ²½ Ü ý    %(object)s tagged with %(tag)sA comma-separated list of tags.Content typeEnter a valid Regular Expression. To make it case-insensitive include "(?i)" in your expression.NameObject idPlease provide a comma-separated list of tags.SlugTagTagged ItemTagged ItemsTaggitTagsProject-Id-Version: Django Taggit Report-Msgid-Bugs-To: POT-Creation-Date: 2010-06-11 11:28+0700 PO-Revision-Date: 2010-06-11 11:30+0700 Last-Translator: Igor 'idle sign' Starikov Language-Team: MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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; X-Poedit-Language: Russian Ñлемент «%(object)s» Ñ Ð¼ÐµÑ‚ÐºÐ¾Ð¹ «%(tag)s»СпиÑок меток через запÑтую.Тип ÑодержимогоВведите регулÑрное выражение. Чтобы Ñделать его чувÑтвительным к региÑтру укажите "(?i)".ÐазваниеID объектаУкажите метки через запÑтую.СлагМеткаЭлемент Ñ Ð¼ÐµÑ‚ÐºÐ¾Ð¹Ð­Ð»ÐµÐ¼ÐµÐ½Ñ‚Ñ‹ Ñ Ð¼ÐµÑ‚ÐºÐ¾Ð¹ÐœÐµÑ‚ÐºÐ¸ÐœÐµÑ‚ÐºÐ¸django-taggit-0.24.0/taggit/locale/ru/LC_MESSAGES/django.po0000664000175000017500000000367513376364411025070 0ustar jdufresnejdufresne00000000000000# 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 , 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 \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" msgid "Taggit" msgstr "Метки" #: 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)\"." django-taggit-0.24.0/taggit/locale/tr/0000775000175000017500000000000013433132635021460 5ustar jdufresnejdufresne00000000000000django-taggit-0.24.0/taggit/locale/tr/LC_MESSAGES/0000775000175000017500000000000013433132635023245 5ustar jdufresnejdufresne00000000000000django-taggit-0.24.0/taggit/locale/tr/LC_MESSAGES/django.mo0000664000175000017500000000205613376364411025054 0ustar jdufresnejdufresne00000000000000Þ• |Ü !@ `m r.|«° ´ ÀÍjÒ"=*`‹› 7¯ ç ñû $   %(object)s tagged with %(tag)sA comma-separated list of tags.Content typeNameObject idPlease provide a comma-separated list of tags.SlugTagTagged ItemTagged ItemsTagsProject-Id-Version: PACKAGE VERSION Report-Msgid-Bugs-To: POT-Creation-Date: 2016-05-10 19:55-0500 PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE Last-Translator: FULL NAME Language-Team: LANGUAGE Language: MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Plural-Forms: nplurals=2; plural=(n > 1); %(object)s %(tag)s ile etiketlendiEtiketlerin virgülle ayrılmış listesi.İçerik türüAdıNesne kimliÄŸiEtiketleri bir virgülle ayrılmış listesini veriniz.KısaltmaEtiketlerTakip edilen ÖğeTakip edilen ÖğelerEtiketlerdjango-taggit-0.24.0/taggit/locale/tr/LC_MESSAGES/django.po0000664000175000017500000000270213376364411025055 0ustar jdufresnejdufresne00000000000000# 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 , YEAR. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2016-05-10 19:55-0500\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "Language: \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:27 msgid "Please provide a comma-separated list of tags." msgstr "Etiketleri bir virgülle ayrılmış listesini veriniz." #: managers.py:297 models.py:101 msgid "Tags" msgstr "Etiketler" #: managers.py:298 msgid "A comma-separated list of tags." msgstr "Etiketlerin virgülle ayrılmış listesi." #: models.py:46 msgid "Name" msgstr "Adı" #: models.py:47 msgid "Slug" msgstr "Kısaltma" #: models.py:100 msgid "Tag" msgstr "Etiketler" #: models.py:107 #, python-format msgid "%(object)s tagged with %(tag)s" msgstr "%(object)s %(tag)s ile etiketlendi" #: models.py:162 msgid "Content type" msgstr "İçerik türü" #: models.py:206 models.py:215 msgid "Object id" msgstr "Nesne kimliÄŸi" #: models.py:223 msgid "Tagged Item" msgstr "Takip edilen Öğe" #: models.py:224 msgid "Tagged Items" msgstr "Takip edilen Öğeler" django-taggit-0.24.0/taggit/locale/zh_Hans/0000775000175000017500000000000013433132635022425 5ustar jdufresnejdufresne00000000000000django-taggit-0.24.0/taggit/locale/zh_Hans/LC_MESSAGES/0000775000175000017500000000000013433132635024212 5ustar jdufresnejdufresne00000000000000django-taggit-0.24.0/taggit/locale/zh_Hans/LC_MESSAGES/django.mo0000664000175000017500000000153013376364411026015 0ustar jdufresnejdufresne00000000000000Þ• tÌ 0= BLQ U anudz"ß  , 3 = GQ  %(object)s tagged with %(tag)sContent typeNameObject idSlugTagTagged ItemTagged ItemsTaggitTagsProject-Id-Version: PACKAGE VERSION Report-Msgid-Bugs-To: POT-Creation-Date: 2016-05-23 17:26+0800 PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE Last-Translator: FULL NAME Language-Team: LANGUAGE Language: MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Plural-Forms: nplurals=1; plural=0; %(object)s 使用了标签 %(tag)s内容类型å称对象ID唯一标识标签标签项标签项标签项标签django-taggit-0.24.0/taggit/locale/zh_Hans/LC_MESSAGES/django.po0000664000175000017500000000271613376364411026027 0ustar jdufresnejdufresne00000000000000# 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 , YEAR. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: PACKAGE VERSION\n" "Report-Msgid-Bugs-To: \n" "POT-Creation-Date: 2016-05-23 17:26+0800\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" "Content-Transfer-Encoding: 8bit\n" "Plural-Forms: nplurals=1; plural=0;\n" #: taggit/apps.py:7 msgid "Taggit" msgstr "标签项" #: taggit/forms.py:27 msgid "Please provide a comma-separated list of tags." msgstr "" #: taggit/managers.py:299 taggit/models.py:102 msgid "Tags" msgstr "标签" #: taggit/managers.py:300 msgid "A comma-separated list of tags." msgstr "" #: taggit/models.py:47 msgid "Name" msgstr "åç§°" #: taggit/models.py:48 msgid "Slug" msgstr "唯一标识" #: taggit/models.py:101 msgid "Tag" msgstr "标签" #: taggit/models.py:108 #, python-format msgid "%(object)s tagged with %(tag)s" msgstr "%(object)s 使用了标签 %(tag)s" #: taggit/models.py:163 msgid "Content type" msgstr "内容类型" #: taggit/models.py:207 taggit/models.py:216 msgid "Object id" msgstr "对象ID" #: taggit/models.py:224 msgid "Tagged Item" msgstr "标签项" #: taggit/models.py:225 msgid "Tagged Items" msgstr "标签项" django-taggit-0.24.0/taggit/managers.py0000664000175000017500000005614113433132422021744 0ustar jdufresnejdufresne00000000000000from __future__ import unicode_literals from operator import attrgetter from django import VERSION from django.conf import settings from django.contrib.contenttypes.fields import GenericRelation from django.contrib.contenttypes.models import ContentType from django.db import connections, models, router from django.db.models import signals from django.db.models.fields.related import ( ManyToManyRel, OneToOneRel, RelatedField, lazy_related_operation, ) from django.db.models.query_utils import PathInfo from django.utils import six from django.utils.text import capfirst from django.utils.translation import ugettext_lazy as _ from taggit.forms import TagField from taggit.models import CommonGenericTaggedItemBase, TaggedItem from taggit.utils import require_instance_manager class ExtraJoinRestriction(object): """ An extra restriction used for contenttype restriction in joins. """ contains_aggregate = False def __init__(self, alias, col, content_types): self.alias = alias self.col = col self.content_types = content_types def as_sql(self, compiler, connection): qn = compiler.quote_name_unless_alias if len(self.content_types) == 1: extra_where = "%s.%s = %%s" % (qn(self.alias), qn(self.col)) else: extra_where = "%s.%s IN (%s)" % ( qn(self.alias), qn(self.col), ",".join(["%s"] * len(self.content_types)), ) return extra_where, self.content_types def relabel_aliases(self, change_map): self.alias = change_map.get(self.alias, self.alias) def clone(self): return self.__class__(self.alias, self.col, self.content_types[:]) class _TaggableManager(models.Manager): def __init__(self, through, model, instance, prefetch_cache_name): super(_TaggableManager, self).__init__() self.through = through self.model = model self.instance = instance self.prefetch_cache_name = prefetch_cache_name def is_cached(self, instance): return self.prefetch_cache_name in instance._prefetched_objects_cache def get_queryset(self, extra_filters=None): try: return self.instance._prefetched_objects_cache[self.prefetch_cache_name] except (AttributeError, KeyError): kwargs = extra_filters if extra_filters else {} return self.through.tags_for(self.model, self.instance, **kwargs) def get_prefetch_queryset(self, instances, queryset=None): if queryset is not None: raise ValueError("Custom queryset can't be used for this lookup.") instance = instances[0] db = self._db or router.db_for_read(instance.__class__, instance=instance) fieldname = ( "object_id" if issubclass(self.through, CommonGenericTaggedItemBase) else "content_object" ) fk = self.through._meta.get_field(fieldname) query = { "%s__%s__in" % (self.through.tag_relname(), fk.name): { obj._get_pk_val() for obj in instances } } join_table = self.through._meta.db_table source_col = fk.column connection = connections[db] qn = connection.ops.quote_name qs = ( self.get_queryset(query) .using(db) .extra( select={ "_prefetch_related_val": "%s.%s" % (qn(join_table), qn(source_col)) } ) ) if VERSION < (2, 0): return ( qs, attrgetter("_prefetch_related_val"), lambda obj: obj._get_pk_val(), False, self.prefetch_cache_name, ) else: return ( qs, attrgetter("_prefetch_related_val"), lambda obj: obj._get_pk_val(), False, self.prefetch_cache_name, False, ) def _lookup_kwargs(self): return self.through.lookup_kwargs(self.instance) @require_instance_manager def add(self, *tags): db = router.db_for_write(self.through, instance=self.instance) tag_objs = self._to_tag_model_instances(tags) new_ids = {t.pk for t in tag_objs} # NOTE: can we hardcode 'tag_id' here or should the column name be got # dynamically from somewhere? vals = ( self.through._default_manager.using(db) .values_list("tag_id", flat=True) .filter(**self._lookup_kwargs()) ) new_ids = new_ids - set(vals) signals.m2m_changed.send( sender=self.through, action="pre_add", instance=self.instance, reverse=False, model=self.through.tag_model(), pk_set=new_ids, using=db, ) for tag in tag_objs: self.through._default_manager.using(db).get_or_create( tag=tag, **self._lookup_kwargs() ) signals.m2m_changed.send( sender=self.through, action="post_add", instance=self.instance, reverse=False, model=self.through.tag_model(), pk_set=new_ids, using=db, ) def _to_tag_model_instances(self, tags): """ Takes an iterable containing either strings, tag objects, or a mixture of both and returns set of tag objects. """ db = router.db_for_write(self.through, instance=self.instance) 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()) ) ) case_insensitive = getattr(settings, "TAGGIT_CASE_INSENSITIVE", False) manager = self.through.tag_model()._default_manager.using(db) if case_insensitive: # Some databases can do case-insensitive comparison with IN, which # would be faster, but we can't rely on it or easily detect it. existing = [] tags_to_create = [] for name in str_tags: try: tag = manager.get(name__iexact=name) existing.append(tag) except self.through.tag_model().DoesNotExist: tags_to_create.append(name) else: # If str_tags has 0 elements Django actually optimizes that to not # do a query. Malcolm is very smart. existing = manager.filter(name__in=str_tags) tags_to_create = str_tags - {t.name for t in existing} tag_objs.update(existing) for new_tag in tags_to_create: if case_insensitive: tag, created = manager.get_or_create( name__iexact=new_tag, defaults={"name": new_tag} ) else: tag, created = manager.get_or_create(name=new_tag) tag_objs.add(tag) return tag_objs @require_instance_manager def names(self): return self.get_queryset().values_list("name", flat=True) @require_instance_manager def slugs(self): return self.get_queryset().values_list("slug", flat=True) @require_instance_manager def set(self, *tags, **kwargs): """ Set the object's tags to the given n tags. If the clear kwarg is True then all existing tags are removed (using `.clear()`) and the new tags added. Otherwise, only those tags that are not present in the args are removed and any new tags added. """ db = router.db_for_write(self.through, instance=self.instance) clear = kwargs.pop("clear", False) if clear: self.clear() self.add(*tags) else: # make sure we're working with a collection of a uniform type objs = self._to_tag_model_instances(tags) # get the existing tag strings old_tag_strs = set( self.through._default_manager.using(db) .filter(**self._lookup_kwargs()) .values_list("tag__name", flat=True) ) new_objs = [] for obj in objs: if obj.name in old_tag_strs: old_tag_strs.remove(obj.name) else: new_objs.append(obj) self.remove(*old_tag_strs) self.add(*new_objs) @require_instance_manager def remove(self, *tags): if not tags: return db = router.db_for_write(self.through, instance=self.instance) qs = ( self.through._default_manager.using(db) .filter(**self._lookup_kwargs()) .filter(tag__name__in=tags) ) old_ids = set(qs.values_list("tag_id", flat=True)) signals.m2m_changed.send( sender=self.through, action="pre_remove", instance=self.instance, reverse=False, model=self.through.tag_model(), pk_set=old_ids, using=db, ) qs.delete() signals.m2m_changed.send( sender=self.through, action="post_remove", instance=self.instance, reverse=False, model=self.through.tag_model(), pk_set=old_ids, using=db, ) @require_instance_manager def clear(self): db = router.db_for_write(self.through, instance=self.instance) signals.m2m_changed.send( sender=self.through, action="pre_clear", instance=self.instance, reverse=False, model=self.through.tag_model(), pk_set=None, using=db, ) self.through._default_manager.using(db).filter(**self._lookup_kwargs()).delete() signals.m2m_changed.send( sender=self.through, action="post_clear", instance=self.instance, reverse=False, model=self.through.tag_model(), pk_set=None, using=db, ) def most_common(self, min_count=None, extra_filters=None): queryset = ( self.get_queryset(extra_filters) .annotate(num_times=models.Count(self.through.tag_relname())) .order_by("-num_times") ) if min_count: queryset = queryset.filter(num_times__gte=min_count) return queryset @require_instance_manager def similar_objects(self): lookup_kwargs = self._lookup_kwargs() lookup_keys = sorted(lookup_kwargs) qs = self.through.objects.values(*six.iterkeys(lookup_kwargs)) qs = qs.annotate(n=models.Count("pk")) qs = qs.exclude(**lookup_kwargs) qs = qs.filter(tag__in=self.all()) qs = qs.order_by("-n") # TODO: This all feels like a bit of a hack. items = {} if len(lookup_keys) == 1: # Can we do this without a second query by using a select_related() # somehow? f = self.through._meta.get_field(lookup_keys[0]) remote_field = f.remote_field rel_model = remote_field.model objs = rel_model._default_manager.filter( **{ "%s__in" % remote_field.field_name: [r["content_object"] for r in qs] } ) for obj in objs: items[(getattr(obj, remote_field.field_name),)] = obj else: preload = {} for result in qs: preload.setdefault(result["content_type"], set()) preload[result["content_type"]].add(result["object_id"]) for ct, obj_ids in preload.items(): ct = ContentType.objects.get_for_id(ct) for obj in ct.model_class()._default_manager.filter(pk__in=obj_ids): items[(ct.pk, obj.pk)] = obj results = [] for result in qs: obj = items[tuple(result[k] for k in lookup_keys)] obj.similar_tags = result["n"] results.append(obj) return results class TaggableManager(RelatedField): # Field flags many_to_many = True many_to_one = False one_to_many = False one_to_one = False _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, ): self.through = through or TaggedItem rel = ManyToManyRel(self, to, related_name=related_name, through=self.through) super(TaggableManager, self).__init__( verbose_name=verbose_name, help_text=help_text, blank=blank, null=True, serialize=False, rel=rel, ) self.swappable = False self.manager = manager 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__ ) manager = self.manager( through=self.through, model=model, instance=instance, prefetch_cache_name=self.name, ) return manager def deconstruct(self): """ Deconstruct the object, used with migrations. """ name, path, args, kwargs = super(TaggableManager, self).deconstruct() # Remove forced kwargs. for kwarg in ("serialize", "null"): del kwargs[kwarg] # Add arguments related to relations. # Ref: https://github.com/jazzband/django-taggit/issues/206#issuecomment-37578676 rel = self.remote_field if isinstance(rel.through, six.string_types): kwargs["through"] = rel.through elif not rel.through._meta.auto_created: kwargs["through"] = "%s.%s" % ( rel.through._meta.app_label, rel.through._meta.object_name, ) related_model = rel.model if isinstance(related_model, six.string_types): kwargs["to"] = related_model else: kwargs["to"] = "%s.%s" % ( related_model._meta.app_label, related_model._meta.object_name, ) return name, path, args, kwargs def contribute_to_class(self, cls, name): self.set_attributes_from_name(name) self.model = cls self.opts = cls._meta cls._meta.add_field(self) setattr(cls, name, self) if not cls._meta.abstract: if isinstance(self.remote_field.model, six.string_types): def resolve_related_class(cls, model, field): field.remote_field.model = model lazy_related_operation( resolve_related_class, cls, self.remote_field.model, field=self ) if isinstance(self.through, six.string_types): def resolve_related_class(cls, model, field): self.through = model self.remote_field.through = model self.post_through_setup(cls) lazy_related_operation( resolve_related_class, cls, self.through, field=self ) else: self.post_through_setup(cls) def get_internal_type(self): return "ManyToManyField" def post_through_setup(self, cls): self.use_gfk = self.through is None or issubclass( self.through, CommonGenericTaggedItemBase ) if not self.remote_field.model: self.remote_field.model = self.through._meta.get_field( "tag" ).remote_field.model if self.use_gfk: tagged_items = GenericRelation(self.through) tagged_items.contribute_to_class(cls, "tagged_items") for rel in cls._meta.local_many_to_many: if rel == self or not isinstance(rel, TaggableManager): continue if rel.through == self.through: raise ValueError( "You can't have two TaggableManagers with the" " same through model." ) def save_form_data(self, instance, value): getattr(instance, self.name).set(*value) def formfield(self, form_class=TagField, **kwargs): defaults = { "label": capfirst(self.verbose_name), "help_text": self.help_text, "required": not self.blank, } defaults.update(kwargs) return form_class(**defaults) def value_from_object(self, instance): if instance.pk: return self.through.objects.filter(**self.through.lookup_kwargs(instance)) return self.through.objects.none() def related_query_name(self): return self.model._meta.model_name def m2m_reverse_name(self): return self.through._meta.get_field("tag").column def m2m_reverse_field_name(self): return self.through._meta.get_field("tag").name def m2m_target_field_name(self): return self.model._meta.pk.name def m2m_reverse_target_field_name(self): return self.remote_field.model._meta.pk.name def m2m_column_name(self): if self.use_gfk: return self.through._meta.virtual_fields[0].fk_field return self.through._meta.get_field("content_object").column def m2m_db_table(self): return self.through._meta.db_table def bulk_related_objects(self, new_objs, using): return [] def _get_mm_case_path_info(self, direct=False, filtered_relation=None): pathinfos = [] linkfield1 = self.through._meta.get_field("content_object") linkfield2 = self.through._meta.get_field(self.m2m_reverse_field_name()) if direct: if VERSION < (2, 0): join1infos = linkfield1.get_reverse_path_info() join2infos = linkfield2.get_path_info() else: join1infos = linkfield1.get_reverse_path_info( filtered_relation=filtered_relation ) join2infos = linkfield2.get_path_info( filtered_relation=filtered_relation ) else: if VERSION < (2, 0): join1infos = linkfield2.get_reverse_path_info() join2infos = linkfield1.get_path_info() else: join1infos = linkfield2.get_reverse_path_info( filtered_relation=filtered_relation ) join2infos = linkfield1.get_path_info( filtered_relation=filtered_relation ) pathinfos.extend(join1infos) pathinfos.extend(join2infos) return pathinfos def _get_gfk_case_path_info(self, direct=False, filtered_relation=None): pathinfos = [] from_field = self.model._meta.pk opts = self.through._meta linkfield = self.through._meta.get_field(self.m2m_reverse_field_name()) if direct: if VERSION < (2, 0): join1infos = [ PathInfo( self.model._meta, opts, [from_field], self.remote_field, True, False, ) ] join2infos = linkfield.get_path_info() else: join1infos = [ PathInfo( self.model._meta, opts, [from_field], self.remote_field, True, False, filtered_relation, ) ] join2infos = linkfield.get_path_info( filtered_relation=filtered_relation ) else: if VERSION < (2, 0): join1infos = linkfield.get_reverse_path_info() join2infos = [ PathInfo(opts, self.model._meta, [from_field], self, True, False) ] else: join1infos = linkfield.get_reverse_path_info( filtered_relation=filtered_relation ) join2infos = [ PathInfo( opts, self.model._meta, [from_field], self, True, False, filtered_relation, ) ] pathinfos.extend(join1infos) pathinfos.extend(join2infos) return pathinfos def get_path_info(self, filtered_relation=None): if self.use_gfk: return self._get_gfk_case_path_info( direct=True, filtered_relation=filtered_relation ) else: return self._get_mm_case_path_info( direct=True, filtered_relation=filtered_relation ) def get_reverse_path_info(self, filtered_relation=None): if self.use_gfk: return self._get_gfk_case_path_info( direct=False, filtered_relation=filtered_relation ) else: return self._get_mm_case_path_info( direct=False, filtered_relation=filtered_relation ) def get_joining_columns(self, reverse_join=False): if reverse_join: return ((self.model._meta.pk.column, "object_id"),) else: return (("object_id", self.model._meta.pk.column),) def get_extra_restriction(self, where_class, alias, related_alias): extra_col = self.through._meta.get_field("content_type").column content_type_ids = [ ContentType.objects.get_for_model(subclass).pk for subclass in _get_subclasses(self.model) ] return ExtraJoinRestriction(related_alias, extra_col, content_type_ids) def get_reverse_joining_columns(self): return self.get_joining_columns(reverse_join=True) @property def related_fields(self): return [(self.through._meta.get_field("object_id"), self.model._meta.pk)] @property def foreign_related_fields(self): return [self.related_fields[0][1]] def _get_subclasses(model): subclasses = [model] for field in model._meta.get_fields(): if isinstance(field, OneToOneRel) and getattr( field.field.remote_field, "parent_link", None ): subclasses.extend(_get_subclasses(field.related_model)) return subclasses django-taggit-0.24.0/taggit/migrations/0000775000175000017500000000000013433132635021750 5ustar jdufresnejdufresne00000000000000django-taggit-0.24.0/taggit/migrations/0001_initial.py0000664000175000017500000000527313433126115024416 0ustar jdufresnejdufresne00000000000000# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.db import migrations, models class Migration(migrations.Migration): dependencies = [("contenttypes", "0001_initial")] operations = [ migrations.CreateModel( name="Tag", fields=[ ( "id", models.AutoField( auto_created=True, primary_key=True, serialize=False, help_text="", verbose_name="ID", ), ), ( "name", models.CharField( help_text="", unique=True, max_length=100, verbose_name="Name" ), ), ( "slug", models.SlugField( help_text="", unique=True, max_length=100, verbose_name="Slug" ), ), ], options={"verbose_name": "Tag", "verbose_name_plural": "Tags"}, bases=(models.Model,), ), migrations.CreateModel( name="TaggedItem", fields=[ ( "id", models.AutoField( auto_created=True, primary_key=True, serialize=False, help_text="", verbose_name="ID", ), ), ( "object_id", models.IntegerField( help_text="", verbose_name="Object id", db_index=True ), ), ( "content_type", models.ForeignKey( related_name="taggit_taggeditem_tagged_items", verbose_name="Content type", to="contenttypes.ContentType", help_text="", on_delete=models.CASCADE, ), ), ( "tag", models.ForeignKey( related_name="taggit_taggeditem_items", to="taggit.Tag", help_text="", on_delete=models.CASCADE, ), ), ], options={ "verbose_name": "Tagged Item", "verbose_name_plural": "Tagged Items", }, bases=(models.Model,), ), ] django-taggit-0.24.0/taggit/migrations/0002_auto_20150616_2121.py0000664000175000017500000000053213433126115025360 0ustar jdufresnejdufresne00000000000000# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.db import migrations class Migration(migrations.Migration): dependencies = [("taggit", "0001_initial")] operations = [ migrations.AlterIndexTogether( name="taggeditem", index_together=set([("content_type", "object_id")]) ) ] django-taggit-0.24.0/taggit/migrations/__init__.py0000664000175000017500000000000013376364411024054 0ustar jdufresnejdufresne00000000000000django-taggit-0.24.0/taggit/models.py0000664000175000017500000001301713433126115021427 0ustar jdufresnejdufresne00000000000000from __future__ import unicode_literals from django.contrib.contenttypes.fields import GenericForeignKey from django.contrib.contenttypes.models import ContentType from django.db import IntegrityError, models, router, transaction from django.utils.encoding import python_2_unicode_compatible from django.utils.text import slugify from django.utils.translation import ugettext, ugettext_lazy as _ try: from unidecode import unidecode except ImportError: def unidecode(tag): return tag @python_2_unicode_compatible class TagBase(models.Model): name = models.CharField(verbose_name=_("Name"), unique=True, max_length=100) slug = models.SlugField(verbose_name=_("Slug"), unique=True, max_length=100) def __str__(self): return self.name def __gt__(self, other): return self.name.lower() > other.name.lower() def __lt__(self, other): return self.name.lower() < other.name.lower() class Meta: abstract = True def save(self, *args, **kwargs): if self._state.adding and not self.slug: self.slug = self.slugify(self.name) using = kwargs.get("using") or router.db_for_write( type(self), instance=self ) # Make sure we write to the same db for all attempted writes, # with a multi-master setup, theoretically we could try to # write and rollback on different DBs kwargs["using"] = using # Be oportunistic and try to save the tag, this should work for # most cases ;) try: with transaction.atomic(using=using): res = super(TagBase, self).save(*args, **kwargs) return res except IntegrityError: pass # Now try to find existing slugs with similar names slugs = set( self.__class__._default_manager.filter( slug__startswith=self.slug ).values_list("slug", flat=True) ) i = 1 while True: slug = self.slugify(self.name, i) if slug not in slugs: self.slug = slug # We purposely ignore concurrecny issues here for now. # (That is, till we found a nice solution...) return super(TagBase, self).save(*args, **kwargs) i += 1 else: return super(TagBase, self).save(*args, **kwargs) def slugify(self, tag, i=None): slug = slugify(unidecode(tag)) if i is not None: slug += "_%d" % i return slug class Tag(TagBase): class Meta: verbose_name = _("Tag") verbose_name_plural = _("Tags") app_label = "taggit" @python_2_unicode_compatible class ItemBase(models.Model): def __str__(self): return ugettext("%(object)s tagged with %(tag)s") % { "object": self.content_object, "tag": self.tag, } class Meta: abstract = True @classmethod def tag_model(cls): field = cls._meta.get_field("tag") return field.remote_field.model @classmethod def tag_relname(cls): field = cls._meta.get_field("tag") return field.remote_field.related_name @classmethod def lookup_kwargs(cls, instance): return {"content_object": instance} class TaggedItemBase(ItemBase): tag = models.ForeignKey( Tag, related_name="%(app_label)s_%(class)s_items", on_delete=models.CASCADE ) class Meta: abstract = True @classmethod def tags_for(cls, model, instance=None, **extra_filters): kwargs = extra_filters or {} if instance is not None: kwargs.update({"%s__content_object" % cls.tag_relname(): instance}) return cls.tag_model().objects.filter(**kwargs) kwargs.update({"%s__content_object__isnull" % cls.tag_relname(): False}) return cls.tag_model().objects.filter(**kwargs).distinct() class CommonGenericTaggedItemBase(ItemBase): content_type = models.ForeignKey( ContentType, on_delete=models.CASCADE, verbose_name=_("Content type"), related_name="%(app_label)s_%(class)s_tagged_items", ) content_object = GenericForeignKey() class Meta: abstract = True @classmethod def lookup_kwargs(cls, instance): return { "object_id": instance.pk, "content_type": ContentType.objects.get_for_model(instance), } @classmethod def tags_for(cls, model, instance=None, **extra_filters): ct = ContentType.objects.get_for_model(model) kwargs = {"%s__content_type" % cls.tag_relname(): ct} if instance is not None: kwargs["%s__object_id" % cls.tag_relname()] = instance.pk if extra_filters: kwargs.update(extra_filters) return cls.tag_model().objects.filter(**kwargs).distinct() class GenericTaggedItemBase(CommonGenericTaggedItemBase): object_id = models.IntegerField(verbose_name=_("Object id"), db_index=True) class Meta: abstract = True class GenericUUIDTaggedItemBase(CommonGenericTaggedItemBase): object_id = models.UUIDField(verbose_name=_("Object id"), db_index=True) class Meta: abstract = True class TaggedItem(GenericTaggedItemBase, TaggedItemBase): class Meta: verbose_name = _("Tagged Item") verbose_name_plural = _("Tagged Items") app_label = "taggit" index_together = [["content_type", "object_id"]] django-taggit-0.24.0/taggit/utils.py0000664000175000017500000001050413433126115021302 0ustar jdufresnejdufresne00000000000000from __future__ import unicode_literals from django.conf import settings from django.utils.encoding import force_text from django.utils.functional import wraps from django.utils.module_loading import import_string def _parse_tags(tagstring): """ Parses tag input, with multiple word input being activated and delineated by commas and double quotes. Quotes take precedence, so they may contain commas. Returns a sorted list of unique tag names. Ported from Jonathan Buchanan's `django-tagging `_ """ if not tagstring: return [] tagstring = force_text(tagstring) # 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 # split on spaces. if "," not in tagstring and '"' not in tagstring: words = list(set(split_strip(tagstring, " "))) words.sort() return words words = [] buffer = [] # Defer splitting of non-quoted sections until we know if there are # any unquoted commas. to_be_split = [] saw_loose_comma = False open_quote = False i = iter(tagstring) try: while True: c = next(i) if c == '"': if buffer: to_be_split.append("".join(buffer)) buffer = [] # Find the matching quote open_quote = True c = next(i) while c != '"': buffer.append(c) c = next(i) if buffer: word = "".join(buffer).strip() if word: words.append(word) buffer = [] open_quote = False else: if not saw_loose_comma and c == ",": saw_loose_comma = True buffer.append(c) except StopIteration: # If we were parsing an open quote which was never closed treat # the buffer as unquoted. if buffer: if open_quote and "," in buffer: saw_loose_comma = True to_be_split.append("".join(buffer)) if to_be_split: if saw_loose_comma: delimiter = "," else: delimiter = " " for chunk in to_be_split: words.extend(split_strip(chunk, delimiter)) words = list(set(words)) words.sort() return words def split_strip(string, delimiter=","): """ Splits ``string`` on ``delimiter``, stripping each resulting string and returning a list of non-empty strings. Ported from Jonathan Buchanan's `django-tagging `_ """ if not string: return [] words = [w.strip() for w in string.split(delimiter)] return [w for w in words if w] def _edit_string_for_tags(tags): """ Given list of ``Tag`` instances, creates a string representation of the list suitable for editing by the user, such that submitting the given string representation back without changing it will give the same list of tags. Tag names which contain commas will be double quoted. If any tag name which isn't being quoted contains whitespace, the resulting string of tag names will be comma-delimited, otherwise it will be space-delimited. Ported from Jonathan Buchanan's `django-tagging `_ """ names = [] for tag in tags: name = tag.name if "," in name or " " in name: names.append('"%s"' % name) else: names.append(name) return ", ".join(sorted(names)) def require_instance_manager(func): @wraps(func) def inner(self, *args, **kwargs): if self.instance is None: raise TypeError("Can't call %s with a non-instance manager" % func.__name__) return func(self, *args, **kwargs) return inner def get_func(key, default): func_path = getattr(settings, key, None) return default if func_path is None else import_string(func_path) def parse_tags(tagstring): func = get_func("TAGGIT_TAGS_FROM_STRING", _parse_tags) return func(tagstring) def edit_string_for_tags(tags): func = get_func("TAGGIT_STRING_FROM_TAGS", _edit_string_for_tags) return func(tags) django-taggit-0.24.0/taggit/views.py0000664000175000017500000000317013433126115021300 0ustar jdufresnejdufresne00000000000000from __future__ import unicode_literals 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, TaggedItem def tagged_object_list(request, slug, queryset, **kwargs): if callable(queryset): queryset = queryset() kwargs["slug"] = slug tag_list_view = type( str("TagListView"), (TagListMixin, ListView), {"model": queryset.model, "queryset": queryset}, ) return tag_list_view.as_view()(request, **kwargs) class TagListMixin(object): tag_suffix = "_tag" def dispatch(self, request, *args, **kwargs): slug = kwargs.pop("slug") self.tag = get_object_or_404(Tag, slug=slug) return super(TagListMixin, self).dispatch(request, *args, **kwargs) def get_queryset(self, **kwargs): qs = super(TagListMixin, self).get_queryset(**kwargs) return qs.filter( pk__in=TaggedItem.objects.filter( tag=self.tag, content_type=ContentType.objects.get_for_model(qs.model) ).values_list("object_id", flat=True) ) def get_template_names(self): if self.tag_suffix: self.template_name_suffix = self.tag_suffix + self.template_name_suffix return super(TagListMixin, self).get_template_names() def get_context_data(self, **kwargs): context = super(TagListMixin, self).get_context_data(**kwargs) if "extra_context" not in context: context["extra_context"] = {} context["extra_context"]["tag"] = self.tag return context django-taggit-0.24.0/tests/0000775000175000017500000000000013433132635017457 5ustar jdufresnejdufresne00000000000000django-taggit-0.24.0/tests/__init__.py0000664000175000017500000000000013376364411021563 0ustar jdufresnejdufresne00000000000000django-taggit-0.24.0/tests/custom_parser.py0000664000175000017500000000025013432313636022715 0ustar jdufresnejdufresne00000000000000def comma_splitter(tag_string): return [t.strip() for t in tag_string.split(",") if t.strip()] def comma_joiner(tags): return ", ".join(t.name for t in tags) django-taggit-0.24.0/tests/forms.py0000664000175000017500000000134013433126115021151 0ustar jdufresnejdufresne00000000000000from __future__ import absolute_import, unicode_literals from django import forms from .models import CustomPKFood, DirectCustomPKFood, DirectFood, Food, OfficialFood class FoodForm(forms.ModelForm): class Meta: model = Food fields = "__all__" class DirectFoodForm(forms.ModelForm): class Meta: model = DirectFood fields = "__all__" class DirectCustomPKFoodForm(forms.ModelForm): class Meta: model = DirectCustomPKFood fields = "__all__" class CustomPKFoodForm(forms.ModelForm): class Meta: model = CustomPKFood fields = "__all__" class OfficialFoodForm(forms.ModelForm): class Meta: model = OfficialFood fields = "__all__" django-taggit-0.24.0/tests/migrations/0000775000175000017500000000000013433132635021633 5ustar jdufresnejdufresne00000000000000django-taggit-0.24.0/tests/migrations/0001_initial.py0000664000175000017500000007713713433126115024311 0ustar jdufresnejdufresne00000000000000# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.db import migrations, models import taggit.managers class Migration(migrations.Migration): dependencies = [("taggit", "0001_initial"), ("contenttypes", "0001_initial")] operations = [ migrations.CreateModel( name="Article", fields=[ ( "id", models.AutoField( auto_created=True, primary_key=True, serialize=False, help_text="", verbose_name="ID", ), ), ("title", models.CharField(help_text="", max_length=100)), ], options={}, bases=(models.Model,), ), migrations.CreateModel( name="CustomManager", fields=[ ( "id", models.AutoField( auto_created=True, primary_key=True, serialize=False, help_text="", verbose_name="ID", ), ), ( "tags", taggit.managers.TaggableManager( to="taggit.Tag", through="taggit.TaggedItem", help_text="A comma-separated list of tags.", verbose_name="Tags", ), ), ], options={}, bases=(models.Model,), ), migrations.CreateModel( name="CustomPKFood", fields=[ ( "name", models.CharField( help_text="", max_length=50, serialize=False, primary_key=True ), ) ], options={}, bases=(models.Model,), ), migrations.CreateModel( name="CustomPKPet", fields=[ ( "name", models.CharField( help_text="", max_length=50, serialize=False, primary_key=True ), ) ], options={}, bases=(models.Model,), ), migrations.CreateModel( name="CustomPKHousePet", fields=[ ( "custompkpet_ptr", models.OneToOneField( parent_link=True, auto_created=True, primary_key=True, serialize=False, to="tests.CustomPKPet", help_text="", on_delete=models.CASCADE, ), ), ("trained", models.BooleanField(default=False, help_text="")), ], options={}, bases=("tests.custompkpet",), ), migrations.CreateModel( name="DirectCustomPKFood", fields=[ ( "name", models.CharField( help_text="", max_length=50, serialize=False, primary_key=True ), ) ], options={}, bases=(models.Model,), ), migrations.CreateModel( name="DirectCustomPKPet", fields=[ ( "name", models.CharField( help_text="", max_length=50, serialize=False, primary_key=True ), ) ], options={}, bases=(models.Model,), ), migrations.CreateModel( name="DirectCustomPKHousePet", fields=[ ( "directcustompkpet_ptr", models.OneToOneField( parent_link=True, auto_created=True, primary_key=True, serialize=False, to="tests.DirectCustomPKPet", help_text="", on_delete=models.CASCADE, ), ), ("trained", models.BooleanField(default=False, help_text="")), ], options={}, bases=("tests.directcustompkpet",), ), migrations.CreateModel( name="DirectFood", fields=[ ( "id", models.AutoField( auto_created=True, primary_key=True, serialize=False, help_text="", verbose_name="ID", ), ), ("name", models.CharField(help_text="", max_length=50)), ], options={}, bases=(models.Model,), ), migrations.CreateModel( name="DirectPet", fields=[ ( "id", models.AutoField( auto_created=True, primary_key=True, serialize=False, help_text="", verbose_name="ID", ), ), ("name", models.CharField(help_text="", max_length=50)), ], options={}, bases=(models.Model,), ), migrations.CreateModel( name="DirectHousePet", fields=[ ( "directpet_ptr", models.OneToOneField( parent_link=True, auto_created=True, primary_key=True, serialize=False, to="tests.DirectPet", help_text="", on_delete=models.CASCADE, ), ), ("trained", models.BooleanField(default=False, help_text="")), ], options={}, bases=("tests.directpet",), ), migrations.CreateModel( name="Food", fields=[ ( "id", models.AutoField( auto_created=True, primary_key=True, serialize=False, help_text="", verbose_name="ID", ), ), ("name", models.CharField(help_text="", max_length=50)), ( "tags", taggit.managers.TaggableManager( to="taggit.Tag", through="taggit.TaggedItem", help_text="A comma-separated list of tags.", verbose_name="Tags", ), ), ], options={}, bases=(models.Model,), ), migrations.CreateModel( name="Movie", fields=[ ( "id", models.AutoField( auto_created=True, primary_key=True, serialize=False, help_text="", verbose_name="ID", ), ), ( "tags", taggit.managers.TaggableManager( to="taggit.Tag", through="taggit.TaggedItem", help_text="A comma-separated list of tags.", verbose_name="Tags", ), ), ], options={"abstract": False}, bases=(models.Model,), ), migrations.CreateModel( name="MultipleTags", fields=[ ( "id", models.AutoField( auto_created=True, primary_key=True, serialize=False, help_text="", verbose_name="ID", ), ) ], options={}, bases=(models.Model,), ), migrations.CreateModel( name="MultipleTagsGFK", fields=[ ( "id", models.AutoField( auto_created=True, primary_key=True, serialize=False, help_text="", verbose_name="ID", ), ), ( "tags1", taggit.managers.TaggableManager( to="taggit.Tag", through="taggit.TaggedItem", help_text="A comma-separated list of tags.", verbose_name="Tags", ), ), ], options={}, bases=(models.Model,), ), migrations.CreateModel( name="OfficialFood", fields=[ ( "id", models.AutoField( auto_created=True, primary_key=True, serialize=False, help_text="", verbose_name="ID", ), ), ("name", models.CharField(help_text="", max_length=50)), ], options={}, bases=(models.Model,), ), migrations.CreateModel( name="OfficialPet", fields=[ ( "id", models.AutoField( auto_created=True, primary_key=True, serialize=False, help_text="", verbose_name="ID", ), ), ("name", models.CharField(help_text="", max_length=50)), ], options={}, bases=(models.Model,), ), migrations.CreateModel( name="OfficialHousePet", fields=[ ( "officialpet_ptr", models.OneToOneField( parent_link=True, auto_created=True, primary_key=True, serialize=False, to="tests.OfficialPet", help_text="", on_delete=models.CASCADE, ), ), ("trained", models.BooleanField(default=False, help_text="")), ], options={}, bases=("tests.officialpet",), ), migrations.CreateModel( name="OfficialTag", fields=[ ( "id", models.AutoField( auto_created=True, primary_key=True, serialize=False, help_text="", verbose_name="ID", ), ), ( "name", models.CharField( help_text="", unique=True, max_length=100, verbose_name="Name" ), ), ( "slug", models.SlugField( help_text="", unique=True, max_length=100, verbose_name="Slug" ), ), ("official", models.BooleanField(default=False, help_text="")), ], options={"abstract": False}, bases=(models.Model,), ), migrations.CreateModel( name="OfficialThroughModel", fields=[ ( "id", models.AutoField( auto_created=True, primary_key=True, serialize=False, help_text="", verbose_name="ID", ), ), ( "object_id", models.IntegerField( help_text="", verbose_name="Object id", db_index=True ), ), ( "content_type", models.ForeignKey( related_name="tests_officialthroughmodel_tagged_items", verbose_name="Content type", to="contenttypes.ContentType", help_text="", on_delete=models.CASCADE, ), ), ( "tag", models.ForeignKey( related_name="tagged_items", to="tests.OfficialTag", help_text="", on_delete=models.CASCADE, ), ), ], options={"abstract": False}, bases=(models.Model,), ), migrations.CreateModel( name="Parent", fields=[ ( "id", models.AutoField( auto_created=True, primary_key=True, serialize=False, help_text="", verbose_name="ID", ), ) ], 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", help_text="", on_delete=models.CASCADE, ), ) ], options={}, bases=("tests.parent",), ), migrations.CreateModel( name="Pet", fields=[ ( "id", models.AutoField( auto_created=True, primary_key=True, serialize=False, help_text="", verbose_name="ID", ), ), ("name", models.CharField(help_text="", max_length=50)), ], options={}, bases=(models.Model,), ), migrations.CreateModel( name="HousePet", fields=[ ( "pet_ptr", models.OneToOneField( parent_link=True, auto_created=True, primary_key=True, serialize=False, to="tests.Pet", help_text="", on_delete=models.CASCADE, ), ), ("trained", models.BooleanField(default=False, help_text="")), ], options={}, bases=("tests.pet",), ), migrations.CreateModel( name="Photo", fields=[ ( "id", models.AutoField( auto_created=True, primary_key=True, serialize=False, help_text="", verbose_name="ID", ), ), ( "tags", taggit.managers.TaggableManager( to="taggit.Tag", through="taggit.TaggedItem", help_text="A comma-separated list of tags.", verbose_name="Tags", ), ), ], options={"abstract": False}, bases=(models.Model,), ), migrations.CreateModel( name="TaggedCustomPK", fields=[ ( "id", models.AutoField( auto_created=True, primary_key=True, serialize=False, help_text="", verbose_name="ID", ), ), ( "object_id", models.CharField( help_text="", max_length=50, verbose_name="Object id", db_index=True, ), ), ( "content_type", models.ForeignKey( related_name="tests_taggedcustompk_tagged_items", verbose_name="Content type", to="contenttypes.ContentType", help_text="", on_delete=models.CASCADE, ), ), ( "tag", models.ForeignKey( related_name="tests_taggedcustompk_items", to="taggit.Tag", help_text="", on_delete=models.CASCADE, ), ), ], options={"abstract": False}, bases=(models.Model,), ), migrations.CreateModel( name="TaggedCustomPKFood", fields=[ ( "id", models.AutoField( auto_created=True, primary_key=True, serialize=False, help_text="", verbose_name="ID", ), ), ( "content_object", models.ForeignKey( help_text="", to="tests.DirectCustomPKFood", on_delete=models.CASCADE, ), ), ( "tag", models.ForeignKey( related_name="tests_taggedcustompkfood_items", to="taggit.Tag", help_text="", on_delete=models.CASCADE, ), ), ], options={"abstract": False}, bases=(models.Model,), ), migrations.CreateModel( name="TaggedCustomPKPet", fields=[ ( "id", models.AutoField( auto_created=True, primary_key=True, serialize=False, help_text="", verbose_name="ID", ), ), ( "content_object", models.ForeignKey( help_text="", to="tests.DirectCustomPKPet", on_delete=models.CASCADE, ), ), ( "tag", models.ForeignKey( related_name="tests_taggedcustompkpet_items", to="taggit.Tag", help_text="", on_delete=models.CASCADE, ), ), ], options={"abstract": False}, bases=(models.Model,), ), migrations.CreateModel( name="TaggedFood", fields=[ ( "id", models.AutoField( auto_created=True, primary_key=True, serialize=False, help_text="", verbose_name="ID", ), ), ( "content_object", models.ForeignKey( help_text="", to="tests.DirectFood", on_delete=models.CASCADE ), ), ( "tag", models.ForeignKey( related_name="tests_taggedfood_items", to="taggit.Tag", help_text="", on_delete=models.CASCADE, ), ), ], options={"abstract": False}, bases=(models.Model,), ), migrations.CreateModel( name="TaggedPet", fields=[ ( "id", models.AutoField( auto_created=True, primary_key=True, serialize=False, help_text="", verbose_name="ID", ), ), ( "content_object", models.ForeignKey( help_text="", to="tests.DirectPet", on_delete=models.CASCADE ), ), ( "tag", models.ForeignKey( related_name="tests_taggedpet_items", to="taggit.Tag", help_text="", on_delete=models.CASCADE, ), ), ], options={"abstract": False}, bases=(models.Model,), ), migrations.CreateModel( name="Through1", fields=[ ( "id", models.AutoField( auto_created=True, primary_key=True, serialize=False, help_text="", verbose_name="ID", ), ), ( "content_object", models.ForeignKey( help_text="", to="tests.MultipleTags", on_delete=models.CASCADE ), ), ( "tag", models.ForeignKey( related_name="tests_through1_items", to="taggit.Tag", help_text="", on_delete=models.CASCADE, ), ), ], options={"abstract": False}, bases=(models.Model,), ), migrations.CreateModel( name="Through2", fields=[ ( "id", models.AutoField( auto_created=True, primary_key=True, serialize=False, help_text="", verbose_name="ID", ), ), ( "content_object", models.ForeignKey( help_text="", to="tests.MultipleTags", on_delete=models.CASCADE ), ), ( "tag", models.ForeignKey( related_name="tests_through2_items", to="taggit.Tag", help_text="", on_delete=models.CASCADE, ), ), ], options={"abstract": False}, bases=(models.Model,), ), migrations.CreateModel( name="ThroughGFK", fields=[ ( "id", models.AutoField( auto_created=True, primary_key=True, serialize=False, help_text="", verbose_name="ID", ), ), ( "object_id", models.IntegerField( help_text="", verbose_name="Object id", db_index=True ), ), ( "content_type", models.ForeignKey( related_name="tests_throughgfk_tagged_items", verbose_name="Content type", to="contenttypes.ContentType", help_text="", on_delete=models.CASCADE, ), ), ( "tag", models.ForeignKey( related_name="tagged_items", to="taggit.Tag", help_text="", on_delete=models.CASCADE, ), ), ], options={"abstract": False}, bases=(models.Model,), ), migrations.AddField( model_name="pet", 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.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.AddField( model_name="officialpet", name="tags", field=taggit.managers.TaggableManager( to="tests.OfficialTag", through="tests.OfficialThroughModel", help_text="A comma-separated list of tags.", verbose_name="Tags", ), preserve_default=True, ), migrations.AddField( model_name="officialfood", name="tags", field=taggit.managers.TaggableManager( to="tests.OfficialTag", through="tests.OfficialThroughModel", help_text="A comma-separated list of tags.", verbose_name="Tags", ), preserve_default=True, ), migrations.AddField( model_name="multipletagsgfk", name="tags2", field=taggit.managers.TaggableManager( to="taggit.Tag", through="tests.ThroughGFK", help_text="A comma-separated list of tags.", verbose_name="Tags", ), preserve_default=True, ), migrations.AddField( model_name="multipletags", name="tags1", field=taggit.managers.TaggableManager( to="taggit.Tag", through="tests.Through1", help_text="A comma-separated list of tags.", verbose_name="Tags", ), preserve_default=True, ), migrations.AddField( model_name="multipletags", name="tags2", field=taggit.managers.TaggableManager( to="taggit.Tag", through="tests.Through2", help_text="A comma-separated list of tags.", verbose_name="Tags", ), preserve_default=True, ), migrations.AddField( model_name="custompkpet", name="tags", field=taggit.managers.TaggableManager( to="taggit.Tag", through="tests.TaggedCustomPK", help_text="A comma-separated list of tags.", verbose_name="Tags", ), preserve_default=True, ), migrations.AddField( model_name="custompkfood", name="tags", field=taggit.managers.TaggableManager( to="taggit.Tag", through="tests.TaggedCustomPK", help_text="A comma-separated list of tags.", verbose_name="Tags", ), preserve_default=True, ), migrations.AddField( model_name="directpet", name="tags", field=taggit.managers.TaggableManager( to="taggit.Tag", through="tests.TaggedPet", help_text="A comma-separated list of tags.", verbose_name="Tags", ), preserve_default=True, ), migrations.AddField( model_name="directfood", name="tags", field=taggit.managers.TaggableManager( to="taggit.Tag", through="tests.TaggedFood", help_text="A comma-separated list of tags.", verbose_name="Tags", ), preserve_default=True, ), migrations.AddField( model_name="directcustompkpet", name="tags", field=taggit.managers.TaggableManager( to="taggit.Tag", through="tests.TaggedCustomPKPet", help_text="A comma-separated list of tags.", verbose_name="Tags", ), preserve_default=True, ), migrations.AddField( model_name="directcustompkfood", name="tags", field=taggit.managers.TaggableManager( to="taggit.Tag", through="tests.TaggedCustomPKFood", help_text="A comma-separated list of tags.", verbose_name="Tags", ), preserve_default=True, ), migrations.CreateModel( name="ArticleTag", fields=[], options={"proxy": True}, bases=("taggit.tag",) ), migrations.CreateModel( name="ArticleTaggedItem", fields=[], options={"proxy": True}, bases=("taggit.taggeditem",), ), migrations.AddField( model_name="article", name="tags", field=taggit.managers.TaggableManager( to="taggit.Tag", through="tests.ArticleTaggedItem", help_text="A comma-separated list of tags.", verbose_name="Tags", ), preserve_default=True, ), ] django-taggit-0.24.0/tests/migrations/0002_uuid_models.py0000664000175000017500000000620513433126115025156 0ustar jdufresnejdufresne00000000000000# -*- coding: utf-8 -*- from __future__ import unicode_literals import uuid import django.db.models.deletion from django.db import migrations, models import taggit.managers class Migration(migrations.Migration): dependencies = [ ("contenttypes", "0002_remove_content_type_name"), ("tests", "0001_initial"), ] operations = [ migrations.CreateModel( name="UUIDFood", fields=[ ( "id", models.UUIDField( default=uuid.uuid4, editable=False, primary_key=True, serialize=False, ), ), ("name", models.CharField(max_length=50)), ], ), migrations.CreateModel( name="UUIDTag", fields=[ ( "name", models.CharField(max_length=100, unique=True, verbose_name="Name"), ), ( "slug", models.SlugField(max_length=100, unique=True, verbose_name="Slug"), ), ( "id", models.UUIDField( default=uuid.uuid4, editable=False, primary_key=True, serialize=False, ), ), ], options={"abstract": False}, ), migrations.CreateModel( name="UUIDTaggedItem", fields=[ ( "id", models.AutoField( auto_created=True, primary_key=True, serialize=False, verbose_name="ID", ), ), ( "object_id", models.UUIDField(db_index=True, verbose_name="Object id"), ), ( "content_type", models.ForeignKey( on_delete=django.db.models.deletion.CASCADE, related_name="tests_uuidtaggeditem_tagged_items", to="contenttypes.ContentType", verbose_name="Content type", ), ), ( "tag", models.ForeignKey( on_delete=django.db.models.deletion.CASCADE, related_name="tests_uuidtaggeditem_items", to="tests.UUIDTag", ), ), ], options={"abstract": False}, ), migrations.AddField( model_name="uuidfood", name="tags", field=taggit.managers.TaggableManager( help_text="A comma-separated list of tags.", through="tests.UUIDTaggedItem", to="tests.UUIDTag", verbose_name="Tags", ), ), ] django-taggit-0.24.0/tests/migrations/__init__.py0000664000175000017500000000000013376364411023737 0ustar jdufresnejdufresne00000000000000django-taggit-0.24.0/tests/models.py0000664000175000017500000001433613433126115021317 0ustar jdufresnejdufresne00000000000000from __future__ import unicode_literals import uuid from django.db import models from django.utils.encoding import python_2_unicode_compatible from taggit.managers import TaggableManager from taggit.models import ( CommonGenericTaggedItemBase, GenericTaggedItemBase, GenericUUIDTaggedItemBase, Tag, TagBase, TaggedItem, TaggedItemBase, ) # Ensure that two TaggableManagers with custom through model are allowed. class Through1(TaggedItemBase): content_object = models.ForeignKey("MultipleTags", on_delete=models.CASCADE) class Through2(TaggedItemBase): content_object = models.ForeignKey("MultipleTags", on_delete=models.CASCADE) class MultipleTags(models.Model): tags1 = TaggableManager(through=Through1, related_name="tags1") tags2 = TaggableManager(through=Through2, related_name="tags2") # Ensure that two TaggableManagers with GFK via different through models are allowed. class ThroughGFK(GenericTaggedItemBase): tag = models.ForeignKey(Tag, related_name="tagged_items", on_delete=models.CASCADE) class MultipleTagsGFK(models.Model): tags1 = TaggableManager(related_name="tagsgfk1") tags2 = TaggableManager(through=ThroughGFK, related_name="tagsgfk2") @python_2_unicode_compatible class Food(models.Model): name = models.CharField(max_length=50) tags = TaggableManager() def __str__(self): return self.name @python_2_unicode_compatible class Pet(models.Model): name = models.CharField(max_length=50) tags = TaggableManager() def __str__(self): return self.name class HousePet(Pet): trained = models.BooleanField(default=False) # Test direct-tagging with custom through model class TaggedFood(TaggedItemBase): content_object = models.ForeignKey("DirectFood", on_delete=models.CASCADE) class TaggedPet(TaggedItemBase): content_object = models.ForeignKey("DirectPet", on_delete=models.CASCADE) @python_2_unicode_compatible class DirectFood(models.Model): name = models.CharField(max_length=50) tags = TaggableManager(through="TaggedFood") def __str__(self): return self.name @python_2_unicode_compatible class DirectPet(models.Model): name = models.CharField(max_length=50) tags = TaggableManager(through=TaggedPet) def __str__(self): return self.name class DirectHousePet(DirectPet): trained = models.BooleanField(default=False) # Test custom through model to model with custom PK class TaggedCustomPKFood(TaggedItemBase): content_object = models.ForeignKey("DirectCustomPKFood", on_delete=models.CASCADE) class TaggedCustomPKPet(TaggedItemBase): content_object = models.ForeignKey("DirectCustomPKPet", on_delete=models.CASCADE) @python_2_unicode_compatible class DirectCustomPKFood(models.Model): name = models.CharField(max_length=50, primary_key=True) tags = TaggableManager(through=TaggedCustomPKFood) def __str__(self): return self.name @python_2_unicode_compatible class DirectCustomPKPet(models.Model): name = models.CharField(max_length=50, primary_key=True) tags = TaggableManager(through=TaggedCustomPKPet) def __str__(self): return self.name class DirectCustomPKHousePet(DirectCustomPKPet): trained = models.BooleanField(default=False) # Test custom through model to model with custom PK using GenericForeignKey class TaggedCustomPK(CommonGenericTaggedItemBase, TaggedItemBase): object_id = models.CharField(max_length=50, verbose_name="Object id", db_index=True) @python_2_unicode_compatible class CustomPKFood(models.Model): name = models.CharField(max_length=50, primary_key=True) tags = TaggableManager(through=TaggedCustomPK) def __str__(self): return self.name @python_2_unicode_compatible class CustomPKPet(models.Model): name = models.CharField(max_length=50, primary_key=True) tags = TaggableManager(through=TaggedCustomPK) def __str__(self): return self.name class CustomPKHousePet(CustomPKPet): trained = models.BooleanField(default=False) # Test custom through model to a custom tag model class OfficialTag(TagBase): official = models.BooleanField(default=False) class OfficialThroughModel(GenericTaggedItemBase): tag = models.ForeignKey( OfficialTag, related_name="tagged_items", on_delete=models.CASCADE ) @python_2_unicode_compatible class OfficialFood(models.Model): name = models.CharField(max_length=50) tags = TaggableManager(through=OfficialThroughModel) def __str__(self): return self.name @python_2_unicode_compatible class OfficialPet(models.Model): name = models.CharField(max_length=50) tags = TaggableManager(through=OfficialThroughModel) def __str__(self): return self.name class OfficialHousePet(OfficialPet): trained = models.BooleanField(default=False) class Media(models.Model): tags = TaggableManager() class Meta: abstract = True class Photo(Media): pass class Movie(Media): pass class ArticleTag(Tag): class Meta: proxy = True def slugify(self, tag, i=None): slug = "category-%s" % tag.lower() if i is not None: slug += "-%d" % i return slug class ArticleTaggedItem(TaggedItem): class Meta: proxy = True @classmethod def tag_model(self): return ArticleTag class Article(models.Model): title = models.CharField(max_length=100) tags = TaggableManager(through=ArticleTaggedItem) class CustomManager(models.Model): class Foo(object): def __init__(*args, **kwargs): pass tags = TaggableManager(manager=Foo) class Parent(models.Model): tags = TaggableManager() class Child(Parent): pass @python_2_unicode_compatible class UUIDFood(models.Model): id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) name = models.CharField(max_length=50) tags = TaggableManager(through="UUIDTaggedItem") def __str__(self): return self.name class UUIDTag(TagBase): id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) class UUIDTaggedItem(GenericUUIDTaggedItemBase): tag = models.ForeignKey( UUIDTag, related_name="%(app_label)s_%(class)s_items", on_delete=models.CASCADE ) django-taggit-0.24.0/tests/settings.py0000664000175000017500000000232113432313636021670 0ustar jdufresnejdufresne00000000000000SECRET_KEY = "secretkey" INSTALLED_APPS = [ "django.contrib.admin", "django.contrib.auth", "django.contrib.contenttypes", "django.contrib.sessions", "django.contrib.messages", "django.contrib.staticfiles", "taggit", "tests", ] MIDDLEWARE = [ "django.middleware.security.SecurityMiddleware", "django.contrib.sessions.middleware.SessionMiddleware", "django.middleware.common.CommonMiddleware", "django.middleware.csrf.CsrfViewMiddleware", "django.contrib.auth.middleware.AuthenticationMiddleware", "django.contrib.messages.middleware.MessageMiddleware", "django.middleware.clickjacking.XFrameOptionsMiddleware", ] ROOT_URLCONF = "tests.urls" TEMPLATES = [ { "BACKEND": "django.template.backends.django.DjangoTemplates", "APP_DIRS": True, "OPTIONS": { "context_processors": [ "django.template.context_processors.debug", "django.template.context_processors.request", "django.contrib.auth.context_processors.auth", "django.contrib.messages.context_processors.messages", ] }, } ] DATABASES = {"default": {"ENGINE": "django.db.backends.sqlite3"}} django-taggit-0.24.0/tests/templates/0000775000175000017500000000000013433132635021455 5ustar jdufresnejdufresne00000000000000django-taggit-0.24.0/tests/templates/tests/0000775000175000017500000000000013433132635022617 5ustar jdufresnejdufresne00000000000000django-taggit-0.24.0/tests/templates/tests/food_tag_list.html0000664000175000017500000000017313376364411026330 0ustar jdufresnejdufresne00000000000000 Title django-taggit-0.24.0/tests/tests.py0000664000175000017500000011721113433126115021172 0ustar jdufresnejdufresne00000000000000from __future__ import absolute_import, unicode_literals import unittest import mock from django.contrib.contenttypes.models import ContentType from django.core import serializers from django.core.exceptions import ValidationError from django.db import connection, models from django.test import RequestFactory, TestCase from django.test.utils import override_settings from django.utils.encoding import force_text from .forms import ( CustomPKFoodForm, DirectCustomPKFoodForm, DirectFoodForm, FoodForm, OfficialFoodForm, ) from .models import ( Article, Child, CustomManager, CustomPKFood, CustomPKHousePet, CustomPKPet, DirectCustomPKFood, DirectCustomPKHousePet, DirectCustomPKPet, DirectFood, DirectHousePet, DirectPet, Food, HousePet, Movie, OfficialFood, OfficialHousePet, OfficialPet, OfficialTag, OfficialThroughModel, Pet, Photo, TaggedCustomPK, TaggedCustomPKFood, TaggedFood, UUIDFood, UUIDTag, ) from taggit.managers import TaggableManager, _TaggableManager from taggit.models import Tag, TaggedItem from taggit.utils import edit_string_for_tags, parse_tags from taggit.views import tagged_object_list class BaseTaggingTestCase(TestCase): def assert_tags_equal(self, qs, tags, sort=True, attr="name"): got = [getattr(obj, attr) for obj in qs] if sort: got.sort() tags.sort() self.assertEqual(got, tags) class TagModelTestCase(BaseTaggingTestCase): food_model = Food tag_model = Tag def test_unique_slug(self): apple = self.food_model.objects.create(name="apple") apple.tags.add("Red", "red") def test_update(self): special = self.tag_model.objects.create(name="special") special.save() def test_add(self): apple = self.food_model.objects.create(name="apple") yummy = self.tag_model.objects.create(name="yummy") apple.tags.add(yummy) def test_slugify(self): a = Article.objects.create(title="django-taggit 1.0 Released") a.tags.add("awesome", "release", "AWESOME") self.assert_tags_equal( a.tags.all(), ["category-awesome", "category-release", "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.assertRaisesRegex( ValueError, ( r"Cannot add 1 \(<(type|class) 'int'>\). " r"Expected or str." ), ): apple.tags.add(1) def test_gt(self): high = self.tag_model.objects.create(name="high") low = self.tag_model.objects.create(name="Low") self.assertIs(low > high, True) self.assertIs(high > low, False) def test_lt(self): high = self.tag_model.objects.create(name="high") low = self.tag_model.objects.create(name="Low") self.assertIs(high < low, True) self.assertIs(low < high, False) class TagModelDirectTestCase(TagModelTestCase): food_model = DirectFood tag_model = Tag class TagModelDirectCustomPKTestCase(TagModelTestCase): food_model = DirectCustomPKFood tag_model = Tag class TagModelCustomPKTestCase(TagModelTestCase): food_model = CustomPKFood tag_model = Tag class TagModelOfficialTestCase(TagModelTestCase): food_model = OfficialFood tag_model = OfficialTag class TagUUIDModelTestCase(TagModelTestCase): food_model = UUIDFood tag_model = UUIDTag class TaggableManagerTestCase(BaseTaggingTestCase): food_model = Food pet_model = Pet housepet_model = HousePet taggeditem_model = TaggedItem tag_model = Tag 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()), []) apple.tags.add("green") self.assert_tags_equal(apple.tags.all(), ["green"]) self.assert_tags_equal(self.food_model.tags.all(), ["green"]) pear = self.food_model.objects.create(name="pear") pear.tags.add("green") self.assert_tags_equal(pear.tags.all(), ["green"]) self.assert_tags_equal(self.food_model.tags.all(), ["green"]) apple.tags.add("red") self.assert_tags_equal(apple.tags.all(), ["green", "red"]) self.assert_tags_equal(self.food_model.tags.all(), ["green", "red"]) self.assert_tags_equal( self.food_model.tags.most_common(), ["green", "red"], sort=False ) self.assert_tags_equal( self.food_model.tags.most_common(min_count=2), ["green"], sort=False ) apple.tags.remove("green") self.assert_tags_equal(apple.tags.all(), ["red"]) self.assert_tags_equal(self.food_model.tags.all(), ["green", "red"]) tag = self.tag_model.objects.create(name="delicious") apple.tags.add(tag) self.assert_tags_equal(apple.tags.all(), ["red", "delicious"]) apple.delete() self.assert_tags_equal(self.food_model.tags.all(), ["green"]) @mock.patch("django.db.models.signals.m2m_changed.send") def test_add_new_tag_sends_m2m_changed_signals(self, send_mock): apple = self.food_model.objects.create(name="apple") apple.tags.add("green") green_pk = self.tag_model.objects.get(name="green").pk self.assertEqual(send_mock.call_count, 2) send_mock.assert_has_calls( [ mock.call( action="pre_add", instance=apple, model=self.tag_model, pk_set={green_pk}, reverse=False, sender=self.taggeditem_model, using="default", ), mock.call( action="post_add", instance=apple, model=self.tag_model, pk_set={green_pk}, reverse=False, sender=self.taggeditem_model, using="default", ), ] ) @mock.patch("django.db.models.signals.m2m_changed.send") def test_add_existing_tag_sends_m2m_changed_signals(self, send_mock): apple = self.food_model.objects.create(name="apple") green = self.tag_model.objects.create(name="green") apple.tags.add("green") self.assertEqual(send_mock.call_count, 2) send_mock.assert_has_calls( [ mock.call( action="pre_add", instance=apple, model=self.tag_model, pk_set={green.pk}, reverse=False, sender=self.taggeditem_model, using="default", ), mock.call( action="post_add", instance=apple, model=self.tag_model, pk_set={green.pk}, reverse=False, sender=self.taggeditem_model, using="default", ), ] ) @mock.patch("django.db.models.signals.m2m_changed.send") def test_add_second_tag_sends_m2m_changed_signals_with_correct_new_pks( self, send_mock ): apple = self.food_model.objects.create(name="apple") green = self.tag_model.objects.create(name="green") apple.tags.add("red") send_mock.reset_mock() apple.tags.add("green", "red") self.assertEqual(send_mock.call_count, 2) send_mock.assert_has_calls( [ mock.call( action="pre_add", instance=apple, model=self.tag_model, pk_set={green.pk}, reverse=False, sender=self.taggeditem_model, using="default", ), mock.call( action="post_add", instance=apple, model=self.tag_model, pk_set={green.pk}, reverse=False, sender=self.taggeditem_model, using="default", ), ] ) @mock.patch("django.db.models.signals.m2m_changed.send") def test_remove_tag_sends_m2m_changed_signals(self, send_mock): apple = self.food_model.objects.create(name="apple") apple.tags.add("green") green_pk = self.tag_model.objects.get(name="green").pk send_mock.reset_mock() apple.tags.remove("green") self.assertEqual(send_mock.call_count, 2) send_mock.assert_has_calls( [ mock.call( action="pre_remove", instance=apple, model=self.tag_model, pk_set={green_pk}, reverse=False, sender=self.taggeditem_model, using="default", ), mock.call( action="post_remove", instance=apple, model=self.tag_model, pk_set={green_pk}, reverse=False, sender=self.taggeditem_model, using="default", ), ] ) @mock.patch("django.db.models.signals.m2m_changed.send") def test_clear_sends_m2m_changed_signal(self, send_mock): apple = self.food_model.objects.create(name="apple") apple.tags.add("red") send_mock.reset_mock() apple.tags.clear() self.assertEqual(send_mock.call_count, 2) send_mock.assert_has_calls( [ mock.call( action="pre_clear", instance=apple, model=self.tag_model, pk_set=None, reverse=False, sender=self.taggeditem_model, using="default", ), mock.call( action="post_clear", instance=apple, model=self.tag_model, pk_set=None, reverse=False, sender=self.taggeditem_model, using="default", ), ] ) @mock.patch("django.db.models.signals.m2m_changed.send") def test_set_with_clear_true_sends_m2m_changed_signal(self, send_mock): apple = self.food_model.objects.create(name="apple") apple.tags.add("green") apple.tags.add("red") send_mock.reset_mock() apple.tags.set("red", clear=True) red_pk = self.tag_model.objects.get(name="red").pk self.assertEqual(send_mock.call_count, 4) send_mock.assert_has_calls( [ mock.call( action="pre_clear", instance=apple, model=self.tag_model, pk_set=None, reverse=False, sender=self.taggeditem_model, using="default", ), mock.call( action="post_clear", instance=apple, model=self.tag_model, pk_set=None, reverse=False, sender=self.taggeditem_model, using="default", ), mock.call( action="pre_add", instance=apple, model=self.tag_model, pk_set={red_pk}, reverse=False, sender=self.taggeditem_model, using="default", ), mock.call( action="post_add", instance=apple, model=self.tag_model, pk_set={red_pk}, reverse=False, sender=self.taggeditem_model, using="default", ), ] ) @mock.patch("django.db.models.signals.m2m_changed.send") def test_set_sends_m2m_changed_signal(self, send_mock): apple = self.food_model.objects.create(name="apple") apple.tags.add("green") send_mock.reset_mock() apple.tags.set("red") green_pk = self.tag_model.objects.get(name="green").pk red_pk = self.tag_model.objects.get(name="red").pk self.assertEqual(send_mock.call_count, 4) send_mock.assert_has_calls( [ mock.call( action="pre_remove", instance=apple, model=self.tag_model, pk_set={green_pk}, reverse=False, sender=self.taggeditem_model, using="default", ), mock.call( action="post_remove", instance=apple, model=self.tag_model, pk_set={green_pk}, reverse=False, sender=self.taggeditem_model, using="default", ), mock.call( action="pre_add", instance=apple, model=self.tag_model, pk_set={red_pk}, reverse=False, sender=self.taggeditem_model, using="default", ), mock.call( action="post_add", instance=apple, model=self.tag_model, pk_set={red_pk}, reverse=False, sender=self.taggeditem_model, using="default", ), ] ) def test_add_queries(self): # Prefill content type cache: ContentType.objects.get_for_model(self.food_model) apple = self.food_model.objects.create(name="apple") # 1. SELECT "taggit_tag"."id", "taggit_tag"."name", "taggit_tag"."slug" FROM "taggit_tag" WHERE "taggit_tag"."name" IN ('green', 'red', 'delicious') # 2. SELECT "taggit_tag"."id", "taggit_tag"."name", "taggit_tag"."slug" FROM "taggit_tag" WHERE "taggit_tag"."name" = 'green' # 3. SAVEPOINT # 4. SAVEPOINT # 5. INSERT INTO "taggit_tag" ("name", "slug") VALUES ('green', 'green') # 6. RELEASE SAVEPOINT # 7. RELEASE SAVEPOINT # 8. SELECT "taggit_tag"."id", "taggit_tag"."name", "taggit_tag"."slug" FROM "taggit_tag" WHERE "taggit_tag"."name" = 'red' # 9. SAVEPOINT # 10. SAVEPOINT # 11. INSERT INTO "taggit_tag" ("name", "slug") VALUES ('red', 'red') # 12. RELEASE SAVEPOINT # 13. RELEASE SAVEPOINT # 14. SELECT "taggit_tag"."id", "taggit_tag"."name", "taggit_tag"."slug" FROM "taggit_tag" WHERE "taggit_tag"."name" = 'delicious' # 15. SAVEPOINT # 16. SAVEPOINT # 17. INSERT INTO "taggit_tag" ("name", "slug") VALUES ('delicious', 'delicious') # 18. RELEASE SAVEPOINT # 19. RELEASE SAVEPOINT # 20. SELECT "taggit_taggeditem"."tag_id" FROM "taggit_taggeditem" WHERE ("taggit_taggeditem"."content_type_id" = 20 AND "taggit_taggeditem"."object_id" = 1) # 21. SELECT "taggit_taggeditem"."id", "taggit_taggeditem"."tag_id", "taggit_taggeditem"."content_type_id", "taggit_taggeditem"."object_id" FROM "taggit_taggeditem" WHERE ("taggit_taggeditem"."content_type_id" = 20 AND "taggit_taggeditem"."object_id" = 1 AND "taggit_taggeditem"."tag_id" = 1) # 22. SAVEPOINT # 23. INSERT INTO "taggit_taggeditem" ("tag_id", "content_type_id", "object_id") VALUES (1, 20, 1) # 24. RELEASE SAVEPOINT # 25. SELECT "taggit_taggeditem"."id", "taggit_taggeditem"."tag_id", "taggit_taggeditem"."content_type_id", "taggit_taggeditem"."object_id" FROM "taggit_taggeditem" WHERE ("taggit_taggeditem"."content_type_id" = 20 AND "taggit_taggeditem"."object_id" = 1 AND "taggit_taggeditem"."tag_id" = 2) # 26. SAVEPOINT # 27. INSERT INTO "taggit_taggeditem" ("tag_id", "content_type_id", "object_id") VALUES (2, 20, 1) # 28. RELEASE SAVEPOINT # 29. SELECT "taggit_taggeditem"."id", "taggit_taggeditem"."tag_id", "taggit_taggeditem"."content_type_id", "taggit_taggeditem"."object_id" FROM "taggit_taggeditem" WHERE ("taggit_taggeditem"."content_type_id" = 20 AND "taggit_taggeditem"."object_id" = 1 AND "taggit_taggeditem"."tag_id" = 3) # 30. SAVEPOINT # 31. INSERT INTO "taggit_taggeditem" ("tag_id", "content_type_id", "object_id") VALUES (3, 20, 1) # 32. RELEASE SAVEPOINT queries = 32 self.assertNumQueries(queries, apple.tags.add, "red", "delicious", "green") pear = self.food_model.objects.create(name="pear") # 1 query to see which tags exist # 1 query to check existing ids for sending m2m_changed signal # + 4 queries to create the intermeidary things (including SELECTs, to # make sure we dont't double create. # + 4 for save points. queries = 10 self.assertNumQueries(queries, pear.tags.add, "green", "delicious") # 1 query to check existing ids for sending m2m_changed signal self.assertNumQueries(1, pear.tags.add) def test_require_pk(self): food_instance = self.food_model() msg = ( "%s objects need to have a primary key value before you can access " "their tags." % self.food_model().__class__.__name__ ) with self.assertRaisesMessage(ValueError, msg): food_instance.tags.all() def test_delete_obj(self): apple = self.food_model.objects.create(name="apple") apple.tags.add("red") self.assert_tags_equal(apple.tags.all(), ["red"]) strawberry = self.food_model.objects.create(name="strawberry") strawberry.tags.add("red") apple.delete() self.assert_tags_equal(strawberry.tags.all(), ["red"]) def test_delete_bulk(self): apple = self.food_model.objects.create(name="apple") kitty = self.pet_model.objects.create(pk=apple.pk, name="kitty") apple.tags.add("red", "delicious", "fruit") kitty.tags.add("feline") self.food_model.objects.all().delete() self.assert_tags_equal(kitty.tags.all(), ["feline"]) def test_lookup_by_tag(self): apple = self.food_model.objects.create(name="apple") apple.tags.add("red", "green") pear = self.food_model.objects.create(name="pear") pear.tags.add("green") self.assertEqual( list(self.food_model.objects.filter(tags__name__in=["red"])), [apple] ) self.assertEqual( list(self.food_model.objects.filter(tags__name__in=["green"])), [apple, pear], ) kitty = self.pet_model.objects.create(name="kitty") kitty.tags.add("fuzzy", "red") dog = self.pet_model.objects.create(name="dog") dog.tags.add("woof", "red") self.assertEqual( list(self.food_model.objects.filter(tags__name__in=["red"]).distinct()), [apple], ) tag = self.tag_model.objects.get(name="woof") self.assertEqual(list(self.pet_model.objects.filter(tags__in=[tag])), [dog]) cat = self.housepet_model.objects.create(name="cat", trained=True) cat.tags.add("fuzzy") 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, ) 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") 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, ) def test_similarity_by_tag(self): """Test that pears are more similar to apples than watermelons""" apple = self.food_model.objects.create(name="apple") apple.tags.add("green", "juicy", "small", "sour") pear = self.food_model.objects.create(name="pear") pear.tags.add("green", "juicy", "small", "sweet") watermelon = self.food_model.objects.create(name="watermelon") watermelon.tags.add("green", "juicy", "large", "sweet") similar_objs = apple.tags.similar_objects() self.assertEqual(similar_objs, [pear, watermelon]) self.assertEqual([obj.similar_tags for obj in similar_objs], [3, 2]) def test_tag_reuse(self): apple = self.food_model.objects.create(name="apple") apple.tags.add("juicy", "juicy") self.assert_tags_equal(apple.tags.all(), ["juicy"]) def test_query_traverse(self): spot = self.pet_model.objects.create(name="Spot") spike = self.pet_model.objects.create(name="Spike") spot.tags.add("scary") spike.tags.add("fluffy") lookup_kwargs = {"%s__name" % self.pet_model._meta.model_name: "Spot"} self.assert_tags_equal( self.tag_model.objects.filter(**lookup_kwargs), ["scary"] ) def test_taggeditem_unicode(self): apple = self.food_model.objects.create(name="apple") apple.tags.add("juicy") self.assertEqual( force_text(self.taggeditem_model.objects.all()[0]), "apple tagged with juicy", ) def test_abstract_subclasses(self): p = Photo.objects.create() p.tags.add("outdoors", "pretty") self.assert_tags_equal(p.tags.all(), ["outdoors", "pretty"]) m = Movie.objects.create() m.tags.add("hd") self.assert_tags_equal(m.tags.all(), ["hd"]) def test_field_api(self): # Check if tag field, which simulates m2m, has django-like api. field = self.food_model._meta.get_field("tags") self.assertTrue(hasattr(field, "remote_field")) self.assertTrue(hasattr(field.remote_field, "model")) self.assertEqual(self.food_model, field.model) self.assertEqual(self.tag_model, field.remote_field.model) def test_names_method(self): apple = self.food_model.objects.create(name="apple") apple.tags.add("green") apple.tags.add("red") self.assertEqual(list(apple.tags.names()), ["green", "red"]) def test_slugs_method(self): apple = self.food_model.objects.create(name="apple") apple.tags.add("green and juicy") apple.tags.add("red") self.assertEqual(list(apple.tags.slugs()), ["green-and-juicy", "red"]) def test_serializes(self): apple = self.food_model.objects.create(name="apple") serializers.serialize("json", (apple,)) def test_prefetch_related(self): apple = self.food_model.objects.create(name="apple") apple.tags.add("1", "2") orange = self.food_model.objects.create(name="orange") orange.tags.add("2", "4") with self.assertNumQueries(2): list_prefetched = list( self.food_model.objects.prefetch_related("tags").all() ) with self.assertNumQueries(0): foods = {f.name: {t.name for t in f.tags.all()} for f in list_prefetched} self.assertEqual(foods, {"orange": {"2", "4"}, "apple": {"1", "2"}}) def test_internal_type_is_manytomany(self): self.assertEqual(TaggableManager().get_internal_type(), "ManyToManyField") def test_prefetch_no_extra_join(self): apple = self.food_model.objects.create(name="apple") apple.tags.add("1", "2") with self.assertNumQueries(2): list(self.food_model.objects.prefetch_related("tags").all()) join_clause = 'INNER JOIN "%s"' % self.taggeditem_model._meta.db_table self.assertEqual( connection.queries[-1]["sql"].count(join_clause), 1, connection.queries[-2:], ) @override_settings(TAGGIT_CASE_INSENSITIVE=True) def test_with_case_insensitive_option(self): spain = self.tag_model.objects.create(name="Spain", slug="spain") orange = self.food_model.objects.create(name="orange") orange.tags.add("spain") self.assertEqual(list(orange.tags.all()), [spain]) @override_settings(TAGGIT_CASE_INSENSITIVE=True) def test_with_case_insensitive_option_and_creation(self): orange = self.food_model.objects.create(name="orange") orange.tags.add("spain", "Spain") tag_names = list(orange.tags.names()) self.assertEqual(len(tag_names), 1, tag_names) @override_settings(TAGGIT_CASE_INSENSITIVE=True) def test_with_case_insensitive_option_new_and_old(self): orange = self.food_model.objects.create(name="orange") orange.tags.add("Spain") tag_names = list(orange.tags.names()) self.assertEqual(len(tag_names), 1, tag_names) orange.tags.add("spain", "Valencia") tag_names = sorted(orange.tags.names()) self.assertEqual(tag_names, ["Spain", "Valencia"]) class TaggableManagerDirectTestCase(TaggableManagerTestCase): food_model = DirectFood pet_model = DirectPet housepet_model = DirectHousePet taggeditem_model = TaggedFood class TaggableManagerDirectCustomPKTestCase(TaggableManagerTestCase): food_model = DirectCustomPKFood pet_model = DirectCustomPKPet housepet_model = DirectCustomPKHousePet taggeditem_model = TaggedCustomPKFood def test_require_pk(self): # With a CharField pk, pk is never None. So taggit has no way to tell # if the instance is saved or not. pass class TaggableManagerCustomPKTestCase(TaggableManagerTestCase): food_model = CustomPKFood pet_model = CustomPKPet housepet_model = CustomPKHousePet taggeditem_model = TaggedCustomPK def test_require_pk(self): # With a CharField pk, pk is never None. So taggit has no way to tell # if the instance is saved or not. pass class TaggableManagerOfficialTestCase(TaggableManagerTestCase): food_model = OfficialFood pet_model = OfficialPet housepet_model = OfficialHousePet taggeditem_model = OfficialThroughModel tag_model = OfficialTag def test_extra_fields(self): self.tag_model.objects.create(name="red") self.tag_model.objects.create(name="delicious", official=True) apple = self.food_model.objects.create(name="apple") apple.tags.add("delicious", "red") pear = self.food_model.objects.create(name="Pear") pear.tags.add("delicious") self.assertEqual(apple, self.food_model.objects.get(tags__official=False)) def test_get_tags_with_count(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") tag_info = self.tag_model.objects.filter( officialfood__in=[apple.id, pear.id], name="green" ).annotate(models.Count("name")) self.assertEqual(tag_info[0].name__count, 2) def test_most_common_extra_filters(self): apple = self.food_model.objects.create(name="apple") apple.tags.add("red") apple.tags.add("green") orange = self.food_model.objects.create(name="orange") orange.tags.add("orange") orange.tags.add("red") pear = self.food_model.objects.create(name="pear") pear.tags.add("green") pear.tags.add("yellow") self.assert_tags_equal( self.food_model.tags.most_common( min_count=2, extra_filters={"officialfood__name__in": ["pear", "apple"]} )[:1], ["green"], sort=False, ) self.assert_tags_equal( self.food_model.tags.most_common( min_count=2, extra_filters={"officialfood__name__in": ["orange", "apple"]}, )[:1], ["red"], sort=False, ) class TaggableManagerInitializationTestCase(TaggableManagerTestCase): """Make sure manager override defaults and sets correctly.""" food_model = Food custom_manager_model = CustomManager def test_default_manager(self): self.assertEqual(self.food_model.tags.__class__, _TaggableManager) def test_custom_manager(self): self.assertEqual(self.custom_manager_model.tags.__class__, CustomManager.Foo) class TaggableFormTestCase(BaseTaggingTestCase): form_class = FoodForm food_model = Food def _get_form_str(self, form_str): form_str %= { "help_start": '', "help_stop": "", "required": "required", } return form_str def assertFormRenders(self, form, html): self.assertHTMLEqual(str(form), self._get_form_str(html)) def test_form(self): self.assertEqual(list(self.form_class.base_fields), ["name", "tags"]) f = self.form_class({"name": "apple", "tags": "green, red, yummy"}) self.assertFormRenders( f, """
%(help_start)sA comma-separated list of tags.%(help_stop)s""", ) f.save() apple = self.food_model.objects.get(name="apple") self.assert_tags_equal(apple.tags.all(), ["green", "red", "yummy"]) f = self.form_class( {"name": "apple", "tags": "green, red, yummy, delicious"}, instance=apple ) f.save() apple = self.food_model.objects.get(name="apple") self.assert_tags_equal(apple.tags.all(), ["green", "red", "yummy", "delicious"]) self.assertEqual(self.food_model.objects.count(), 1) f = self.form_class({"name": "raspberry"}) self.assertFalse(f.is_valid()) f = self.form_class(instance=apple) self.assertFormRenders( f, """
%(help_start)sA comma-separated list of tags.%(help_stop)s""", ) apple.tags.add("has,comma") f = self.form_class(instance=apple) self.assertFormRenders( f, """
%(help_start)sA comma-separated list of tags.%(help_stop)s""", ) apple.tags.add("has space") f = self.form_class(instance=apple) self.assertFormRenders( f, """
%(help_start)sA comma-separated list of tags.%(help_stop)s""", ) def test_formfield(self): tm = TaggableManager( verbose_name="categories", help_text="Add some categories", blank=True ) ff = tm.formfield() self.assertEqual(ff.label, "Categories") self.assertEqual(ff.help_text, "Add some categories") self.assertEqual(ff.required, False) self.assertEqual(ff.clean(""), []) tm = TaggableManager() ff = tm.formfield() self.assertRaises(ValidationError, ff.clean, "") class TaggableFormDirectTestCase(TaggableFormTestCase): form_class = DirectFoodForm food_model = DirectFood class TaggableFormDirectCustomPKTestCase(TaggableFormTestCase): form_class = DirectCustomPKFoodForm food_model = DirectCustomPKFood class TaggableFormCustomPKTestCase(TaggableFormTestCase): form_class = CustomPKFoodForm food_model = CustomPKFood class TaggableFormOfficialTestCase(TaggableFormTestCase): form_class = OfficialFoodForm food_model = OfficialFood class TagStringParseTestCase(unittest.TestCase): """ Ported from Jonathan Buchanan's `django-tagging `_ """ def test_with_simple_space_delimited_tags(self): """ Test with simple space-delimited tags. """ self.assertEqual(parse_tags("one"), ["one"]) self.assertEqual(parse_tags("one two"), ["one", "two"]) self.assertEqual(parse_tags("one two three"), ["one", "three", "two"]) self.assertEqual(parse_tags("one one two two"), ["one", "two"]) def test_with_comma_delimited_multiple_words(self): """ Test with comma-delimited multiple words. An unquoted comma in the input will trigger this. """ self.assertEqual(parse_tags(",one"), ["one"]) 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"] ) def test_with_double_quoted_multiple_words(self): """ Test with double-quoted multiple words. A completed quote will trigger this. Unclosed quotes are ignored. """ self.assertEqual(parse_tags('"one'), ["one"]) self.assertEqual(parse_tags('"one two'), ["one", "two"]) 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"] ) def test_with_no_loose_commas(self): """ Test with no loose commas -- split on spaces. """ self.assertEqual(parse_tags('one two "thr,ee"'), ["one", "thr,ee", "two"]) def test_with_loose_commas(self): """ Loose commas - split on commas """ self.assertEqual(parse_tags('"one", two three'), ["one", "two three"]) def test_tags_with_double_quotes_can_contain_commas(self): """ Double quotes can contain commas """ self.assertEqual( parse_tags('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"]) def test_with_naughty_input(self): """ Test with naughty input. """ # Bad users! Naughty users! self.assertEqual(parse_tags(None), []) self.assertEqual(parse_tags(""), []) self.assertEqual(parse_tags('"'), []) self.assertEqual(parse_tags('""'), []) self.assertEqual(parse_tags('"' * 7), []) 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"], ) def test_recreation_of_tag_list_string_representations(self): plain = Tag(name="plain") spaces = Tag(name="spa ces") comma = Tag(name="com,ma") self.assertEqual(edit_string_for_tags([plain]), "plain") self.assertEqual(edit_string_for_tags([plain, spaces]), '"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]), '"com,ma", plain') self.assertEqual(edit_string_for_tags([comma, spaces]), '"com,ma", "spa ces"') @override_settings(TAGGIT_TAGS_FROM_STRING="tests.custom_parser.comma_splitter") def test_custom_comma_splitter(self): self.assertEqual(parse_tags(" Cued Speech "), ["Cued Speech"]) self.assertEqual(parse_tags(" ,Cued Speech, "), ["Cued Speech"]) self.assertEqual(parse_tags("Cued Speech"), ["Cued Speech"]) self.assertEqual( parse_tags("Cued Speech, dictionary"), ["Cued Speech", "dictionary"] ) @override_settings(TAGGIT_STRING_FROM_TAGS="tests.custom_parser.comma_joiner") def test_custom_comma_joiner(self): a = Tag(name="Cued Speech") b = Tag(name="transliterator") self.assertEqual(edit_string_for_tags([a, b]), "Cued Speech, transliterator") class DeconstructTestCase(unittest.TestCase): def test_deconstruct_kwargs_kept(self): instance = TaggableManager(through=OfficialThroughModel, to="dummy.To") name, path, args, kwargs = instance.deconstruct() new_instance = TaggableManager(*args, **kwargs) self.assertEqual( "tests.OfficialThroughModel", new_instance.remote_field.through ) self.assertEqual("dummy.To", new_instance.remote_field.model) 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.assertEqual(4, no_prefetch_tags.count()) child = Child.objects.prefetch_related("tags").get() prefetch_tags = child.tags.all() self.assertEqual(4, prefetch_tags.count()) self.assertEqual( {t.name for t in no_prefetch_tags}, {t.name for t in prefetch_tags} ) class TagListViewTests(TestCase): model = Food def setUp(self): super(TagListViewTests, self).setUp() self.factory = RequestFactory() self.slug = "green" self.apple = self.model.objects.create(name="apple") self.apple.tags.add(self.slug) self.strawberry = self.model.objects.create(name="strawberry") self.strawberry.tags.add("red") def test_url_request_returns_view(self): request = self.factory.get("/food/tags/{}/".format(self.slug)) queryset = self.model.objects.all() response = tagged_object_list(request, self.slug, queryset) self.assertEqual(response.status_code, 200) self.assertIn(self.apple, response.context_data["object_list"]) self.assertNotIn(self.strawberry, response.context_data["object_list"]) self.assertEqual( self.apple.tags.first(), response.context_data["extra_context"]["tag"] ) def test_list_view_returns_single(self): response = self.client.get("/food/tags/{}/".format(self.slug)) self.assertEqual(response.status_code, 200) self.assertIn(self.apple, response.context_data["object_list"]) self.assertNotIn(self.strawberry, response.context_data["object_list"]) django-taggit-0.24.0/tests/urls.py0000664000175000017500000000032713432313636021021 0ustar jdufresnejdufresne00000000000000from django.conf.urls import url from .views import FoodTagListView urlpatterns = [ url( r"^food/tags/(?P[a-z0-9_-]+)/$", FoodTagListView.as_view(), name="food-tag-list", ) ] django-taggit-0.24.0/tests/views.py0000664000175000017500000000026213376364411021173 0ustar jdufresnejdufresne00000000000000from django.views.generic.list import ListView from .models import Food from taggit.views import TagListMixin class FoodTagListView(TagListMixin, ListView): model = Food django-taggit-0.24.0/tox.ini0000664000175000017500000000153313433126115017626 0ustar jdufresnejdufresne00000000000000[tox] minversion = 1.9 envlist = black flake8 isort py{27,34,35,36}-django111 py{34,35,36,37}-django20 py{35,36,37}-django21 py{35,36,37}-django22 py{36,37}-djangomaster [testenv] deps = django111: Django~=1.11.0 django20: Django~=2.0.0 django21: Django~=2.1.0 django22: Django>=2.2a1<3.0 djangomaster: https://github.com/django/django/archive/master.tar.gz#egg=django coverage mock commands = coverage run -m django test --settings=tests.settings {posargs} coverage report [testenv:black] basepython = python3 skip_install = true deps = black commands = black --check --diff . [testenv:flake8] basepython = python3 skip_install = true deps = flake8 commands = flake8 [testenv:isort] basepython = python3 skip_install = true deps = isort commands = isort --recursive --check-only --diff