[bug 972522] Upgrade to django-taggit v0.11.2.

* Also, converted the dependency to a git submodule.
* Includes migration to add taggit to south.
This commit is contained in:
Ricky Rosario 2014-02-21 11:28:41 -05:00
Родитель b5027306b5
Коммит 2cfb59c42b
35 изменённых файлов: 11 добавлений и 2097 удалений

3
.gitmodules поставляемый
Просмотреть файл

@ -172,3 +172,6 @@
[submodule "vendor/src/django-axes"]
path = vendor/src/django-axes
url = https://github.com/django-security/django-axes.git
[submodule "vendor/src/django-taggit"]
path = vendor/src/django-taggit
url = https://github.com/alex/django-taggit.git

Просмотреть файл

@ -6,6 +6,10 @@ from django.db import models
class Migration(DataMigration):
depends_on = (
('taggit', "0001_initial"),
)
def forwards(self, orm):
"""Unescalate questions over a week old."""
# Get the escalate tag.

Просмотреть файл

@ -0,0 +1,2 @@
INSERT INTO `south_migrationhistory` (app_name, migration, applied) VALUES
('taggit','0001_initial','2014-02-21 11:38:42');

2
vendor/kitsune.pth поставляемый
Просмотреть файл

@ -10,7 +10,6 @@ packages/pylint
packages/ipython
packages/pyflakes
packages/amqp
packages/django-taggit
packages/anyjson
packages/carrot
packages/pyquery
@ -98,3 +97,4 @@ src/billiard
src/django-statici18n/src
src/django-appconf
src/django-axes
src/django-taggit

14
vendor/packages/django-taggit/AUTHORS.txt поставляемый
Просмотреть файл

@ -1,14 +0,0 @@
django-taggit was originally created by Alex Gaynor.
The following is a list of much appreciated contributors:
Nathan Borror <nathan@playgroundblues.com>
fakeempire <adam@fakeempire.com>
Ben Firshman <ben@firshman.co.uk>
Alex Gaynor <alex.gaynor@gmail.com>
Rob Hudson <rob@cogit8.org>
Carl Meyer <carl@oddbird.net>
Frank Wiles
Jonathan Buchanan
idle sign <idlesign@yandex.ru>
Charles Leifer

28
vendor/packages/django-taggit/LICENSE.txt поставляемый
Просмотреть файл

@ -1,28 +0,0 @@
Copyright (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.

2
vendor/packages/django-taggit/MANIFEST.in поставляемый
Просмотреть файл

@ -1,2 +0,0 @@
recursive-include docs *.txt
recursive-include taggit/locale *

52
vendor/packages/django-taggit/PKG-INFO поставляемый
Просмотреть файл

@ -1,52 +0,0 @@
Metadata-Version: 1.0
Name: django-taggit
Version: 0.9.2
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: UNKNOWN
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::
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::
>>> apple = Food.objects.create(name="apple")
>>> apple.tags.add("red", "green", "delicious")
>>> apple.tags.all()
[<Tag: red>, <Tag: green>, <Tag: delicious>]
>>> apple.tags.remove("green")
>>> apple.tags.all()
[<Tag: red>, <Tag: delicious>]
>>> Food.objects.filter(tags__name__in=["red"])
[<Food: apple>, <Food: cherry>]
Tags will show up for you automatically in forms and the admin.
``django-taggit`` requires Django 1.1 or greater.
For more info checkout out the documentation. And for questions about usage or
development you can contact the
`mailinglist <http://groups.google.com/group/django-taggit>`_.
Platform: UNKNOWN
Classifier: Development Status :: 4 - Beta
Classifier: Environment :: Web Environment
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: BSD License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python
Classifier: Framework :: Django

35
vendor/packages/django-taggit/README.txt поставляемый
Просмотреть файл

@ -1,35 +0,0 @@
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::
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::
>>> apple = Food.objects.create(name="apple")
>>> apple.tags.add("red", "green", "delicious")
>>> apple.tags.all()
[<Tag: red>, <Tag: green>, <Tag: delicious>]
>>> apple.tags.remove("green")
>>> apple.tags.all()
[<Tag: red>, <Tag: delicious>]
>>> Food.objects.filter(tags__name__in=["red"])
[<Food: apple>, <Food: cherry>]
Tags will show up for you automatically in forms and the admin.
``django-taggit`` requires Django 1.1 or greater.
For more info checkout out the documentation. And for questions about usage or
development you can contact the
`mailinglist <http://groups.google.com/group/django-taggit>`_.

89
vendor/packages/django-taggit/docs/Makefile поставляемый
Просмотреть файл

@ -1,89 +0,0 @@
# 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 <target>' where <target> 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."

14
vendor/packages/django-taggit/docs/admin.txt поставляемый
Просмотреть файл

@ -1,14 +0,0 @@
Using 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 <http://docs.djangoproject.com/en/1.2/ref/contrib/admin/#django.contrib.admin.ModelAdmin.list_display>`_.

88
vendor/packages/django-taggit/docs/api.txt поставляемый
Просмотреть файл

@ -1,88 +0,0 @@
The 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.
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"])
[<Food: apple>, <Food: pear>, <Food: plum>]
If you're filtering on multiple tags, it's very common to get duplicate
results, because of the way relational databases work. Often you'll want to
make use of the ``distinct()`` method on ``QuerySets``::
>>> Food.objects.filter(tags__name__in=["delicious", "red"])
[<Food: apple>, <Food: apple>]
>>> Food.objects.filter(tags__name__in=["delicious", "red"]).distinct()
[<Food: apple>]
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 <http://code.djangoproject.com/ticket/10870>`_, it is not
currently possible to use aggregation in conjunction with ``taggit``. This is
a `documented interaction <http://docs.djangoproject.com/en/dev/ref/contrib/contenttypes/#generic-relations-and-aggregation>`_
of generic relations (which ``taggit`` uses internally) and aggregates.

Просмотреть файл

@ -1,44 +0,0 @@
Changelog
=========
0.9.2
~~~~~
* *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
~~~~~
* Added a Hebrew locale.
* Added an index on the ``object_id`` field of ``TaggedItem``.
* When displaying tags always join them with commas, never spaces.
* The docs are now available `online <http://django-taggit.readthedocs.org/>`_.
* 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
~~~~~
* Fixed querying for objects using ``exclude(tags__in=tags)``.
* Marked strings as translatable.
* Added a Russian translation.
* Created a `mailing list <http://groups.google.com/group/django-taggit>`_.
* Smarter tagstring parsing for form field; ported from Jonathan
Buchanan's `django-tagging
<http://django-tagging.googlecode.com>`_. Now supports tags
containing commas. See :ref:`tags-in-forms` for details.
* Switched to using savepoints around the slug generation for tags. This
ensures that it works fine on databases (such as Postgres) which dirty a
transaction with an ``IntegrityError``.
* Added Python 2.4 compatibility.
* Added Django 1.1 compatibility.

198
vendor/packages/django-taggit/docs/conf.py поставляемый
Просмотреть файл

@ -1,198 +0,0 @@
# -*- 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, Alex Gaynor'
# 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.9.2'
# The full version, including alpha/beta/rc tags.
release = '0.9.2'
# 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
# "<project> v<release> 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 <link> 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}

Просмотреть файл

@ -1,58 +0,0 @@
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.
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.
To change the behavior in other ways there are a number of other classes you
can subclass to obtain different behavior:
========================= ===========================================================
Class name Behavior
========================= ===========================================================
``TaggedItemBase`` Allows custom ``ForeignKeys`` to models.
``GenericTaggedItemBase`` Allows custom ``Tag`` models.
``ItemBase`` Allows custom ``Tag`` models and ``ForeignKeys`` to models.
========================= ===========================================================
When providing a custom ``Tag`` model it should be a ``ForeignKey`` to your tag model named ``"tag"``.
.. 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.

Просмотреть файл

@ -1,26 +0,0 @@
External 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`__.
__ http://github.com/alex/django-taggit
__ http://github.com/frankwiles/django-taggit-suggest
__ http://github.com/feuervogel/django-taggit-templatetags

51
vendor/packages/django-taggit/docs/forms.txt поставляемый
Просмотреть файл

@ -1,51 +0,0 @@
.. _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()

Просмотреть файл

@ -1,22 +0,0 @@
Getting 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.
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()

27
vendor/packages/django-taggit/docs/index.txt поставляемый
Просмотреть файл

@ -1,27 +0,0 @@
Welcome 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.1 and 1.2 (see :doc:`issues` for known
issues with older versions of Django), and Python 2.4-2.X.
.. toctree::
:maxdepth: 2
getting_started
forms
admin
api
custom_tagging
issues
external_apps
changelog
Indices and tables
==================
* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`

Просмотреть файл

@ -1,9 +0,0 @@
Known Issues
============
Currently there is 1 known issue:
* When run under Django 1.1, doing ``Model.objects.all().delete()`` (or any
bulk deletion operation) on a model with a ``TaggableManager`` will result
in losing the tags for items beyond just those assosciated with the deleted
objects. This issue is not present in Django 1.2.

37
vendor/packages/django-taggit/setup.py поставляемый
Просмотреть файл

@ -1,37 +0,0 @@
import os
from setuptools import setup, find_packages
from taggit import VERSION
f = open(os.path.join(os.path.dirname(__file__), 'README.txt'))
readme = f.read()
f.close()
setup(
name='django-taggit',
version=".".join(map(str, 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(),
zip_safe=False,
package_data = {
'taggit': [
'locale/*/LC_MESSAGES/*',
],
},
classifiers=[
'Development Status :: 4 - Beta',
'Environment :: Web Environment',
'Intended Audience :: Developers',
'License :: OSI Approved :: BSD License',
'Operating System :: OS Independent',
'Programming Language :: Python',
'Framework :: Django',
],
test_suite='taggit.tests.runtests.runtests'
)

Просмотреть файл

@ -1 +0,0 @@
VERSION = (0, 9, 2)

16
vendor/packages/django-taggit/taggit/admin.py поставляемый
Просмотреть файл

@ -1,16 +0,0 @@
from django.contrib import admin
from taggit.models import Tag, TaggedItem
class TaggedItemInline(admin.StackedInline):
model = TaggedItem
class TagAdmin(admin.ModelAdmin):
list_display = ["name"]
inlines = [
TaggedItemInline
]
admin.site.register(Tag, TagAdmin)

21
vendor/packages/django-taggit/taggit/forms.py поставляемый
Просмотреть файл

@ -1,21 +0,0 @@
from django import forms
from django.utils.translation import ugettext as _
from taggit.utils import parse_tags, edit_string_for_tags
class TagWidget(forms.TextInput):
def render(self, name, value, attrs=None):
if value is not None and not isinstance(value, basestring):
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."))

Просмотреть файл

@ -1,244 +0,0 @@
from django.contrib.contenttypes.generic import GenericRelation
from django.contrib.contenttypes.models import ContentType
from django.db import models
from django.db.models.fields.related import ManyToManyRel, RelatedField, add_lazy_relation
from django.db.models.related import RelatedObject
from django.utils.text import capfirst
from django.utils.translation import ugettext_lazy as _
from taggit.forms import TagField
from taggit.models import TaggedItem, GenericTaggedItemBase
from taggit.utils import require_instance_manager
try:
all
except NameError:
# 2.4 compat
try:
from django.utils.itercompat import all
except ImportError:
# 1.1.X compat
def all(iterable):
for item in iterable:
if not item:
return False
return True
class TaggableRel(ManyToManyRel):
def __init__(self):
self.related_name = None
self.limit_choices_to = {}
self.symmetrical = True
self.multiple = True
self.through = None
class TaggableManager(RelatedField):
def __init__(self, verbose_name=_("Tags"),
help_text=_("A comma-separated list of tags."), through=None, blank=False):
self.through = through or TaggedItem
self.rel = TaggableRel()
self.verbose_name = verbose_name
self.help_text = help_text
self.blank = blank
self.editable = True
self.unique = False
self.creates_table = False
self.db_column = None
self.choices = None
self.serialize = False
self.null = True
self.creation_counter = models.Field.creation_counter
models.Field.creation_counter += 1
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 = _TaggableManager(
through=self.through, model=model, instance=instance
)
return manager
def contribute_to_class(self, cls, name):
self.name = self.column = name
self.model = cls
cls._meta.add_field(self)
setattr(cls, name, self)
if not cls._meta.abstract:
if isinstance(self.through, basestring):
def resolve_related_class(field, model, cls):
self.through = model
self.post_through_setup(cls)
add_lazy_relation(
cls, self, self.through, resolve_related_class
)
else:
self.post_through_setup(cls)
def post_through_setup(self, cls):
self.use_gfk = (
self.through is None or issubclass(self.through, GenericTaggedItemBase)
)
self.rel.to = self.through._meta.get_field("tag").rel.to
if self.use_gfk:
tagged_items = GenericRelation(self.through)
tagged_items.contribute_to_class(cls, "tagged_items")
def save_form_data(self, instance, value):
getattr(instance, self.name).set(*value)
def formfield(self, form_class=TagField, **kwargs):
defaults = {
"label": capfirst(self.verbose_name),
"help_text": self.help_text,
"required": not self.blank
}
defaults.update(kwargs)
return form_class(**defaults)
def value_from_object(self, instance):
if instance.pk:
return self.through.objects.filter(**self.through.lookup_kwargs(instance))
return self.through.objects.none()
def related_query_name(self):
return self.model._meta.module_name
def m2m_reverse_name(self):
return self.through._meta.get_field_by_name("tag")[0].column
def m2m_target_field_name(self):
return self.model._meta.pk.name
def m2m_reverse_target_field_name(self):
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 extra_filters(self, pieces, pos, negate):
if negate or not self.use_gfk:
return []
prefix = "__".join(["tagged_items"] + pieces[:pos-2])
cts = map(ContentType.objects.get_for_model, _get_subclasses(self.model))
if len(cts) == 1:
return [("%s__content_type" % prefix, cts[0])]
return [("%s__content_type__in" % prefix, cts)]
def bulk_related_objects(self, new_objs, using):
return []
class _TaggableManager(models.Manager):
def __init__(self, through, model, instance):
self.through = through
self.model = model
self.instance = instance
def get_query_set(self):
return self.through.tags_for(self.model, self.instance)
def _lookup_kwargs(self):
return self.through.lookup_kwargs(self.instance)
@require_instance_manager
def add(self, *tags):
str_tags = set([
t
for t in tags
if not isinstance(t, self.through.tag_model())
])
tag_objs = set(tags) - str_tags
# 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
)
tag_objs.update(existing)
for new_tag in str_tags - set(t.name for t in existing):
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 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_query_set().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(*lookup_kwargs.keys())
qs = qs.annotate(n=models.Count('pk'))
qs = qs.exclude(**lookup_kwargs)
qs = qs.filter(tag__in=self.all())
qs = qs.order_by('-n')
# TODO: This all feels like a bit of a hack.
items = {}
if len(lookup_keys) == 1:
# Can we do this without a second query by using a select_related()
# somehow?
f = self.through._meta.get_field_by_name(lookup_keys[0])[0]
objs = f.rel.to._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.iteritems():
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
def _get_subclasses(model):
subclasses = [model]
for f in model._meta.get_all_field_names():
field = model._meta.get_field_by_name(f)[0]
if (isinstance(field, RelatedObject) and
getattr(field.field.rel, "parent_link", None)):
subclasses.extend(_get_subclasses(field.model))
return subclasses

160
vendor/packages/django-taggit/taggit/models.py поставляемый
Просмотреть файл

@ -1,160 +0,0 @@
import django
from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes.generic import GenericForeignKey
from django.db import models, IntegrityError, transaction
from django.template.defaultfilters import slugify as default_slugify
from django.utils.translation import ugettext_lazy as _, ugettext
class TagBase(models.Model):
name = models.CharField(verbose_name=_('Name'), max_length=100)
slug = models.SlugField(verbose_name=_('Slug'), unique=True, max_length=100)
def __unicode__(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)
if django.VERSION >= (1, 2):
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
trans_kwargs = {"using": using}
else:
trans_kwargs = {}
i = 0
while True:
i += 1
try:
sid = transaction.savepoint(**trans_kwargs)
res = super(TagBase, self).save(*args, **kwargs)
transaction.savepoint_commit(sid, **trans_kwargs)
return res
except IntegrityError:
transaction.savepoint_rollback(sid, **trans_kwargs)
self.slug = self.slugify(self.name, i)
else:
return super(TagBase, self).save(*args, **kwargs)
def slugify(self, tag, i=None):
slug = default_slugify(tag)
if i is not None:
slug += "_%d" % i
return slug
class Tag(TagBase):
class Meta:
verbose_name = _("Tag")
verbose_name_plural = _("Tags")
class ItemBase(models.Model):
def __unicode__(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):
return cls._meta.get_field_by_name("tag")[0].rel.to
@classmethod
def tag_relname(cls):
return cls._meta.get_field_by_name('tag')[0].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):
if django.VERSION < (1, 2):
tag = models.ForeignKey(Tag, related_name="%(class)s_items")
else:
tag = models.ForeignKey(Tag, related_name="%(app_label)s_%(class)s_items")
class Meta:
abstract = True
@classmethod
def tags_for(cls, model, instance=None):
if instance is not None:
return cls.tag_model().objects.filter(**{
'%s__content_object' % cls.tag_relname(): instance
})
return cls.tag_model().objects.filter(**{
'%s__content_object__isnull' % cls.tag_relname(): False
}).distinct()
class GenericTaggedItemBase(ItemBase):
object_id = models.IntegerField(verbose_name=_('Object id'), db_index=True)
if django.VERSION < (1, 2):
content_type = models.ForeignKey(
ContentType,
verbose_name=_('Content type'),
related_name="%(class)s_tagged_items"
)
else:
content_type = models.ForeignKey(
ContentType,
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):
# 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):
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
return cls.tag_model().objects.filter(**kwargs).distinct()
class TaggedItem(GenericTaggedItemBase, TaggedItemBase):
class Meta:
verbose_name = _("Tagged Item")
verbose_name_plural = _("Tagged Items")

Просмотреть файл

Просмотреть файл

@ -1,20 +0,0 @@
from django import forms
from taggit.tests.models import Food, DirectFood, CustomPKFood, OfficialFood
class FoodForm(forms.ModelForm):
class Meta:
model = Food
class DirectFoodForm(forms.ModelForm):
class Meta:
model = DirectFood
class CustomPKFoodForm(forms.ModelForm):
class Meta:
model = CustomPKFood
class OfficialFoodForm(forms.ModelForm):
class Meta:
model = OfficialFood

Просмотреть файл

@ -1,143 +0,0 @@
from django.db import models
from taggit.managers import TaggableManager
from taggit.models import (TaggedItemBase, GenericTaggedItemBase, TaggedItem,
TagBase, Tag)
class Food(models.Model):
name = models.CharField(max_length=50)
tags = TaggableManager()
def __unicode__(self):
return self.name
class Pet(models.Model):
name = models.CharField(max_length=50)
tags = TaggableManager()
def __unicode__(self):
return self.name
class HousePet(Pet):
trained = models.BooleanField()
# Test direct-tagging with custom through model
class TaggedFood(TaggedItemBase):
content_object = models.ForeignKey('DirectFood')
class TaggedPet(TaggedItemBase):
content_object = models.ForeignKey('DirectPet')
class DirectFood(models.Model):
name = models.CharField(max_length=50)
tags = TaggableManager(through="TaggedFood")
class DirectPet(models.Model):
name = models.CharField(max_length=50)
tags = TaggableManager(through=TaggedPet)
def __unicode__(self):
return self.name
class DirectHousePet(DirectPet):
trained = models.BooleanField()
# Test custom through model to model with custom PK
class TaggedCustomPKFood(TaggedItemBase):
content_object = models.ForeignKey('CustomPKFood')
class TaggedCustomPKPet(TaggedItemBase):
content_object = models.ForeignKey('CustomPKPet')
class CustomPKFood(models.Model):
name = models.CharField(max_length=50, primary_key=True)
tags = TaggableManager(through=TaggedCustomPKFood)
def __unicode__(self):
return self.name
class CustomPKPet(models.Model):
name = models.CharField(max_length=50, primary_key=True)
tags = TaggableManager(through=TaggedCustomPKPet)
def __unicode__(self):
return self.name
class CustomPKHousePet(CustomPKPet):
trained = models.BooleanField()
# Test custom through model to a custom tag model
class OfficialTag(TagBase):
official = models.BooleanField()
class OfficialThroughModel(GenericTaggedItemBase):
tag = models.ForeignKey(OfficialTag, related_name="tagged_items")
class OfficialFood(models.Model):
name = models.CharField(max_length=50)
tags = TaggableManager(through=OfficialThroughModel)
def __unicode__(self):
return self.name
class OfficialPet(models.Model):
name = models.CharField(max_length=50)
tags = TaggableManager(through=OfficialThroughModel)
def __unicode__(self):
return self.name
class OfficialHousePet(OfficialPet):
trained = models.BooleanField()
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)

Просмотреть файл

@ -1,34 +0,0 @@
#!/usr/bin/env python
import os
import sys
from django.conf import settings
if not settings.configured:
settings.configure(
DATABASE_ENGINE='sqlite3',
INSTALLED_APPS=[
'django.contrib.contenttypes',
'taggit',
'taggit.tests',
]
)
from django.test.simple import run_tests
def runtests(*test_args):
if not test_args:
test_args = ['tests']
parent = os.path.join(
os.path.dirname(os.path.abspath(__file__)),
"..",
"..",
)
sys.path.insert(0, parent)
failures = run_tests(test_args, verbosity=1, interactive=True)
sys.exit(failures)
if __name__ == '__main__':
runtests(*sys.argv[1:])

Просмотреть файл

@ -1,475 +0,0 @@
from unittest import TestCase as UnitTestCase
import django
from django.conf import settings
from django.core.exceptions import ValidationError
from django.db import connection
from django.test import TestCase, TransactionTestCase
from taggit.managers import TaggableManager
from taggit.models import Tag, TaggedItem
from taggit.tests.forms import (FoodForm, DirectFoodForm, CustomPKFoodForm,
OfficialFoodForm)
from taggit.tests.models import (Food, Pet, HousePet, DirectFood, DirectPet,
DirectHousePet, TaggedPet, CustomPKFood, CustomPKPet, CustomPKHousePet,
TaggedCustomPKPet, OfficialFood, OfficialPet, OfficialHousePet,
OfficialThroughModel, OfficialTag, Photo, Movie, Article)
from taggit.utils import parse_tags, edit_string_for_tags
class BaseTaggingTest(object):
def assert_tags_equal(self, qs, tags, sort=True, attr="name"):
got = map(lambda tag: getattr(tag, attr), qs)
if sort:
got.sort()
tags.sort()
self.assertEqual(got, tags)
def assert_num_queries(self, n, f, *args, **kwargs):
original_DEBUG = settings.DEBUG
settings.DEBUG = True
current = len(connection.queries)
try:
f(*args, **kwargs)
self.assertEqual(
len(connection.queries) - current,
n,
)
finally:
settings.DEBUG = original_DEBUG
def _get_form_str(self, form_str):
if django.VERSION >= (1, 3):
form_str %= {
"help_start": '<span class="helptext">',
"help_stop": "</span>"
}
else:
form_str %= {
"help_start": "",
"help_stop": ""
}
return form_str
def assert_form_renders(self, form, html):
self.assertEqual(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")
class TagModelDirectTestCase(TagModelTestCase):
food_model = DirectFood
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):
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.
self.assert_num_queries(10, 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.
self.assert_num_queries(5, pear.tags.add, "green", "delicious")
self.assert_num_queries(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")
self.assertEqual(
map(lambda o: o.pk, self.pet_model.objects.filter(tags__name__in=["fuzzy"])),
[kitty.pk, cat.pk]
)
def test_exclude(self):
apple = self.food_model.objects.create(name="apple")
apple.tags.add("red", "green", "delicious")
pear = self.food_model.objects.create(name="pear")
pear.tags.add("green", "delicious")
guava = self.food_model.objects.create(name="guava")
self.assertEqual(
map(lambda o: o.pk, self.food_model.objects.exclude(tags__name__in=["red"])),
[pear.pk, guava.pk],
)
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(map(lambda x: x.similar_tags, similar_objs), [3, 2])
def test_tag_reuse(self):
apple = self.food_model.objects.create(name="apple")
apple.tags.add("juicy", "juicy")
self.assert_tags_equal(apple.tags.all(), ['juicy'])
def test_query_traverse(self):
spot = self.pet_model.objects.create(name='Spot')
spike = self.pet_model.objects.create(name='Spike')
spot.tags.add('scary')
spike.tags.add('fluffy')
lookup_kwargs = {
'%s__name' % self.pet_model._meta.module_name: 'Spot'
}
self.assert_tags_equal(
self.tag_model.objects.filter(**lookup_kwargs),
['scary']
)
def test_taggeditem_unicode(self):
ross = self.pet_model.objects.create(name="ross")
# I keep Ross Perot for a pet, what's it to you?
ross.tags.add("president")
self.assertEqual(
unicode(self.taggeditem_model.objects.all()[0]),
"ross tagged with president"
)
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"],
)
class TaggableManagerDirectTestCase(TaggableManagerTestCase):
food_model = DirectFood
pet_model = DirectPet
housepet_model = DirectHousePet
taggeditem_model = TaggedPet
class TaggableManagerCustomPKTestCase(TaggableManagerTestCase):
food_model = CustomPKFood
pet_model = CustomPKPet
housepet_model = CustomPKHousePet
taggeditem_model = TaggedCustomPKPet
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(
map(lambda o: o.pk, self.food_model.objects.filter(tags__official=False)),
[apple.pk],
)
class TaggableFormTestCase(BaseTaggingTestCase):
form_class = FoodForm
food_model = Food
def test_form(self):
self.assertEqual(self.form_class.base_fields.keys(), ['name', 'tags'])
f = self.form_class({'name': 'apple', 'tags': 'green, red, yummy'})
self.assert_form_renders(f, """<tr><th><label for="id_name">Name:</label></th><td><input id="id_name" type="text" name="name" value="apple" maxlength="50" /></td></tr>
<tr><th><label for="id_tags">Tags:</label></th><td><input type="text" name="tags" value="green, red, yummy" id="id_tags" /><br />%(help_start)sA comma-separated list of tags.%(help_stop)s</td></tr>""")
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, """<tr><th><label for="id_name">Name:</label></th><td><input id="id_name" type="text" name="name" value="apple" maxlength="50" /></td></tr>
<tr><th><label for="id_tags">Tags:</label></th><td><input type="text" name="tags" value="delicious, green, red, yummy" id="id_tags" /><br />%(help_start)sA comma-separated list of tags.%(help_stop)s</td></tr>""")
apple.tags.add('has,comma')
f = self.form_class(instance=apple)
self.assert_form_renders(f, """<tr><th><label for="id_name">Name:</label></th><td><input id="id_name" type="text" name="name" value="apple" maxlength="50" /></td></tr>
<tr><th><label for="id_tags">Tags:</label></th><td><input type="text" name="tags" value="&quot;has,comma&quot;, delicious, green, red, yummy" id="id_tags" /><br />%(help_start)sA comma-separated list of tags.%(help_stop)s</td></tr>""")
apple.tags.add('has space')
f = self.form_class(instance=apple)
self.assert_form_renders(f, """<tr><th><label for="id_name">Name:</label></th><td><input id="id_name" type="text" name="name" value="apple" maxlength="50" /></td></tr>
<tr><th><label for="id_tags">Tags:</label></th><td><input type="text" name="tags" value="&quot;has space&quot;, &quot;has,comma&quot;, delicious, green, red, yummy" id="id_tags" /><br />%(help_start)sA comma-separated list of tags.%(help_stop)s</td></tr>""")
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, u'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 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
<http://django-tagging.googlecode.com/>`_
"""
def test_with_simple_space_delimited_tags(self):
"""
Test with simple space-delimited tags.
"""
self.assertEqual(parse_tags('one'), [u'one'])
self.assertEqual(parse_tags('one two'), [u'one', u'two'])
self.assertEqual(parse_tags('one two three'), [u'one', u'three', u'two'])
self.assertEqual(parse_tags('one one two two'), [u'one', u'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'), [u'one'])
self.assertEqual(parse_tags(',one two'), [u'one two'])
self.assertEqual(parse_tags(',one two three'), [u'one two three'])
self.assertEqual(parse_tags('a-one, a-two and a-three'),
[u'a-one', u'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'), [u'one'])
self.assertEqual(parse_tags('"one two'), [u'one', u'two'])
self.assertEqual(parse_tags('"one two three'), [u'one', u'three', u'two'])
self.assertEqual(parse_tags('"one two"'), [u'one two'])
self.assertEqual(parse_tags('a-one "a-two and a-three"'),
[u'a-one', u'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"'), [u'one', u'thr,ee', u'two'])
def test_with_loose_commas(self):
"""
Loose commas - split on commas
"""
self.assertEqual(parse_tags('"one", two three'), [u'one', u'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"'),
[u'a-one', u'a-two, and a-three'])
self.assertEqual(parse_tags('"two", one, one, two, "one"'),
[u'one', u'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('",",",",",",","'), [u','])
self.assertEqual(parse_tags('a-one "a-two" and "a-three'),
[u'a-one', u'a-three', u'a-two', u'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]), u'plain')
self.assertEqual(edit_string_for_tags([plain, spaces]), u'"spa ces", plain')
self.assertEqual(edit_string_for_tags([plain, spaces, comma]), u'"com,ma", "spa ces", plain')
self.assertEqual(edit_string_for_tags([plain, comma]), u'"com,ma", plain')
self.assertEqual(edit_string_for_tags([comma, spaces]), u'"com,ma", "spa ces"')

126
vendor/packages/django-taggit/taggit/utils.py поставляемый
Просмотреть файл

@ -1,126 +0,0 @@
from django.utils.encoding import force_unicode
from django.utils.functional import wraps
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
<http://django-tagging.googlecode.com/>`_
"""
if not tagstring:
return []
tagstring = force_unicode(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 u',' not in tagstring and u'"' not in tagstring:
words = list(set(split_strip(tagstring, u' ')))
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 = i.next()
if c == u'"':
if buffer:
to_be_split.append(u''.join(buffer))
buffer = []
# Find the matching quote
open_quote = True
c = i.next()
while c != u'"':
buffer.append(c)
c = i.next()
if buffer:
word = u''.join(buffer).strip()
if word:
words.append(word)
buffer = []
open_quote = False
else:
if not saw_loose_comma and c == u',':
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 u',' in buffer:
saw_loose_comma = True
to_be_split.append(u''.join(buffer))
if to_be_split:
if saw_loose_comma:
delimiter = u','
else:
delimiter = u' '
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=u','):
"""
Splits ``string`` on ``delimiter``, stripping each resulting string
and returning a list of non-empty strings.
Ported from Jonathan Buchanan's `django-tagging
<http://django-tagging.googlecode.com/>`_
"""
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
<http://django-tagging.googlecode.com/>`_
"""
names = []
for tag in tags:
name = tag.name
if u',' in name or u' ' in name:
names.append('"%s"' % name)
else:
names.append(name)
return u', '.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

18
vendor/packages/django-taggit/taggit/views.py поставляемый
Просмотреть файл

@ -1,18 +0,0 @@
from django.contrib.contenttypes.models import ContentType
from django.shortcuts import get_object_or_404
from django.views.generic.list_detail import object_list
from taggit.models import TaggedItem, Tag
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 object_list(request, qs, **kwargs)

44
vendor/packages/django-taggit/tox.ini поставляемый
Просмотреть файл

@ -1,44 +0,0 @@
[tox]
envlist =
py24, py25, py26, py27, pypy, py24-trunk, py25-trunk, py26-trunk, py27-trunk, pypy-trunk, docs
[testenv]
commands =
python setup.py test
deps =
django==1.2.4
# We lied here, these are not really trunk, but rather the 1.3 beta-1, which
# is close enough.
[testenv:py24-trunk]
basepython = python2.4
deps =
http://www.djangoproject.com/download/1.3-beta-1/tarball/
[testenv:py25-trunk]
basepython = python2.5
deps =
http://www.djangoproject.com/download/1.3-beta-1/tarball/
[testenv:py26-trunk]
basepython = python2.6
deps =
http://www.djangoproject.com/download/1.3-beta-1/tarball/
[testenv:py27-trunk]
basepython = python2.7
deps =
http://www.djangoproject.com/download/1.3-beta-1/tarball/
[testenv:pypy-trunk]
basepython = pypy
deps =
http://www.djangoproject.com/download/1.3-beta-1/tarball/
[testenv:docs]
changedir = docs
deps =
sphinx
commands =
sphinx-build -W -b html -d {envtmpdir}/doctrees . {envtmpdir}/html
sphinx-build -W -b linkcheck -d {envtmpdir}/doctrees . {envtmpdir}/linkcheck

1
vendor/src/django-taggit поставляемый Submodule

@ -0,0 +1 @@
Subproject commit 1f4ffa629c8deddea2f75779b8407c6c97fe3e4b