Add API documentation to the package!

fix_request_path_info
Diederik van der Boor 2017-01-09 16:53:12 +01:00
parent c76cc663e0
commit f9fffc44c1
25 changed files with 397 additions and 93 deletions

View File

View File

@ -0,0 +1,3 @@
# for readthedocs
# Remaining requirements are picked up from setup.py
Django==1.5.10

View File

@ -0,0 +1,11 @@
# Settings file to allow parsing API documentation of Django modules,
# and provide defaults to use in the documentation.
#
# This file is placed in a subdirectory,
# so the docs root won't be detected by find_packages()
# Display sane URLs in the docs:
STATIC_URL = '/static/'
# Avoid error for missing the secret key
SECRET_KEY = 'docs'

View File

View File

@ -0,0 +1,47 @@
"""
Automatically mention all model fields as parameters in the model construction.
Based on http://djangosnippets.org/snippets/2533/
"""
import django
from django.utils.html import strip_tags
from django.utils.encoding import force_unicode
import inspect
def improve_model_docstring(app, what, name, obj, options, lines):
from django.db import models # must be inside the function, to allow settings initialization first.
if inspect.isclass(obj) and issubclass(obj, models.Model):
if django.VERSION >= (1,8):
model_fields = obj._meta.get_fields()
elif django.VERSION >= (1,6):
model_fields = obj._meta.fields
else:
model_fields = obj._meta._fields()
for field in model_fields:
help_text = strip_tags(force_unicode(field.help_text))
verbose_name = force_unicode(field.verbose_name).capitalize()
# Add parameter
if help_text:
lines.append(u':param %s: %s' % (field.attname, help_text))
else:
lines.append(u':param %s: %s' % (field.attname, verbose_name))
# Add type
if isinstance(field, models.ForeignKey):
to = field.rel.to
lines.append(u':type %s: %s to :class:`~%s.%s`' % (field.attname, type(field).__name__, to.__module__, to.__name__))
else:
lines.append(u':type %s: %s' % (field.attname, type(field).__name__))
# Return the extended docstring
return lines
# Allow this module to be used as sphinx extension:
def setup(app):
# Generate docstrings for Django model fields
# Register the docstring processor with sphinx
app.connect('autodoc-process-docstring', improve_model_docstring)

View File

@ -0,0 +1,8 @@
# Allow :django:setting:`SITE_ID` to work.
def setup(app):
app.add_crossref_type(
directivename = "setting",
rolename = "setting",
indextemplate = "pair: %s; setting",
)

13
docs/api/index.rst 100644
View File

@ -0,0 +1,13 @@
API Documentation
=================
.. toctree::
polymorphic.admin
polymorphic.contrib.extra_views
polymorphic.contrib.guardian
polymorphic.formsets
polymorphic.managers
polymorphic.models
polymorphic.templatetags
polymorphic.utils

View File

@ -0,0 +1,93 @@
polymorphic.admin
=================
ModelAdmin classes
------------------
The ``PolymorphicParentModelAdmin`` class
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. autoclass:: polymorphic.admin.PolymorphicParentModelAdmin
:members:
:undoc-members:
:show-inheritance:
The ``PolymorphicChildModelAdmin`` class
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. autoclass:: polymorphic.admin.PolymorphicChildModelAdmin
:members:
:undoc-members:
:show-inheritance:
List filtering
--------------
The ``PolymorphicChildModelFilter`` class
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. autoclass:: polymorphic.admin.PolymorphicChildModelFilter
:show-inheritance:
Inlines support
---------------
The ``StackedPolymorphicInline`` class
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. autoclass:: polymorphic.admin.StackedPolymorphicInline
:show-inheritance:
The ``GenericStackedPolymorphicInline`` class
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. autoclass:: polymorphic.admin.GenericStackedPolymorphicInline
:members:
:undoc-members:
:show-inheritance:
The ``PolymorphicInlineSupportMixin`` class
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. autoclass:: polymorphic.admin.PolymorphicInlineSupportMixin
:members:
:undoc-members:
:show-inheritance:
Low-level classes
-----------------
These classes are useful when existing parts of the admin classes.
.. autoclass:: polymorphic.admin.PolymorphicModelChoiceForm
:members:
:undoc-members:
:show-inheritance:
.. autoclass:: polymorphic.admin.PolymorphicInlineModelAdmin
:members:
:undoc-members:
:show-inheritance:
.. autoclass:: polymorphic.admin.GenericPolymorphicInlineModelAdmin
:members:
:undoc-members:
:show-inheritance:
.. autoclass:: polymorphic.admin.PolymorphicInlineAdminForm
:show-inheritance:
.. autoclass:: polymorphic.admin.PolymorphicInlineAdminFormSet
:show-inheritance:

View File

@ -0,0 +1,7 @@
polymorphic.contrib.extra_views
===============================
.. automodule:: polymorphic.contrib.extra_views
:members:
:undoc-members:
:show-inheritance:

View File

@ -0,0 +1,7 @@
polymorphic.contrib.guardian
============================
.. automodule:: polymorphic.contrib.guardian
:members:
:undoc-members:
:show-inheritance:

View File

@ -0,0 +1,41 @@
polymorphic.formsets
====================
.. automodule:: polymorphic.formsets
Model formsets
--------------
.. autofunction:: polymorphic.formsets.polymorphic_modelformset_factory
.. autoclass:: polymorphic.formsets.PolymorphicFormSetChild
Inline formsets
---------------
.. autofunction:: polymorphic.formsets.polymorphic_inlineformset_factory
Generic formsets
----------------
.. autofunction:: polymorphic.formsets.generic_polymorphic_inlineformset_factory
Low-level features
------------------
The internal machinery can be used to extend the formset classes. This includes:
.. autofunction:: polymorphic.formsets.polymorphic_child_forms_factory
.. autoclass:: polymorphic.formsets.BasePolymorphicModelFormSet
:show-inheritance:
.. autoclass:: polymorphic.formsets.BasePolymorphicInlineFormSet
:show-inheritance:
.. autoclass:: polymorphic.formsets.BaseGenericPolymorphicInlineFormSet
:show-inheritance:

View File

@ -0,0 +1,21 @@
polymorphic.managers
====================
.. automodule:: polymorphic.managers
The ``PolymorphicManager`` class
--------------------------------
.. autoclass:: polymorphic.managers.PolymorphicManager
:members:
:show-inheritance:
The ``PolymorphicQuerySet`` class
---------------------------------
.. autoclass:: polymorphic.managers.PolymorphicQuerySet
:members:
:show-inheritance:

View File

@ -0,0 +1,8 @@
polymorphic.models
==================
.. automodule:: polymorphic.models
.. autoclass:: polymorphic.models.PolymorphicModel
:members:
:show-inheritance:

View File

@ -0,0 +1,4 @@
polymorphic.templatetags.polymorphic_admin_tags
===============================================
.. automodule:: polymorphic.templatetags

View File

@ -0,0 +1,5 @@
polymorphic.utils
=================
.. automodule:: polymorphic.utils
:members:

View File

@ -13,6 +13,8 @@
import sys import sys
import os import os
import django
import sphinx_rtd_theme
# If extensions (or modules to document with autodoc) are in another directory, # 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 # add these directories to sys.path here. If the directory is relative to the
@ -21,6 +23,9 @@ sys.path.insert(0, os.path.abspath('_ext'))
sys.path.insert(0, os.path.abspath('..')) sys.path.insert(0, os.path.abspath('..'))
os.environ['DJANGO_SETTINGS_MODULE'] = 'djangodummy.settings' os.environ['DJANGO_SETTINGS_MODULE'] = 'djangodummy.settings'
if django.VERSION >= (1, 8):
django.setup()
# -- General configuration ----------------------------------------------------- # -- General configuration -----------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here. # If your documentation needs a minimal Sphinx version, state it here.
@ -31,7 +36,9 @@ os.environ['DJANGO_SETTINGS_MODULE'] = 'djangodummy.settings'
extensions = [ extensions = [
'sphinx.ext.autodoc', 'sphinx.ext.autodoc',
'sphinx.ext.graphviz', 'sphinx.ext.graphviz',
'sphinx.ext.intersphinx' 'sphinx.ext.intersphinx',
'djangoext.docstrings',
'djangoext.roles',
] ]
# Add any paths that contain templates here, relative to this directory. # Add any paths that contain templates here, relative to this directory.
@ -98,7 +105,8 @@ pygments_style = 'sphinx'
# The theme to use for HTML and HTML Help pages. See the documentation for # The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes. # a list of builtin themes.
#html_theme = 'alabaster' html_theme = 'sphinx_rtd_theme'
html_theme_path = [sphinx_rtd_theme.get_html_theme_path()]
# Theme options are theme-specific and customize the look and feel of a theme # 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 # further. For a list of options available for each theme, see the
@ -252,5 +260,8 @@ texinfo_documents = [
# Example configuration for intersphinx: refer to the Python standard library. # Example configuration for intersphinx: refer to the Python standard library.
intersphinx_mapping = { intersphinx_mapping = {
#'http://docs.python.org/': None, #'http://docs.python.org/': None,
'https://docs.djangoproject.com/en/dev': 'https://docs.djangoproject.com/en/dev/_objects', 'https://docs.djangoproject.com/en/stable': 'https://docs.djangoproject.com/en/stable/_objects',
} }
# autodoc settings
autodoc_member_order = 'groupwise'

View File

@ -1,8 +1,9 @@
Welcome to django-polymorphic's documentation! Welcome to django-polymorphic's documentation!
============================================== ==============================================
Django-polymorphic simplifies using inherited models in Django projects. Django-polymorphic builds on top of the standard Django model inheritance.
When a query is made at the base model, the inherited model classes are returned. It makes using inherited models easier. When a query is made at the base model,
the inherited model classes are returned.
When we store models that inherit from a ``Project`` model... When we store models that inherit from a ``Project`` model...
@ -43,6 +44,7 @@ Features
* Combining querysets of different models (``qs3 = qs1 | qs2``) * Combining querysets of different models (``qs3 = qs1 | qs2``)
* Support for custom user-defined managers. * Support for custom user-defined managers.
* Formset support.
* Uses the minimum amount of queries needed to fetch the inherited models. * Uses the minimum amount of queries needed to fetch the inherited models.
* Disabling polymorphic behavior when needed. * Disabling polymorphic behavior when needed.
@ -56,6 +58,7 @@ Getting started
quickstart quickstart
admin admin
performance performance
third-party
Advanced topics Advanced topics
--------------- ---------------
@ -67,9 +70,10 @@ Advanced topics
migrating migrating
managers managers
advanced advanced
third-party
changelog changelog
contributing contributing
api/index
Indices and tables Indices and tables
================== ==================

View File

@ -2,6 +2,35 @@ Third-party applications support
================================ ================================
django-guardian support
-----------------------
.. versionadded:: 1.0.2
You can configure django-guardian_ to use the base model for object level permissions.
Add this option to your settings:
.. code-block:: python
GUARDIAN_GET_CONTENT_TYPE = 'polymorphic.contrib.guardian.get_polymorphic_base_content_type'
This option requires django-guardian_ >= 1.4.6. Details about how this option works are available in the
`django-guardian documentation <https://django-guardian.readthedocs.io/en/latest/configuration.html#guardian-get-content-type>`_.
django-extra-views
------------------
.. versionadded:: 1.1
The :mod:`polymorphic.contrib.extra_views` package provides classes to display polymorphic formsets
using the classes from django-extra-views_. See the documentation of:
* :class:`~polymorphic.contrib.extra_views.PolymorphicFormSetView`
* :class:`~polymorphic.contrib.extra_views.PolymorphicInlineFormSetView`
* :class:`~polymorphic.contrib.extra_views.PolymorphicInlineFormSet`
django-mptt support django-mptt support
------------------- -------------------
@ -97,24 +126,9 @@ This doesn't work, since it needs to look for revisions of the child model. Usin
the view of the actual child model is used, similar to the way the regular change and delete views are redirected. the view of the actual child model is used, similar to the way the regular change and delete views are redirected.
django-guardian support .. _django-extra-views: https://github.com/AndrewIngram/django-extra-views
----------------------- .. _django-guardian: https://github.com/django-guardian/django-guardian
.. versionadded:: 1.0.2
You can configure django-guardian_ to use the base model for object level permissions.
Add this option to your settings:
.. code-block:: python
GUARDIAN_GET_CONTENT_TYPE = 'polymorphic.contrib.guardian.get_polymorphic_base_content_type'
This option requires django-guardian_ >= 1.4.6. Details about how this option works are available in the
`django-guardian documentation <https://django-guardian.readthedocs.io/en/latest/configuration.html#guardian-get-content-type>`_.
.. _django-reversion: https://github.com/etianen/django-reversion
.. _django-reversion-compare: https://github.com/jedie/django-reversion-compare
.. _django-mptt: https://github.com/django-mptt/django-mptt .. _django-mptt: https://github.com/django-mptt/django-mptt
.. _django-polymorphic-tree: https://github.com/django-polymorphic/django-polymorphic-tree .. _django-polymorphic-tree: https://github.com/django-polymorphic/django-polymorphic-tree
.. _django-guardian: https://github.com/django-guardian/django-guardian .. _django-reversion-compare: https://github.com/jedie/django-reversion-compare
.. _django-reversion: https://github.com/etianen/django-reversion

View File

@ -10,6 +10,7 @@ class GenericPolymorphicInlineModelAdmin(PolymorphicInlineModelAdmin, GenericInl
""" """
Base class for variation of inlines based on generic foreign keys. Base class for variation of inlines based on generic foreign keys.
""" """
#: The formset class
formset = BaseGenericPolymorphicInlineFormSet formset = BaseGenericPolymorphicInlineFormSet
def get_formset(self, request, obj=None, **kwargs): def get_formset(self, request, obj=None, **kwargs):
@ -58,4 +59,5 @@ class GenericStackedPolymorphicInline(GenericPolymorphicInlineModelAdmin):
""" """
The stacked layout for generic inlines. The stacked layout for generic inlines.
""" """
#: The default template to use.
template = 'admin/polymorphic/edit_inline/stacked.html' template = 'admin/polymorphic/edit_inline/stacked.html'

View File

@ -245,4 +245,5 @@ class StackedPolymorphicInline(PolymorphicInlineModelAdmin):
Stacked inline for django-polymorphic models. Stacked inline for django-polymorphic models.
Since tabular doesn't make much sense with changed fields, just offer this one. Since tabular doesn't make much sense with changed fields, just offer this one.
""" """
#: The default template to use.
template = 'admin/polymorphic/edit_inline/stacked.html' template = 'admin/polymorphic/edit_inline/stacked.html'

View File

@ -1,6 +1,4 @@
""" """
Polymorphic formsets support.
This allows creating formsets where each row can be a different form type. This allows creating formsets where each row can be a different form type.
The logic of the formsets work similar to the standard Django formsets; The logic of the formsets work similar to the standard Django formsets;
there are factory methods to construct the classes with the proper form settings. there are factory methods to construct the classes with the proper form settings.
@ -9,21 +7,6 @@ The "parent" formset hosts the entire model and their child model.
For every child type, there is an :class:`PolymorphicFormSetChild` instance For every child type, there is an :class:`PolymorphicFormSetChild` instance
that describes how to display and construct the child. that describes how to display and construct the child.
It's parameters are very similar to the parent's factory method. It's parameters are very similar to the parent's factory method.
See:
* :func:`polymorphic_inlineformset_factory`
* :class:`PolymorphicFormSetChild`
The internal machinery can be used to extend the formset classes. This includes:
* :class:`BasePolymorphicModelFormSet`
* :class:`BasePolymorphicInlineFormSet`
* :func:`polymorphic_child_forms_factory`
For generic relations, a similar set is available:
* :class:`BasePolymorphicGenericInlineFormSet`
* :class:`PolymorphicGenericFormSetChild`
* :func:`polymorphic_generic_inlineformset_factory`
""" """
from .models import ( from .models import (
BasePolymorphicModelFormSet, BasePolymorphicModelFormSet,

View File

@ -1,6 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" PolymorphicManager """
Please see README.rst or DOCS.rst or http://chrisglass.github.com/django_polymorphic/ The manager class for use in the models.
""" """
from __future__ import unicode_literals from __future__ import unicode_literals
import warnings import warnings
@ -14,6 +14,12 @@ except ImportError:
from django.utils.encoding import python_2_unicode_compatible # Django 1.5 from django.utils.encoding import python_2_unicode_compatible # Django 1.5
__all__ = (
'PolymorphicManager',
'PolymorphicQuerySet',
)
@python_2_unicode_compatible @python_2_unicode_compatible
class PolymorphicManager(models.Manager): class PolymorphicManager(models.Manager):
""" """

View File

@ -1,17 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
""" """
Seamless Polymorphic Inheritance for Django Models Seamless Polymorphic Inheritance for Django Models
==================================================
Please see README.rst and DOCS.rst for further information.
Or on the Web:
http://chrisglass.github.com/django_polymorphic/
http://github.com/chrisglass/django_polymorphic
Copyright:
This code and affiliated files are (C) by Bert Constantin and individual contributors.
Please see LICENSE and AUTHORS for more information.
""" """
from __future__ import absolute_import from __future__ import absolute_import
@ -33,18 +22,8 @@ class PolymorphicModel(six.with_metaclass(PolymorphicModelBase, models.Model)):
Abstract base class that provides polymorphic behaviour Abstract base class that provides polymorphic behaviour
for any model directly or indirectly derived from it. for any model directly or indirectly derived from it.
For usage instructions & examples please see documentation. PolymorphicModel declares one field for internal use (:attr:`polymorphic_ctype`)
and provides a polymorphic manager as the default manager (and as 'objects').
PolymorphicModel declares one field for internal use (polymorphic_ctype)
and provides a polymorphic manager as the default manager
(and as 'objects').
PolymorphicModel overrides the save() and __init__ methods.
If your derived class overrides any of these methods as well, then you need
to take care that you correctly call the method of the superclass, like:
super(YourClass,self).save(*args,**kwargs)
""" """
# for PolymorphicModelBase, so it can tell which models are polymorphic and which are not (duck typing) # for PolymorphicModelBase, so it can tell which models are polymorphic and which are not (duck typing)
@ -57,6 +36,7 @@ class PolymorphicModel(six.with_metaclass(PolymorphicModelBase, models.Model)):
abstract = True abstract = True
# avoid ContentType related field accessor clash (an error emitted by model validation) # avoid ContentType related field accessor clash (an error emitted by model validation)
#: The model field that stores the :class:`~django.contrib.contenttypes.models.ContentType` reference to the actual class.
polymorphic_ctype = models.ForeignKey(ContentType, null=True, editable=False, polymorphic_ctype = models.ForeignKey(ContentType, null=True, editable=False,
related_name='polymorphic_%(app_label)s.%(class)s_set+') related_name='polymorphic_%(app_label)s.%(class)s_set+')
@ -69,24 +49,24 @@ class PolymorphicModel(six.with_metaclass(PolymorphicModelBase, models.Model)):
base_objects = models.Manager() base_objects = models.Manager()
@classmethod @classmethod
def translate_polymorphic_Q_object(self_class, q): def translate_polymorphic_Q_object(cls, q):
return translate_polymorphic_Q_object(self_class, q) return translate_polymorphic_Q_object(cls, q)
def pre_save_polymorphic(self, using=DEFAULT_DB_ALIAS): def pre_save_polymorphic(self, using=DEFAULT_DB_ALIAS):
"""Normally not needed.
This function may be called manually in special use-cases. When the object
is saved for the first time, we store its real class in polymorphic_ctype.
When the object later is retrieved by PolymorphicQuerySet, it uses this
field to figure out the real class of this object
(used by PolymorphicQuerySet._get_real_instances)
""" """
Make sure the ``polymorphic_ctype`` value is correctly set on this model.
"""
# This function may be called manually in special use-cases. When the object
# is saved for the first time, we store its real class in polymorphic_ctype.
# When the object later is retrieved by PolymorphicQuerySet, it uses this
# field to figure out the real class of this object
# (used by PolymorphicQuerySet._get_real_instances)
if not self.polymorphic_ctype_id: if not self.polymorphic_ctype_id:
self.polymorphic_ctype = ContentType.objects.db_manager(using).get_for_model(self, for_concrete_model=False) self.polymorphic_ctype = ContentType.objects.db_manager(using).get_for_model(self, for_concrete_model=False)
pre_save_polymorphic.alters_data = True pre_save_polymorphic.alters_data = True
def save(self, *args, **kwargs): def save(self, *args, **kwargs):
"""Overridden model save function which supports the polymorphism """Calls :meth:`pre_save_polymorphic` and saves the model."""
functionality (through pre_save_polymorphic)."""
using = kwargs.get('using', self._state.db or DEFAULT_DB_ALIAS) using = kwargs.get('using', self._state.db or DEFAULT_DB_ALIAS)
self.pre_save_polymorphic(using=using) self.pre_save_polymorphic(using=using)
return super(PolymorphicModel, self).save(*args, **kwargs) return super(PolymorphicModel, self).save(*args, **kwargs)
@ -94,7 +74,8 @@ class PolymorphicModel(six.with_metaclass(PolymorphicModelBase, models.Model)):
def get_real_instance_class(self): def get_real_instance_class(self):
""" """
Normally not needed. Return the actual model type of the object.
If a non-polymorphic manager (like base_objects) has been used to If a non-polymorphic manager (like base_objects) has been used to
retrieve objects, then the real class/type of these objects may be retrieve objects, then the real class/type of these objects may be
determined using this method. determined using this method.
@ -121,10 +102,10 @@ class PolymorphicModel(six.with_metaclass(PolymorphicModelBase, models.Model)):
return model return model
def get_real_concrete_instance_class_id(self): def get_real_concrete_instance_class_id(self):
model_class = self.get_real_instance_class() ct = self.get_real_concrete_instance_class()
if model_class is None: if ct is None:
return None return None
return ContentType.objects.db_manager(self._state.db).get_for_model(model_class, for_concrete_model=True).pk return ct.pk
def get_real_concrete_instance_class(self): def get_real_concrete_instance_class(self):
model_class = self.get_real_instance_class() model_class = self.get_real_instance_class()
@ -133,11 +114,18 @@ class PolymorphicModel(six.with_metaclass(PolymorphicModelBase, models.Model)):
return ContentType.objects.db_manager(self._state.db).get_for_model(model_class, for_concrete_model=True).model_class() return ContentType.objects.db_manager(self._state.db).get_for_model(model_class, for_concrete_model=True).model_class()
def get_real_instance(self): def get_real_instance(self):
"""Normally not needed. """
Upcast an object to it's actual type.
If a non-polymorphic manager (like base_objects) has been used to If a non-polymorphic manager (like base_objects) has been used to
retrieve objects, then the complete object with it's real class/type retrieve objects, then the complete object with it's real class/type
and all fields may be retrieved with this method. and all fields may be retrieved with this method.
Each method call executes one db query (if necessary)."""
.. note::
Each method call executes one db query (if necessary).
Use the :meth:`~polymorphic.managers.PolymorphicQuerySet.get_real_instances`
to upcast a complete list in a single efficient query.
"""
real_model = self.get_real_instance_class() real_model = self.get_real_instance_class()
if real_model == self.__class__: if real_model == self.__class__:
return self return self

View File

@ -63,7 +63,7 @@ class PolymorphicQuerySet(QuerySet):
""" """
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
"init our queryset object member variables" # init our queryset object member variables
self.polymorphic_disabled = False self.polymorphic_disabled = False
# A parallel structure to django.db.models.query.Query.deferred_loading, # A parallel structure to django.db.models.query.Query.deferred_loading,
# which we maintain with the untranslated field names passed to # which we maintain with the untranslated field names passed to
@ -74,7 +74,7 @@ class PolymorphicQuerySet(QuerySet):
super(PolymorphicQuerySet, self).__init__(*args, **kwargs) super(PolymorphicQuerySet, self).__init__(*args, **kwargs)
def _clone(self, *args, **kwargs): def _clone(self, *args, **kwargs):
"Django's _clone only copies its own variables, so we need to copy ours here" # Django's _clone only copies its own variables, so we need to copy ours here
new = super(PolymorphicQuerySet, self)._clone(*args, **kwargs) new = super(PolymorphicQuerySet, self)._clone(*args, **kwargs)
new.polymorphic_disabled = self.polymorphic_disabled new.polymorphic_disabled = self.polymorphic_disabled
new.polymorphic_deferred_loading = ( new.polymorphic_deferred_loading = (
@ -101,17 +101,17 @@ class PolymorphicQuerySet(QuerySet):
return qs return qs
def instance_of(self, *args): def instance_of(self, *args):
"""Filter the queryset to only include the classes in args (and their subclasses). """Filter the queryset to only include the classes in args (and their subclasses)."""
Implementation in _translate_polymorphic_filter_defnition.""" # Implementation in _translate_polymorphic_filter_defnition.
return self.filter(instance_of=args) return self.filter(instance_of=args)
def not_instance_of(self, *args): def not_instance_of(self, *args):
"""Filter the queryset to exclude the classes in args (and their subclasses). """Filter the queryset to exclude the classes in args (and their subclasses)."""
Implementation in _translate_polymorphic_filter_defnition.""" # Implementation in _translate_polymorphic_filter_defnition."""
return self.filter(not_instance_of=args) return self.filter(not_instance_of=args)
def _filter_or_exclude(self, negate, *args, **kwargs): def _filter_or_exclude(self, negate, *args, **kwargs):
"We override this internal Django functon as it is used for all filter member functions." # We override this internal Django functon as it is used for all filter member functions.
q_objects = translate_polymorphic_filter_definitions_in_args(self.model, args, using=self.db) # the Q objects q_objects = translate_polymorphic_filter_definitions_in_args(self.model, args, using=self.db) # the Q objects
additional_args = translate_polymorphic_filter_definitions_in_kwargs(self.model, kwargs, using=self.db) # filter_field='data' additional_args = translate_polymorphic_filter_definitions_in_kwargs(self.model, kwargs, using=self.db) # filter_field='data'
return super(PolymorphicQuerySet, self)._filter_or_exclude(negate, *(list(q_objects) + additional_args), **kwargs) return super(PolymorphicQuerySet, self)._filter_or_exclude(negate, *(list(q_objects) + additional_args), **kwargs)
@ -413,7 +413,7 @@ class PolymorphicQuerySet(QuerySet):
By overriding it, we modify the objects that this queryset returns By overriding it, we modify the objects that this queryset returns
when it is evaluated (or its get method or other object-returning methods are called). when it is evaluated (or its get method or other object-returning methods are called).
Here we do the same as: Here we do the same as::
base_result_objects = list(super(PolymorphicQuerySet, self).iterator()) base_result_objects = list(super(PolymorphicQuerySet, self).iterator())
real_results = self._get_real_instances(base_result_objects) real_results = self._get_real_instances(base_result_objects)
@ -464,6 +464,17 @@ class PolymorphicQuerySet(QuerySet):
return '[ ' + ',\n '.join(result) + ' ]' return '[ ' + ',\n '.join(result) + ' ]'
def get_real_instances(self, base_result_objects=None): def get_real_instances(self, base_result_objects=None):
"""
Cast a list of objects to their actual classes.
This does roughly the same as::
return [ o.get_real_instance() for o in base_result_objects ]
but more efficiently.
:rtype: PolymorphicQuerySet
"""
"same as _get_real_instances, but make sure that __repr__ for ShowField... creates correct output" "same as _get_real_instances, but make sure that __repr__ for ShowField... creates correct output"
if not base_result_objects: if not base_result_objects:
base_result_objects = self base_result_objects = self

View File

@ -0,0 +1,16 @@
"""
Template tags to use in the admin.
The ``{% breadcrumb_scope ... %}`` tag makes sure the ``{{ opts }}`` and ``{{ app_label }}``
values are temporary based on the provided ``{{ base_opts }}``.
This allows fixing the breadcrumb in admin templates:
.. code-block:: html+django
{% extends "admin/change_form.html" %}
{% load polymorphic_admin_tags %}
{% block breadcrumbs %}
{% breadcrumb_scope base_opts %}{{ block.super }}{% endbreadcrumb_scope %}
{% endblock %}
"""