django-taggit-0.18.0/0000755000076500000240000000000012647304630014736 5ustar coreyfstaff00000000000000django-taggit-0.18.0/AUTHORS0000644000076500000240000000066312625364503016014 0ustar coreyfstaff00000000000000django-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 django-taggit-0.18.0/CHANGELOG.txt0000644000076500000240000001334712647304553017002 0ustar coreyfstaff00000000000000Changelog ========= 0.18.0 (2016-01-18) ~~~~~~~~~~~~~~~~~~~ * Add option to override default tag string parsing * https://github.com/alex/django-taggit/pull/232 * Drop support for Python 2.6 * https://github.com/alex/django-taggit/pull/373 0.17.6 (2015-12-09) ~~~~~~~~~~~~~~~~~~~ * Silence Django 1.9 warning * https://github.com/alex/django-taggit/pull/366 0.17.5 (2015-11-27) ~~~~~~~~~~~~~~~~~~~ * Django 1.9 compatibility fix * https://github.com/alex/django-taggit/pull/364 0.17.4 (2015-11-25) ~~~~~~~~~~~~~~~~~~~ * Allows custom Through Model with GenericForeignKey * https://github.com/alex/django-taggit/pull/359 0.17.3 (2015-10-26) ~~~~~~~~~~~~~~~~~~~ * Silence Django 1.9 warning about on_delete * https://github.com/alex/django-taggit/pull/338 0.17.2 (2015-10-25) ~~~~~~~~~~~~~~~~~~~ * Django 1.9 beta compatibility * https://github.com/alex/django-taggit/pull/352 0.17.1 (2015-09-10) ~~~~~~~~~~~~~~~~~~~ * Fix unknown column `object_id` issue with Django 1.6+ * https://github.com/alex/django-taggit/pull/305 0.17.0 (2015-08-14) ~~~~~~~~~~~~~~~~~~~ * Database index added on TaggedItem fields content_type & object_id * https://github.com/alex/django-taggit/pull/319 0.16.4 (2015-08-13) ~~~~~~~~~~~~~~~~~~~ * Access default manager via class instead of instance * https://github.com/alex/django-taggit/pull/335 0.16.3 (2015-08-08) ~~~~~~~~~~~~~~~~~~~ * Prevent IntegrityError with custom TagBase classes * https://github.com/alex/django-taggit/pull/334 0.16.2 (2015-07-13) ~~~~~~~~~~~~~~~~~~~ * Fix an admin bug related to the `Manager` property `through_fields` * https://github.com/alex/django-taggit/pull/328 0.16.1 (2015-07-09) ~~~~~~~~~~~~~~~~~~~ * Fix bug that assumed all primary keys are named 'id' * https://github.com/alex/django-taggit/pull/322 0.16.0 (2015-07-04) ~~~~~~~~~~~~~~~~~~~ * Add option to allow case-insensitive tags * https://github.com/alex/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/alex/django-taggit/pull/315 * https://github.com/alex/django-taggit/pull/273 0.14.0 (2015-04-26) ~~~~~~~~~~~~~~~~~~~ * Prevent extra JOIN when prefetching * https://github.com/alex/django-taggit/pull/275 * Prevent _meta warnings with Django 1.8 * https://github.com/alex/django-taggit/pull/299 0.13.0 (2015-04-02) ~~~~~~~~~~~~~~~~~~~ * Django 1.8 support * https://github.com/alex/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.18.0/django_taggit.egg-info/0000755000076500000240000000000012647304630021231 5ustar coreyfstaff00000000000000django-taggit-0.18.0/django_taggit.egg-info/dependency_links.txt0000644000076500000240000000000112647304630025277 0ustar coreyfstaff00000000000000 django-taggit-0.18.0/django_taggit.egg-info/not-zip-safe0000644000076500000240000000000112625365604023463 0ustar coreyfstaff00000000000000 django-taggit-0.18.0/django_taggit.egg-info/PKG-INFO0000644000076500000240000000440212647304630022326 0ustar coreyfstaff00000000000000Metadata-Version: 1.1 Name: django-taggit Version: 0.18.0 Summary: django-taggit is a reusable Django application for simple tagging. Home-page: http://github.com/alex/django-taggit/tree/master Author: Alex Gaynor Author-email: alex.gaynor@gmail.com License: BSD Description: django-taggit ============= ``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.4.5 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: Intended Audience :: Developers Classifier: License :: OSI Approved :: BSD License Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3.2 Classifier: Programming Language :: Python :: 3.3 Classifier: Programming Language :: Python :: 3.4 Classifier: Framework :: Django django-taggit-0.18.0/django_taggit.egg-info/SOURCES.txt0000644000076500000240000000336112647304630023120 0ustar coreyfstaff00000000000000AUTHORS CHANGELOG.txt LICENSE MANIFEST.in README.rst runtests.py 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/top_level.txt docs/Makefile docs/admin.txt docs/api.txt docs/changelog.txt docs/conf.py 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/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/en/LC_MESSAGES/django.po taggit/locale/eo/LC_MESSAGES/django.mo taggit/locale/eo/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/migrations/0001_initial.py taggit/migrations/0002_auto_20150616_2121.py taggit/migrations/__init__.py taggit/south_migrations/0001_initial.py taggit/south_migrations/0002_unique_tagnames.py taggit/south_migrations/__init__.py tests/__init__.py tests/custom_parser.py tests/forms.py tests/models.py tests/tests.py tests/migrations/0001_initial.py tests/migrations/__init__.pydjango-taggit-0.18.0/django_taggit.egg-info/top_level.txt0000644000076500000240000000000712647304630023760 0ustar coreyfstaff00000000000000taggit django-taggit-0.18.0/docs/0000755000076500000240000000000012647304630015666 5ustar coreyfstaff00000000000000django-taggit-0.18.0/docs/admin.txt0000644000076500000240000000137012625364503017521 0ustar coreyfstaff00000000000000Using 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. 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 'flatchoices' 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 really would like to add it, you can read the `Django documentation `_. django-taggit-0.18.0/docs/api.txt0000644000076500000240000001012112625364503017174 0ustar coreyfstaff00000000000000The 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) Removes all the current tags and then adds the specified tags to the object. .. method: most_common() Returns a ``QuerySet`` of all tags, annotated with the number of times they appear, available as the ``num_times`` attribute on each tag. The ``QuerySet``is ordered by ``num_times``, descending. The ``QuerySet`` is lazily evaluated, and can be sliced efficiently. .. method:: similar_objects() Returns a list (not a lazy ``QuerySet``) of other objects tagged similarly to this one, ordered with most similar first. Each object in the list is decorated with a ``similar_tags`` attribute, the number of tags it shares with this object. If the model is using generic tagging (the default), this method searches tagged objects from all classes. If you are querying on a model with its own tagging through table, only other instances of the same model will be returned. .. 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. Aggregation ~~~~~~~~~~~ Unfortunately, due to a `bug in Django `_, it is not currently (Django < 1.6) possible to use aggregation in conjunction with ``taggit``. This is a `documented interaction `_ of generic relations (which ``taggit`` uses internally) and aggregates. django-taggit-0.18.0/docs/changelog.txt0000644000076500000240000000003612625364503020356 0ustar coreyfstaff00000000000000.. include:: ../CHANGELOG.txt django-taggit-0.18.0/docs/conf.py0000644000076500000240000001454412625364503017176 0ustar coreyfstaff00000000000000# -*- 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 sys, os # 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.12' # The full version, including alpha/beta/rc tags. release = '0.12' # 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 = {'http://docs.python.org/': None} django-taggit-0.18.0/docs/custom_tagging.txt0000644000076500000240000001723612647304553021456 0ustar coreyfstaff00000000000000Customizing 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 ~~~~~~~~~~~~~~~~~~~~~~~~~ .. note:: ``GenericUUIDTaggedItemBase`` relies on Django UUIDField introduced with Django 1.8. Therefore ``GenericUUIDTaggedItemBase`` is only defined if you are using Django 1.8+. 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.template.defaultfilters.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(tag_string): 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.18.0/docs/external_apps.txt0000644000076500000240000000306312625364503021277 0ustar coreyfstaff00000000000000External 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 ``taggit`` on `github`__, add it to this list, and send a pull request. * ``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``. Available on `github`__. * ``django-taggit-templatetags``: Provides several templatetags, including one for tag clouds, to expose various ``taggit`` APIs directly to templates. Available on `github`__. * ``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``. Available on `github`__. * ``django-taggit-labels``: Provides a clickable label widget for the Django admin for user friendly selection from managed tag sets. __ http://github.com/alex/django-taggit __ http://github.com/frankwiles/django-taggit-suggest __ http://github.com/feuervogel/django-taggit-templatetags __ http://github.com/mfcovington/django-taggit-helpers __ https://github.com/bennylope/django-taggit-labels django-taggit-0.18.0/docs/forms.txt0000644000076500000240000000425212625364503017561 0ustar coreyfstaff00000000000000.. _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.18.0/docs/getting_started.txt0000644000076500000240000000171212625364503021620 0ustar coreyfstaff00000000000000Getting 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 syncdb` or `./manage.py migrate` if using migrations. .. note:: If you are using South you'll have to add the following setting, since taggit uses Django migrations by default:: SOUTH_MIGRATION_MODULES = { 'taggit': 'taggit.south_migrations', } 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.18.0/docs/index.txt0000644000076500000240000000204512625364503017540 0ustar coreyfstaff00000000000000Welcome to django-taggit's documentation! ========================================= ``django-taggit`` is a reusable Django application designed to making adding tagging to your project easy and fun. ``django-taggit`` works with Django 1.4.5+ and Python 2.7-3.X. .. warning:: Since version 0.10.0 taggit uses South for database migrations. This means that users who are upgrading to 0.10.0 and up will have to fake the initial migration, like this:: python manage.py migrate taggit --fake 0001 python manage.py migrate Since version 0.12.0 taggit uses Django migrations by default. South users have to adjust their settings:: SOUTH_MIGRATION_MODULES = { 'taggit': 'taggit.south_migrations', } For more information, see `south documentation`__ .. toctree:: :maxdepth: 2 getting_started forms admin api custom_tagging external_apps changelog Indices and tables ================== * :ref:`genindex` * :ref:`modindex` * :ref:`search` __ http://south.readthedocs.org/en/latest/ django-taggit-0.18.0/docs/Makefile0000644000076500000240000000610412625364503017330 0ustar coreyfstaff00000000000000# 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.18.0/LICENSE0000644000076500000240000000301012625364503015736 0ustar coreyfstaff00000000000000Copyright (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.18.0/MANIFEST.in0000644000076500000240000000030212625364503016470 0ustar coreyfstaff00000000000000include AUTHORS include CHANGELOG.txt include LICENSE include README.rst include tox.ini include runtests.py recursive-include docs * recursive-include taggit/locale * recursive-include tests * django-taggit-0.18.0/PKG-INFO0000644000076500000240000000440212647304630016033 0ustar coreyfstaff00000000000000Metadata-Version: 1.1 Name: django-taggit Version: 0.18.0 Summary: django-taggit is a reusable Django application for simple tagging. Home-page: http://github.com/alex/django-taggit/tree/master Author: Alex Gaynor Author-email: alex.gaynor@gmail.com License: BSD Description: django-taggit ============= ``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.4.5 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: Intended Audience :: Developers Classifier: License :: OSI Approved :: BSD License Classifier: Operating System :: OS Independent Classifier: Programming Language :: Python Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3.2 Classifier: Programming Language :: Python :: 3.3 Classifier: Programming Language :: Python :: 3.4 Classifier: Framework :: Django django-taggit-0.18.0/README.rst0000644000076500000240000000216412625364503016431 0ustar coreyfstaff00000000000000django-taggit ============= ``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.4.5 or greater. For more info check out the `documentation `_. And for questions about usage or development you can contact the `mailinglist `_. django-taggit-0.18.0/runtests.py0000755000076500000240000000114712647224522017206 0ustar coreyfstaff00000000000000#!/usr/bin/env python import sys from django.conf import settings from django.core.management import execute_from_command_line if not settings.configured: settings.configure( DATABASES={ 'default': { 'ENGINE': 'django.db.backends.sqlite3', } }, INSTALLED_APPS=[ 'django.contrib.contenttypes', 'taggit', 'tests', ], MIDDLEWARE_CLASSES=[], ) def runtests(): argv = sys.argv[:1] + ['test'] + sys.argv[1:] execute_from_command_line(argv) if __name__ == '__main__': runtests() django-taggit-0.18.0/setup.cfg0000644000076500000240000000034012647304630016554 0ustar coreyfstaff00000000000000[metadata] license-file = LICENSE [wheel] universal = 1 [flake8] ignore = E501,E302 exclude = south_migrations,migrations [isort] forced_separate = tests,taggit [egg_info] tag_build = tag_date = 0 tag_svn_revision = 0 django-taggit-0.18.0/setup.py0000644000076500000240000000232212647304553016453 0ustar coreyfstaff00000000000000from setuptools import setup, find_packages 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='http://github.com/alex/django-taggit/tree/master', packages=find_packages(exclude=('tests*',)), package_data = { 'taggit': [ 'locale/*/LC_MESSAGES/*', ], }, license='BSD', classifiers=[ 'Development Status :: 5 - Production/Stable', 'Environment :: Web Environment', 'Intended Audience :: Developers', 'License :: OSI Approved :: BSD License', 'Operating System :: OS Independent', 'Programming Language :: Python', 'Programming Language :: Python', 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3.2', 'Programming Language :: Python :: 3.3', 'Programming Language :: Python :: 3.4', 'Framework :: Django', ], include_package_data=True, zip_safe=False, ) django-taggit-0.18.0/taggit/0000755000076500000240000000000012647304630016215 5ustar coreyfstaff00000000000000django-taggit-0.18.0/taggit/__init__.py0000644000076500000240000000002512647304553020327 0ustar coreyfstaff00000000000000VERSION = (0, 18, 0) django-taggit-0.18.0/taggit/admin.py0000644000076500000240000000070012625364503017655 0ustar coreyfstaff00000000000000from __future__ import unicode_literals from django.contrib import admin from taggit.models import Tag, TaggedItem class TaggedItemInline(admin.StackedInline): model = TaggedItem class TagAdmin(admin.ModelAdmin): inlines = [ TaggedItemInline ] list_display = ["name", "slug"] ordering = ["name", "slug"] search_fields = ["name"] prepopulated_fields = {"slug": ["name"]} admin.site.register(Tag, TagAdmin) django-taggit-0.18.0/taggit/forms.py0000644000076500000240000000153012625364503017715 0ustar coreyfstaff00000000000000from __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 render(self, name, value, attrs=None): if value is not None and not isinstance(value, six.string_types): value = edit_string_for_tags([ o.tag for o in value.select_related("tag")]) return super(TagWidget, self).render(name, value, attrs) 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.18.0/taggit/locale/0000755000076500000240000000000012647304630017454 5ustar coreyfstaff00000000000000django-taggit-0.18.0/taggit/locale/cs/0000755000076500000240000000000012647304630020061 5ustar coreyfstaff00000000000000django-taggit-0.18.0/taggit/locale/cs/LC_MESSAGES/0000755000076500000240000000000012647304630021646 5ustar coreyfstaff00000000000000django-taggit-0.18.0/taggit/locale/cs/LC_MESSAGES/django.mo0000644000076500000240000000204512625364503023447 0ustar coreyfstaff00000000000000Þ• |Ü !@ `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.18.0/taggit/locale/cs/LC_MESSAGES/django.po0000644000076500000240000000264612625364503023461 0ustar coreyfstaff00000000000000# 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.18.0/taggit/locale/de/0000755000076500000240000000000012647304630020044 5ustar coreyfstaff00000000000000django-taggit-0.18.0/taggit/locale/de/LC_MESSAGES/0000755000076500000240000000000012647304630021631 5ustar coreyfstaff00000000000000django-taggit-0.18.0/taggit/locale/de/LC_MESSAGES/django.mo0000644000076500000240000000246312625364503023436 0ustar coreyfstaff00000000000000Þ• „ì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.18.0/taggit/locale/de/LC_MESSAGES/django.po0000644000076500000240000000310212625364503023430 0ustar coreyfstaff00000000000000#, 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.18.0/taggit/locale/en/0000755000076500000240000000000012647304630020056 5ustar coreyfstaff00000000000000django-taggit-0.18.0/taggit/locale/en/LC_MESSAGES/0000755000076500000240000000000012647304630021643 5ustar coreyfstaff00000000000000django-taggit-0.18.0/taggit/locale/en/LC_MESSAGES/django.po0000644000076500000240000000250512625364503023450 0ustar coreyfstaff00000000000000# 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.18.0/taggit/locale/eo/0000755000076500000240000000000012647304630020057 5ustar coreyfstaff00000000000000django-taggit-0.18.0/taggit/locale/eo/LC_MESSAGES/0000755000076500000240000000000012647304630021644 5ustar coreyfstaff00000000000000django-taggit-0.18.0/taggit/locale/eo/LC_MESSAGES/django.mo0000644000076500000240000000245212625364503023447 0ustar coreyfstaff00000000000000Þ• „ì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.18.0/taggit/locale/eo/LC_MESSAGES/django.po0000644000076500000240000000306512625364503023453 0ustar coreyfstaff00000000000000msgid "" 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.18.0/taggit/locale/he/0000755000076500000240000000000012647304630020050 5ustar coreyfstaff00000000000000django-taggit-0.18.0/taggit/locale/he/LC_MESSAGES/0000755000076500000240000000000012647304630021635 5ustar coreyfstaff00000000000000django-taggit-0.18.0/taggit/locale/he/LC_MESSAGES/django.mo0000644000076500000240000000151712625364503023441 0ustar coreyfstaff00000000000000Þ•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.18.0/taggit/locale/he/LC_MESSAGES/django.po0000644000076500000240000000301612625364503023440 0ustar coreyfstaff00000000000000# 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.18.0/taggit/locale/it/0000755000076500000240000000000012647304630020070 5ustar coreyfstaff00000000000000django-taggit-0.18.0/taggit/locale/it/LC_MESSAGES/0000755000076500000240000000000012647304630021655 5ustar coreyfstaff00000000000000django-taggit-0.18.0/taggit/locale/it/LC_MESSAGES/django.mo0000644000076500000240000000225312625364503023457 0ustar coreyfstaff00000000000000Þ• „ì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.18.0/taggit/locale/it/LC_MESSAGES/django.po0000644000076500000240000000316312625364503023463 0ustar coreyfstaff00000000000000# 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.18.0/taggit/locale/ja/0000755000076500000240000000000012647304630020046 5ustar coreyfstaff00000000000000django-taggit-0.18.0/taggit/locale/ja/LC_MESSAGES/0000755000076500000240000000000012647304630021633 5ustar coreyfstaff00000000000000django-taggit-0.18.0/taggit/locale/ja/LC_MESSAGES/django.mo0000644000076500000240000000263512625364503023441 0ustar coreyfstaff00000000000000Þ• „ì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.18.0/taggit/locale/ja/LC_MESSAGES/django.po0000644000076500000240000000325012625364503023436 0ustar coreyfstaff00000000000000msgid "" 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.18.0/taggit/locale/nb/0000755000076500000240000000000012647304630020053 5ustar coreyfstaff00000000000000django-taggit-0.18.0/taggit/locale/nb/LC_MESSAGES/0000755000076500000240000000000012647304630021640 5ustar coreyfstaff00000000000000django-taggit-0.18.0/taggit/locale/nb/LC_MESSAGES/django.mo0000644000076500000240000000237612625364503023450 0ustar coreyfstaff00000000000000Þ• „ì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.18.0/taggit/locale/nb/LC_MESSAGES/django.po0000644000076500000240000000331012625364503023440 0ustar coreyfstaff00000000000000# 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.18.0/taggit/locale/nl/0000755000076500000240000000000012647304630020065 5ustar coreyfstaff00000000000000django-taggit-0.18.0/taggit/locale/nl/LC_MESSAGES/0000755000076500000240000000000012647304630021652 5ustar coreyfstaff00000000000000django-taggit-0.18.0/taggit/locale/nl/LC_MESSAGES/django.mo0000644000076500000240000000230112625364503023446 0ustar coreyfstaff00000000000000Þ• „ì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.18.0/taggit/locale/nl/LC_MESSAGES/django.po0000644000076500000240000000264412625364503023463 0ustar coreyfstaff00000000000000msgid "" 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.18.0/taggit/locale/pt_BR/0000755000076500000240000000000012647304630020462 5ustar coreyfstaff00000000000000django-taggit-0.18.0/taggit/locale/pt_BR/LC_MESSAGES/0000755000076500000240000000000012647304630022247 5ustar coreyfstaff00000000000000django-taggit-0.18.0/taggit/locale/pt_BR/LC_MESSAGES/django.mo0000644000076500000240000000205012625364503024044 0ustar coreyfstaff00000000000000Þ• |Ü !@ `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.18.0/taggit/locale/pt_BR/LC_MESSAGES/django.po0000644000076500000240000000251012625364503024050 0ustar coreyfstaff00000000000000# 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.18.0/taggit/locale/ru/0000755000076500000240000000000012647304630020102 5ustar coreyfstaff00000000000000django-taggit-0.18.0/taggit/locale/ru/LC_MESSAGES/0000755000076500000240000000000012647304630021667 5ustar coreyfstaff00000000000000django-taggit-0.18.0/taggit/locale/ru/LC_MESSAGES/django.mo0000644000076500000240000000275112625364503023474 0ustar coreyfstaff00000000000000Þ• „ì01P p`}Þ ã.í! % 1>ÂC92@s ‘2C4UŠ “ž ½ Þ   %(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-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.18.0/taggit/locale/ru/LC_MESSAGES/django.po0000644000076500000240000000363112625364503023475 0ustar coreyfstaff00000000000000# 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" #: 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.18.0/taggit/managers.py0000644000076500000240000005333112647304553020375 0ustar coreyfstaff00000000000000from __future__ import unicode_literals from operator import attrgetter from django import VERSION from django.contrib.contenttypes.models import ContentType from django.conf import settings from django.db import models, router from django.db.models.fields import Field from django.db.models.fields.related import (add_lazy_relation, ManyToManyRel, OneToOneRel, RelatedField) 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 _get_field, require_instance_manager if VERSION < (1, 8): # related.py was removed in Django 1.8 # Depending on how Django was updated, related.py could still exist # on the users system even on Django 1.8+, so we check the Django # version before importing it to make sure this doesn't get imported # accidentally. from django.db.models.related import RelatedObject else: RelatedObject = None try: from django.contrib.contenttypes.fields import GenericRelation except ImportError: # django < 1.7 from django.contrib.contenttypes.generic import GenericRelation try: from django.db.models.query_utils import PathInfo except ImportError: # Django < 1.8 try: from django.db.models.related import PathInfo except ImportError: pass # PathInfo is not used on Django < 1.6 def _model_name(model): if VERSION < (1, 7): return model._meta.module_name else: return model._meta.model_name class TaggableRel(ManyToManyRel): def __init__(self, field, related_name, through, to=None): # rel.to renamed to rel.model in Django 1.9 if VERSION >= (1, 9): self.model = to else: self.to = to self.related_name = related_name self.limit_choices_to = {} self.symmetrical = True self.multiple = True self.through = None if VERSION < (1, 7) else through self.field = field self.through_fields = None def get_joining_columns(self): return self.field.get_reverse_joining_columns() def get_extra_restriction(self, where_class, alias, related_alias): return self.field.get_extra_restriction(where_class, related_alias, alias) 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, qn, connection): 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): self.through = through self.model = model self.instance = instance self.prefetch_cache_name = prefetch_cache_name self._db = None 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] from django.db import connections 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): set(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)) } ) return (qs, attrgetter('_prefetch_related_val'), lambda obj: obj._get_pk_val(), False, self.prefetch_cache_name) # Django < 1.6 uses the previous name of query_set get_query_set = get_queryset get_prefetch_query_set = get_prefetch_queryset def _lookup_kwargs(self): return self.through.lookup_kwargs(self.instance) @require_instance_manager def add(self, *tags): str_tags = set() tag_objs = set() for t in tags: if isinstance(t, self.through.tag_model()): tag_objs.add(t) elif isinstance(t, six.string_types): str_tags.add(t) else: raise ValueError("Cannot add {0} ({1}). Expected {2} or str.".format( t, type(t), type(self.through.tag_model()))) if getattr(settings, 'TAGGIT_CASE_INSENSITIVE', False): # 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 = self.through.tag_model().objects.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 = self.through.tag_model().objects.filter( name__in=str_tags ) tags_to_create = str_tags - set(t.name for t in existing) tag_objs.update(existing) for new_tag in tags_to_create: tag_objs.add(self.through.tag_model().objects.create(name=new_tag)) for tag in tag_objs: self.through.objects.get_or_create(tag=tag, **self._lookup_kwargs()) @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): self.clear() self.add(*tags) @require_instance_manager def remove(self, *tags): self.through.objects.filter(**self._lookup_kwargs()).filter( tag__name__in=tags).delete() @require_instance_manager def clear(self): self.through.objects.filter(**self._lookup_kwargs()).delete() def most_common(self): return self.get_queryset().annotate( num_times=models.Count(self.through.tag_relname()) ).order_by('-num_times') @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 = _get_field(self.through, lookup_keys[0]) rel_model = f.rel.model if VERSION >= (1, 9) else f.rel.to objs = rel_model._default_manager.filter(**{ "%s__in" % f.rel.field_name: [r["content_object"] for r in qs] }) for obj in objs: items[(getattr(obj, f.rel.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 # _TaggableManager needs to be hashable but BaseManagers in Django 1.8+ overrides # the __eq__ method which makes the default __hash__ method disappear. # This checks if the __hash__ attribute is None, and if so, it reinstates the original method. if models.Manager.__hash__ is None: __hash__ = object.__hash__ class TaggableManager(RelatedField, Field): # 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 self.swappable = False self.manager = manager rel = TaggableRel(self, related_name, self.through, to=to) Field.__init__( self, verbose_name=verbose_name, help_text=help_text, blank=blank, null=True, serialize=False, rel=rel, ) # NOTE: `to` is ignored, only used via `deconstruct`. 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/alex/django-taggit/issues/206#issuecomment-37578676 if isinstance(self.rel.through, six.string_types): kwargs['through'] = self.rel.through elif not self.rel.through._meta.auto_created: kwargs['through'] = "%s.%s" % (self.rel.through._meta.app_label, self.rel.through._meta.object_name) # rel.to renamed to remote_field.model in Django 1.9 if VERSION >= (1, 9): if isinstance(self.remote_field.model, six.string_types): kwargs['to'] = self.remote_field.model else: kwargs['to'] = '%s.%s' % (self.remote_field.model._meta.app_label, self.remote_field.model._meta.object_name) else: if isinstance(self.rel.to, six.string_types): kwargs['to'] = self.rel.to else: kwargs['to'] = '%s.%s' % (self.rel.to._meta.app_label, self.rel.to._meta.object_name) return name, path, args, kwargs def contribute_to_class(self, cls, name): if VERSION < (1, 7): self.name = self.column = self.attname = name else: 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: # rel.to renamed to remote_field.model in Django 1.9 if VERSION >= (1, 9): if isinstance(self.remote_field.model, six.string_types): def resolve_related_class(field, model, cls): field.remote_field.model = model add_lazy_relation(cls, self, self.remote_field.model, resolve_related_class) else: if isinstance(self.rel.to, six.string_types): def resolve_related_class(field, model, cls): field.rel.to = model add_lazy_relation(cls, self, self.rel.to, resolve_related_class) if isinstance(self.through, six.string_types): def resolve_related_class(field, model, cls): self.through = model self.rel.through = model self.post_through_setup(cls) add_lazy_relation( cls, self, self.through, resolve_related_class ) else: self.post_through_setup(cls) def get_internal_type(self): return 'ManyToManyField' def __lt__(self, other): """ Required contribute_to_class as Django uses bisect for ordered class contribution and bisect requires a orderable type in py3. """ return False def post_through_setup(self, cls): if RelatedObject is not None: # Django < 1.8 self.related = RelatedObject(cls, self.model, self) self.use_gfk = ( self.through is None or issubclass(self.through, CommonGenericTaggedItemBase) ) # rel.to renamed to remote_field.model in Django 1.9 if VERSION >= (1, 9): if not self.remote_field.model: self.remote_field.model = self.through._meta.get_field("tag").remote_field.model else: if not self.rel.to: self.rel.to = self.through._meta.get_field("tag").rel.to if RelatedObject is not None: # Django < 1.8 self.related = RelatedObject(self.through, cls, self) 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 _model_name(self.model) def m2m_reverse_name(self): return _get_field(self.through, 'tag').column def m2m_reverse_field_name(self): return _get_field(self.through, 'tag').name def m2m_target_field_name(self): return self.model._meta.pk.name def m2m_reverse_target_field_name(self): # rel.to renamed to remote_field.model in Django 1.9 if VERSION >= (1, 9): return self.remote_field.model._meta.pk.name else: return self.rel.to._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 db_type(self, connection=None): return None def m2m_db_table(self): return self.through._meta.db_table def bulk_related_objects(self, new_objs, using): return [] def extra_filters(self, pieces, pos, negate): if negate or not self.use_gfk: return [] prefix = "__".join(["tagged_items"] + pieces[:pos - 2]) get = ContentType.objects.get_for_model cts = [get(obj) for obj in _get_subclasses(self.model)] if len(cts) == 1: return [("%s__content_type" % prefix, cts[0])] return [("%s__content_type__in" % prefix, cts)] def get_extra_join_sql(self, connection, qn, lhs_alias, rhs_alias): model_name = _model_name(self.through) if rhs_alias == '%s_%s' % (self.through._meta.app_label, model_name): alias_to_join = rhs_alias else: alias_to_join = lhs_alias extra_col = _get_field(self.through, 'content_type').column content_type_ids = [ContentType.objects.get_for_model(subclass).pk for subclass in _get_subclasses(self.model)] if len(content_type_ids) == 1: content_type_id = content_type_ids[0] extra_where = " AND %s.%s = %%s" % (qn(alias_to_join), qn(extra_col)) params = [content_type_id] else: extra_where = " AND %s.%s IN (%s)" % (qn(alias_to_join), qn(extra_col), ','.join(['%s'] * len(content_type_ids))) params = content_type_ids return extra_where, params # This and all the methods till the end of class are only used in django >= 1.6 def _get_mm_case_path_info(self, direct=False): pathinfos = [] linkfield1 = _get_field(self.through, 'content_object') linkfield2 = _get_field(self.through, self.m2m_reverse_field_name()) if direct: join1infos = linkfield1.get_reverse_path_info() join2infos = linkfield2.get_path_info() else: join1infos = linkfield2.get_reverse_path_info() join2infos = linkfield1.get_path_info() pathinfos.extend(join1infos) pathinfos.extend(join2infos) return pathinfos def _get_gfk_case_path_info(self, direct=False): pathinfos = [] from_field = self.model._meta.pk opts = self.through._meta linkfield = _get_field(self.through, self.m2m_reverse_field_name()) if direct: join1infos = [PathInfo(self.model._meta, opts, [from_field], self.rel, True, False)] join2infos = linkfield.get_path_info() else: join1infos = linkfield.get_reverse_path_info() join2infos = [PathInfo(opts, self.model._meta, [from_field], self, True, False)] pathinfos.extend(join1infos) pathinfos.extend(join2infos) return pathinfos def get_path_info(self): if self.use_gfk: return self._get_gfk_case_path_info(direct=True) else: return self._get_mm_case_path_info(direct=True) def get_reverse_path_info(self): if self.use_gfk: return self._get_gfk_case_path_info(direct=False) else: return self._get_mm_case_path_info(direct=False) 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 = _get_field(self.through, '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 [(_get_field(self.through, 'object_id'), self.model._meta.pk)] @property def foreign_related_fields(self): return [self.related_fields[0][1]] def _get_subclasses(model): subclasses = [model] if VERSION < (1, 8): all_fields = (_get_field(model, f) for f in model._meta.get_all_field_names()) else: all_fields = model._meta.get_fields() for field in all_fields: # Django 1.8 + if (not RelatedObject and isinstance(field, OneToOneRel) and getattr(field.field.rel, "parent_link", None)): subclasses.extend(_get_subclasses(field.related_model)) # < Django 1.8 if (RelatedObject and isinstance(field, RelatedObject) and getattr(field.field.rel, "parent_link", None)): subclasses.extend(_get_subclasses(field.model)) return subclasses # `total_ordering` does not exist in Django 1.4, as such # we special case this import to be py3k specific which # is not supported by Django 1.4 if six.PY3: from django.utils.functional import total_ordering TaggableManager = total_ordering(TaggableManager) django-taggit-0.18.0/taggit/migrations/0000755000076500000240000000000012647304630020371 5ustar coreyfstaff00000000000000django-taggit-0.18.0/taggit/migrations/0001_initial.py0000644000076500000240000000325612625364503023043 0ustar coreyfstaff00000000000000# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.db import models, migrations 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.18.0/taggit/migrations/0002_auto_20150616_2121.py0000644000076500000240000000057712625364503024017 0ustar coreyfstaff00000000000000# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.db import models, migrations class Migration(migrations.Migration): dependencies = [ ('taggit', '0001_initial'), ] operations = [ migrations.AlterIndexTogether( name='taggeditem', index_together=set([('content_type', 'object_id')]), ), ] django-taggit-0.18.0/taggit/migrations/__init__.py0000644000076500000240000000112212625364503022477 0ustar coreyfstaff00000000000000""" Django migrations for taggit app This package does not contain South migrations. South migrations can be found in the ``south_migrations`` package. """ SOUTH_ERROR_MESSAGE = """\n For South support, customize the SOUTH_MIGRATION_MODULES setting like so: SOUTH_MIGRATION_MODULES = { 'taggit': 'taggit.south_migrations', } """ # Ensure the user is not using Django 1.6 or below with South try: from django.db import migrations # noqa except ImportError: from django.core.exceptions import ImproperlyConfigured raise ImproperlyConfigured(SOUTH_ERROR_MESSAGE) django-taggit-0.18.0/taggit/models.py0000644000076500000240000001576112647304553020070 0ustar coreyfstaff00000000000000from __future__ import unicode_literals import django from django import VERSION from django.contrib.contenttypes.models import ContentType from django.db import IntegrityError, models, transaction from django.db.models.query import QuerySet from django.template.defaultfilters import slugify as default_slugify from django.utils.encoding import python_2_unicode_compatible from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext from taggit.utils import _get_field try: from unidecode import unidecode except ImportError: def unidecode(tag): return tag try: from django.contrib.contenttypes.fields import GenericForeignKey except ImportError: # django < 1.7 from django.contrib.contenttypes.generic import GenericForeignKey try: atomic = transaction.atomic except AttributeError: from contextlib import contextmanager @contextmanager def atomic(using=None): sid = transaction.savepoint(using=using) try: yield except IntegrityError: transaction.savepoint_rollback(sid, using=using) raise else: transaction.savepoint_commit(sid, using=using) @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 class Meta: abstract = True def save(self, *args, **kwargs): if not self.pk and not self.slug: self.slug = self.slugify(self.name) from django.db import router 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 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 = default_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") @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 = _get_field(cls, 'tag') return field.remote_field.model if VERSION >= (1, 9) else field.rel.to @classmethod def tag_relname(cls): field = _get_field(cls, 'tag') return field.remote_field.related_name if VERSION >= (1, 9) else field.rel.related_name @classmethod def lookup_kwargs(cls, instance): return { 'content_object': instance } @classmethod def bulk_lookup_kwargs(cls, instances): return { "content_object__in": instances, } 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 bulk_lookup_kwargs(cls, instances): if isinstance(instances, QuerySet): # Can do a real object_id IN (SELECT ..) query. return { "object_id__in": instances, "content_type": ContentType.objects.get_for_model(instances.model), } else: # TODO: instances[0], can we assume there are instances. return { "object_id__in": [instance.pk for instance in instances], "content_type": ContentType.objects.get_for_model(instances[0]), } @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 if VERSION >= (1, 8): 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") if django.VERSION >= (1, 5): index_together = [ ["content_type", "object_id"], ] django-taggit-0.18.0/taggit/south_migrations/0000755000076500000240000000000012647304630021613 5ustar coreyfstaff00000000000000django-taggit-0.18.0/taggit/south_migrations/0001_initial.py0000644000076500000240000000562612625364503024270 0ustar coreyfstaff00000000000000# -*- coding: utf-8 -*- import datetime from south.db import db from south.v2 import SchemaMigration from django.db import models class Migration(SchemaMigration): def forwards(self, orm): # Adding model 'Tag' db.create_table('taggit_tag', ( ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), ('name', self.gf('django.db.models.fields.CharField')(max_length=100)), ('slug', self.gf('django.db.models.fields.SlugField')(unique=True, max_length=100)), )) db.send_create_signal('taggit', ['Tag']) # Adding model 'TaggedItem' db.create_table('taggit_taggeditem', ( ('id', self.gf('django.db.models.fields.AutoField')(primary_key=True)), ('tag', self.gf('django.db.models.fields.related.ForeignKey')(related_name='taggit_taggeditem_items', to=orm['taggit.Tag'])), ('object_id', self.gf('django.db.models.fields.IntegerField')(db_index=True)), ('content_type', self.gf('django.db.models.fields.related.ForeignKey')(related_name='taggit_taggeditem_tagged_items', to=orm['contenttypes.ContentType'])), )) db.send_create_signal('taggit', ['TaggedItem']) def backwards(self, orm): # Deleting model 'Tag' db.delete_table('taggit_tag') # Deleting model 'TaggedItem' db.delete_table('taggit_taggeditem') models = { 'contenttypes.contenttype': { 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) }, 'taggit.tag': { 'Meta': {'object_name': 'Tag'}, 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}), 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '100'}) }, 'taggit.taggeditem': { 'Meta': {'object_name': 'TaggedItem'}, 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'taggit_taggeditem_tagged_items'", 'to': "orm['contenttypes.ContentType']"}), 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 'object_id': ('django.db.models.fields.IntegerField', [], {'db_index': 'True'}), 'tag': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'taggit_taggeditem_items'", 'to': "orm['taggit.Tag']"}) } } complete_apps = ['taggit'] django-taggit-0.18.0/taggit/south_migrations/0002_unique_tagnames.py0000644000076500000240000000375312625364503026024 0ustar coreyfstaff00000000000000# -*- coding: utf-8 -*- import datetime from south.db import db from south.v2 import SchemaMigration from django.db import models class Migration(SchemaMigration): def forwards(self, orm): # Adding unique constraint on 'Tag', fields ['name'] db.create_unique('taggit_tag', ['name']) def backwards(self, orm): # Removing unique constraint on 'Tag', fields ['name'] db.delete_unique('taggit_tag', ['name']) models = { 'contenttypes.contenttype': { 'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"}, 'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}), 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}), 'name': ('django.db.models.fields.CharField', [], {'max_length': '100'}) }, 'taggit.tag': { 'Meta': {'object_name': 'Tag'}, 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '100'}), 'slug': ('django.db.models.fields.SlugField', [], {'unique': 'True', 'max_length': '100'}) }, 'taggit.taggeditem': { 'Meta': {'object_name': 'TaggedItem'}, 'content_type': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'taggit_taggeditem_tagged_items'", 'to': "orm['contenttypes.ContentType']"}), 'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}), 'object_id': ('django.db.models.fields.IntegerField', [], {'db_index': 'True'}), 'tag': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'taggit_taggeditem_items'", 'to': "orm['taggit.Tag']"}) } } complete_apps = ['taggit'] django-taggit-0.18.0/taggit/south_migrations/__init__.py0000644000076500000240000000000012625364503023713 0ustar coreyfstaff00000000000000django-taggit-0.18.0/taggit/utils.py0000644000076500000240000001172512647304553017741 0ustar coreyfstaff00000000000000from __future__ import unicode_literals from importlib import import_module from django import VERSION from django.conf import settings from django.utils import six from django.utils.encoding import force_text from django.utils.functional import wraps def _get_field(model, name): if VERSION < (1, 8): return model._meta.get_field_by_name(name)[0] else: return model._meta.get_field(name) 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 = six.next(i) if c == '"': if buffer: to_be_split.append(''.join(buffer)) buffer = [] # Find the matching quote open_quote = True c = six.next(i) while c != '"': buffer.append(c) c = six.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, default) try: return get_func.cache[func_path] except KeyError: mod_path, func_name = func_path.rsplit('.', 1) func = getattr(import_module(mod_path), func_name) get_func.cache[func_path] = func return func # Create a cache as an attribute on the function that way it can cache the # imported callable rather than re-importing it each time `parse_tags` or # `edit_string_for_tags` needs the callable. get_func.cache = {} def parse_tags(tagstring): func = get_func('TAGGIT_TAGS_FROM_STRING', 'taggit.utils._parse_tags') return func(tagstring) def edit_string_for_tags(tags): func = get_func('TAGGIT_STRING_FROM_TAGS', 'taggit.utils._edit_string_for_tags') return func(tags) django-taggit-0.18.0/taggit/views.py0000644000076500000240000000135412625364503017730 0ustar coreyfstaff00000000000000from __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() tag = get_object_or_404(Tag, slug=slug) qs = queryset.filter(pk__in=TaggedItem.objects.filter( tag=tag, content_type=ContentType.objects.get_for_model(queryset.model) ).values_list("object_id", flat=True)) if "extra_context" not in kwargs: kwargs["extra_context"] = {} kwargs["extra_context"]["tag"] = tag return ListView.as_view(request, qs, **kwargs) django-taggit-0.18.0/tests/0000755000076500000240000000000012647304630016100 5ustar coreyfstaff00000000000000django-taggit-0.18.0/tests/__init__.py0000644000076500000240000000000012625364503020200 0ustar coreyfstaff00000000000000django-taggit-0.18.0/tests/custom_parser.py0000644000076500000240000000024712647304553021347 0ustar coreyfstaff00000000000000def 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.18.0/tests/forms.py0000644000076500000240000000145112625365035017603 0ustar coreyfstaff00000000000000from __future__ import absolute_import, unicode_literals from django import forms, VERSION from .models import (CustomPKFood, DirectCustomPKFood, DirectFood, Food, OfficialFood) fields = None if VERSION >= (1, 6): fields = '__all__' class FoodForm(forms.ModelForm): class Meta: model = Food fields = fields class DirectFoodForm(forms.ModelForm): class Meta: model = DirectFood fields = fields class DirectCustomPKFoodForm(forms.ModelForm): class Meta: model = DirectCustomPKFood fields = fields class CustomPKFoodForm(forms.ModelForm): class Meta: model = CustomPKFood fields = fields class OfficialFoodForm(forms.ModelForm): class Meta: model = OfficialFood fields = fields django-taggit-0.18.0/tests/migrations/0000755000076500000240000000000012647304630020254 5ustar coreyfstaff00000000000000django-taggit-0.18.0/tests/migrations/0001_initial.py0000644000076500000240000004775012625365035022736 0ustar coreyfstaff00000000000000# -*- coding: utf-8 -*- from __future__ import unicode_literals from django.db import models, migrations 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='')), ('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='')), ('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='')), ('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='')), ('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='')), ('tag', models.ForeignKey(related_name='tagged_items', to='tests.OfficialTag', help_text='')), ], 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='')), ], 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='')), ('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='')), ], 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')), ('tag', models.ForeignKey(related_name='tests_taggedcustompkfood_items', to='taggit.Tag', help_text='')), ], 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')), ('tag', models.ForeignKey(related_name='tests_taggedcustompkpet_items', to='taggit.Tag', help_text='')), ], 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')), ('tag', models.ForeignKey(related_name='tests_taggedfood_items', to='taggit.Tag', help_text='')), ], 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')), ('tag', models.ForeignKey(related_name='tests_taggedpet_items', to='taggit.Tag', help_text='')), ], 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')), ('tag', models.ForeignKey(related_name='tests_through1_items', to='taggit.Tag', help_text='')), ], 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')), ('tag', models.ForeignKey(related_name='tests_through2_items', to='taggit.Tag', help_text='')), ], 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='')), ('tag', models.ForeignKey(related_name='tagged_items', to='taggit.Tag', help_text='')), ], 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.18.0/tests/migrations/__init__.py0000644000076500000240000000000012625364503022354 0ustar coreyfstaff00000000000000django-taggit-0.18.0/tests/models.py0000644000076500000240000001261312625365035017742 0ustar coreyfstaff00000000000000from __future__ import unicode_literals 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, Tag, TagBase, TaggedItem, TaggedItemBase) # Ensure that two TaggableManagers with custom through model are allowed. class Through1(TaggedItemBase): content_object = models.ForeignKey('MultipleTags') class Through2(TaggedItemBase): content_object = models.ForeignKey('MultipleTags') 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') 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') class TaggedPet(TaggedItemBase): content_object = models.ForeignKey('DirectPet') @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') class TaggedCustomPKPet(TaggedItemBase): content_object = models.ForeignKey('DirectCustomPKPet') @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") @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 django-taggit-0.18.0/tests/tests.py0000644000076500000240000006550612647304553017634 0ustar coreyfstaff00000000000000from __future__ import absolute_import, unicode_literals from unittest import TestCase as UnitTestCase import django from django.contrib.contenttypes.models import ContentType from django.core import serializers from django.core.exceptions import ImproperlyConfigured, ValidationError from django.core.management import call_command from django.db import connection, models from django.test import TestCase, TransactionTestCase from django.test.utils import override_settings from django.utils import six 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) from taggit.managers import _model_name, _TaggableManager, TaggableManager from taggit.models import Tag, TaggedItem from taggit.utils import edit_string_for_tags, parse_tags try: from unittest import skipIf, skipUnless except ImportError: from django.utils.unittest import skipIf, skipUnless class BaseTaggingTest(object): 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) def _get_form_str(self, form_str): if django.VERSION >= (1, 3): form_str %= { "help_start": '', "help_stop": "" } else: form_str %= { "help_start": "", "help_stop": "" } return form_str def assert_form_renders(self, form, html): self.assertHTMLEqual(str(form), self._get_form_str(html)) class BaseTaggingTestCase(TestCase, BaseTaggingTest): pass class BaseTaggingTransactionTestCase(TransactionTestCase, BaseTaggingTest): pass class TagModelTestCase(BaseTaggingTransactionTestCase): 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.assertRaisesRegexp(ValueError, ( r"Cannot add 1 \(<(type|class) 'int'>\). " r"Expected or str.")): apple.tags.add(1) 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 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 ) 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"]) 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 query to see which tags exist # + 3 queries to create the tags. # + 6 queries to create the intermediary things (including SELECTs, to # make sure we don't double create. # + 12 on Django 1.6 for save points. queries = 22 if django.VERSION < (1, 6): queries -= 12 self.assertNumQueries(queries, apple.tags.add, "red", "delicious", "green") pear = self.food_model.objects.create(name="pear") # 1 query to see which tags exist # + 4 queries to create the intermeidary things (including SELECTs, to # make sure we dont't double create. # + 4 on Django 1.6 for save points. queries = 9 if django.VERSION < (1, 6): queries -= 4 self.assertNumQueries(queries, pear.tags.add, "green", "delicious") self.assertNumQueries(0, pear.tags.add) def test_require_pk(self): food_instance = self.food_model() self.assertRaises(ValueError, lambda: 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_lookup_bulk(self): apple = self.food_model.objects.create(name="apple") pear = self.food_model.objects.create(name="pear") apple.tags.add('fruit', 'green') pear.tags.add('fruit', 'yummie') def lookup_qs(): # New fix: directly allow WHERE object_id IN (SELECT id FROM ..) objects = self.food_model.objects.all() lookup = self.taggeditem_model.bulk_lookup_kwargs(objects) list(self.taggeditem_model.objects.filter(**lookup)) def lookup_list(): # Simulate old situation: iterate over a list. objects = list(self.food_model.objects.all()) lookup = self.taggeditem_model.bulk_lookup_kwargs(objects) list(self.taggeditem_model.objects.filter(**lookup)) self.assertNumQueries(1, lookup_qs) self.assertNumQueries(2, lookup_list) 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' % _model_name(self.pet_model): '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, 'rel')) self.assertTrue(hasattr(field.rel, 'to')) self.assertTrue(hasattr(field, 'related')) # This API has changed in Django 1.8 # https://code.djangoproject.com/ticket/21414 if django.VERSION >= (1, 8): self.assertEqual(self.food_model, field.model) self.assertEqual(self.tag_model, field.related.model) else: self.assertEqual(self.food_model, field.related.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): l = list(self.food_model.objects.prefetch_related('tags').all()) with self.assertNumQueries(0): foods = dict((f.name, set(t.name for t in f.tags.all())) for f in l) self.assertEqual(foods, { 'orange': set(['2', '4']), 'apple': set(['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]) 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): # TODO 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): # TODO 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) 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 test_form(self): self.assertEqual(list(self.form_class.base_fields), ['name', 'tags']) f = self.form_class({'name': 'apple', 'tags': 'green, red, yummy'}) self.assert_form_renders(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.assert_form_renders(f, """
%(help_start)sA comma-separated list of tags.%(help_stop)s""") apple.tags.add('has,comma') f = self.form_class(instance=apple) self.assert_form_renders(f, """
%(help_start)sA comma-separated list of tags.%(help_stop)s""") apple.tags.add('has space') f = self.form_class(instance=apple) self.assert_form_renders(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(UnitTestCase): """ 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.objects.create(name='plain') spaces = Tag.objects.create(name='spa ces') comma = Tag.objects.create(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.objects.create(name='Cued Speech') b = Tag.objects.create(name='transliterator') self.assertEqual(edit_string_for_tags([a, b]), 'Cued Speech, transliterator') @skipIf(django.VERSION < (1, 7), "not relevant for Django < 1.7") class DeconstructTestCase(UnitTestCase): 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.rel.through) self.assertEqual('dummy.To', new_instance.rel.to) @skipUnless(django.VERSION < (1, 7), "test only applies to 1.6 and below") class SouthSupportTests(TestCase): def test_import_migrations_module(self): try: from taggit.migrations import __doc__ # noqa except ImproperlyConfigured as e: exception = e self.assertIn("SOUTH_MIGRATION_MODULES", exception.args[0]) class InheritedPrefetchTests(TestCase): def test_inherited_tags_with_prefetch(self): child = Child() child.save() child.tags.add('tag 1', 'tag 2', 'tag 3', 'tag 4') child = Child.objects.get() no_prefetch_tags = child.tags.all() self.assertEquals(4, no_prefetch_tags.count()) child = Child.objects.prefetch_related('tags').get() prefetch_tags = child.tags.all() self.assertEquals(4, prefetch_tags.count()) self.assertEquals(set([t.name for t in no_prefetch_tags]), set([t.name for t in prefetch_tags])) class DjangoCheckTests(UnitTestCase): def test_django_checks(self): if django.VERSION >= (1, 6): call_command('check', tag=['models']) else: call_command('validate') django-taggit-0.18.0/tox.ini0000644000076500000240000000357512647304553016267 0ustar coreyfstaff00000000000000[testenv] skipsdist = True usedevelop = True deps = flake8 deps14 = https://github.com/django/django/archive/stable/1.4.x.tar.gz#egg=django deps15 = https://github.com/django/django/archive/stable/1.5.x.tar.gz#egg=django deps16 = https://github.com/django/django/archive/stable/1.6.x.tar.gz#egg=django deps17 = https://github.com/django/django/archive/stable/1.7.x.tar.gz#egg=django deps18 = https://github.com/django/django/archive/stable/1.8.x.tar.gz#egg=django deps19 = https://github.com/django/django/archive/stable/1.9.x.tar.gz#egg=django commands = python ./runtests.py {posargs} [testenv:py27-1.4.x] basepython = python2.7 deps = {[testenv]deps} {[testenv]deps14} [testenv:py27-1.5.x] basepython = python2.7 deps = {[testenv]deps} {[testenv]deps15} [testenv:py27-1.6.x] basepython = python2.7 deps = {[testenv]deps} {[testenv]deps16} [testenv:py27-1.7.x] basepython = python2.7 deps = {[testenv]deps} {[testenv]deps17} [testenv:py27-1.8.x] basepython = python2.7 deps = {[testenv]deps} {[testenv]deps18} [testenv:py27-1.9.x] basepython = python2.7 deps = {[testenv]deps} {[testenv]deps19} [testenv:py33-1.5.x] basepython = python3.3 deps = {[testenv]deps} {[testenv]deps15} [testenv:py33-1.6.x] basepython = python3.3 deps = {[testenv]deps} {[testenv]deps16} [testenv:py33-1.7.x] basepython = python3.3 deps = {[testenv]deps} {[testenv]deps17} [testenv:py33-1.8.x] basepython = python3.3 deps = {[testenv]deps} {[testenv]deps18} [testenv:py34-1.5.x] basepython = python3.4 deps = {[testenv]deps} {[testenv]deps15} [testenv:py34-1.6.x] basepython = python3.4 deps = {[testenv]deps} {[testenv]deps16} [testenv:py34-1.7.x] basepython = python3.4 deps = {[testenv]deps} {[testenv]deps17} [testenv:py34-1.8.x] basepython = python3.4 deps = {[testenv]deps} {[testenv]deps18} [testenv:py34-1.9.x] basepython = python3.4 deps = {[testenv]deps} {[testenv]deps19}