Add API documentation to the package!
parent
c76cc663e0
commit
f9fffc44c1
|
|
@ -0,0 +1,3 @@
|
||||||
|
# for readthedocs
|
||||||
|
# Remaining requirements are picked up from setup.py
|
||||||
|
Django==1.5.10
|
||||||
|
|
@ -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'
|
||||||
|
|
@ -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)
|
||||||
|
|
||||||
|
|
@ -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",
|
||||||
|
)
|
||||||
|
|
@ -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
|
||||||
|
|
@ -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:
|
||||||
|
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
polymorphic.contrib.extra_views
|
||||||
|
===============================
|
||||||
|
|
||||||
|
.. automodule:: polymorphic.contrib.extra_views
|
||||||
|
:members:
|
||||||
|
:undoc-members:
|
||||||
|
:show-inheritance:
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
polymorphic.contrib.guardian
|
||||||
|
============================
|
||||||
|
|
||||||
|
.. automodule:: polymorphic.contrib.guardian
|
||||||
|
:members:
|
||||||
|
:undoc-members:
|
||||||
|
:show-inheritance:
|
||||||
|
|
@ -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:
|
||||||
|
|
@ -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:
|
||||||
|
|
@ -0,0 +1,8 @@
|
||||||
|
polymorphic.models
|
||||||
|
==================
|
||||||
|
|
||||||
|
.. automodule:: polymorphic.models
|
||||||
|
|
||||||
|
.. autoclass:: polymorphic.models.PolymorphicModel
|
||||||
|
:members:
|
||||||
|
:show-inheritance:
|
||||||
|
|
@ -0,0 +1,4 @@
|
||||||
|
polymorphic.templatetags.polymorphic_admin_tags
|
||||||
|
===============================================
|
||||||
|
|
||||||
|
.. automodule:: polymorphic.templatetags
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
polymorphic.utils
|
||||||
|
=================
|
||||||
|
|
||||||
|
.. automodule:: polymorphic.utils
|
||||||
|
:members:
|
||||||
17
docs/conf.py
17
docs/conf.py
|
|
@ -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'
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
==================
|
==================
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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'
|
||||||
|
|
|
||||||
|
|
@ -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'
|
||||||
|
|
|
||||||
|
|
@ -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,
|
||||||
|
|
|
||||||
|
|
@ -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):
|
||||||
"""
|
"""
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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 %}
|
||||||
|
"""
|
||||||
Loading…
Reference in New Issue