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 os
import django
import sphinx_rtd_theme
# 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
@ -21,6 +23,9 @@ sys.path.insert(0, os.path.abspath('_ext'))
sys.path.insert(0, os.path.abspath('..'))
os.environ['DJANGO_SETTINGS_MODULE'] = 'djangodummy.settings'
if django.VERSION >= (1, 8):
django.setup()
# -- General configuration -----------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here.
@ -31,7 +36,9 @@ os.environ['DJANGO_SETTINGS_MODULE'] = 'djangodummy.settings'
extensions = [
'sphinx.ext.autodoc',
'sphinx.ext.graphviz',
'sphinx.ext.intersphinx'
'sphinx.ext.intersphinx',
'djangoext.docstrings',
'djangoext.roles',
]
# 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
# 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
# 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.
intersphinx_mapping = {
#'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!
==============================================
Django-polymorphic simplifies using inherited models in Django projects.
When a query is made at the base model, the inherited model classes are returned.
Django-polymorphic builds on top of the standard Django model inheritance.
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...
@ -43,6 +44,7 @@ Features
* Combining querysets of different models (``qs3 = qs1 | qs2``)
* Support for custom user-defined managers.
* Formset support.
* Uses the minimum amount of queries needed to fetch the inherited models.
* Disabling polymorphic behavior when needed.
@ -56,6 +58,7 @@ Getting started
quickstart
admin
performance
third-party
Advanced topics
---------------
@ -67,9 +70,10 @@ Advanced topics
migrating
managers
advanced
third-party
changelog
contributing
api/index
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
-------------------
@ -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.
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-reversion: https://github.com/etianen/django-reversion
.. _django-reversion-compare: https://github.com/jedie/django-reversion-compare
.. _django-extra-views: https://github.com/AndrewIngram/django-extra-views
.. _django-guardian: https://github.com/django-guardian/django-guardian
.. _django-mptt: https://github.com/django-mptt/django-mptt
.. _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.
"""
#: The formset class
formset = BaseGenericPolymorphicInlineFormSet
def get_formset(self, request, obj=None, **kwargs):
@ -58,4 +59,5 @@ class GenericStackedPolymorphicInline(GenericPolymorphicInlineModelAdmin):
"""
The stacked layout for generic inlines.
"""
#: The default template to use.
template = 'admin/polymorphic/edit_inline/stacked.html'

View File

@ -245,4 +245,5 @@ class StackedPolymorphicInline(PolymorphicInlineModelAdmin):
Stacked inline for django-polymorphic models.
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'

View File

@ -1,6 +1,4 @@
"""
Polymorphic formsets support.
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;
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
that describes how to display and construct the child.
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 (
BasePolymorphicModelFormSet,

View File

@ -1,6 +1,6 @@
# -*- 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
import warnings
@ -14,6 +14,12 @@ except ImportError:
from django.utils.encoding import python_2_unicode_compatible # Django 1.5
__all__ = (
'PolymorphicManager',
'PolymorphicQuerySet',
)
@python_2_unicode_compatible
class PolymorphicManager(models.Manager):
"""

View File

@ -1,17 +1,6 @@
# -*- coding: utf-8 -*-
"""
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
@ -33,18 +22,8 @@ class PolymorphicModel(six.with_metaclass(PolymorphicModelBase, models.Model)):
Abstract base class that provides polymorphic behaviour
for any model directly or indirectly derived from it.
For usage instructions & examples please see documentation.
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)
PolymorphicModel declares one field for internal use (:attr:`polymorphic_ctype`)
and provides a polymorphic manager as the default manager (and as 'objects').
"""
# 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
# 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,
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()
@classmethod
def translate_polymorphic_Q_object(self_class, q):
return translate_polymorphic_Q_object(self_class, q)
def translate_polymorphic_Q_object(cls, q):
return translate_polymorphic_Q_object(cls, q)
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:
self.polymorphic_ctype = ContentType.objects.db_manager(using).get_for_model(self, for_concrete_model=False)
pre_save_polymorphic.alters_data = True
def save(self, *args, **kwargs):
"""Overridden model save function which supports the polymorphism
functionality (through pre_save_polymorphic)."""
"""Calls :meth:`pre_save_polymorphic` and saves the model."""
using = kwargs.get('using', self._state.db or DEFAULT_DB_ALIAS)
self.pre_save_polymorphic(using=using)
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):
"""
Normally not needed.
Return the actual model type of the object.
If a non-polymorphic manager (like base_objects) has been used to
retrieve objects, then the real class/type of these objects may be
determined using this method.
@ -121,10 +102,10 @@ class PolymorphicModel(six.with_metaclass(PolymorphicModelBase, models.Model)):
return model
def get_real_concrete_instance_class_id(self):
model_class = self.get_real_instance_class()
if model_class is None:
ct = self.get_real_concrete_instance_class()
if ct is 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):
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()
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
retrieve objects, then the complete object with it's real class/type
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()
if real_model == self.__class__:
return self

View File

@ -63,7 +63,7 @@ class PolymorphicQuerySet(QuerySet):
"""
def __init__(self, *args, **kwargs):
"init our queryset object member variables"
# init our queryset object member variables
self.polymorphic_disabled = False
# A parallel structure to django.db.models.query.Query.deferred_loading,
# which we maintain with the untranslated field names passed to
@ -74,7 +74,7 @@ class PolymorphicQuerySet(QuerySet):
super(PolymorphicQuerySet, self).__init__(*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.polymorphic_disabled = self.polymorphic_disabled
new.polymorphic_deferred_loading = (
@ -101,17 +101,17 @@ class PolymorphicQuerySet(QuerySet):
return qs
def instance_of(self, *args):
"""Filter the queryset to only include the classes in args (and their subclasses).
Implementation in _translate_polymorphic_filter_defnition."""
"""Filter the queryset to only include the classes in args (and their subclasses)."""
# Implementation in _translate_polymorphic_filter_defnition.
return self.filter(instance_of=args)
def not_instance_of(self, *args):
"""Filter the queryset to exclude the classes in args (and their subclasses).
Implementation in _translate_polymorphic_filter_defnition."""
"""Filter the queryset to exclude the classes in args (and their subclasses)."""
# Implementation in _translate_polymorphic_filter_defnition."""
return self.filter(not_instance_of=args)
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
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)
@ -413,10 +413,10 @@ class PolymorphicQuerySet(QuerySet):
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).
Here we do the same as:
Here we do the same as::
base_result_objects=list(super(PolymorphicQuerySet, self).iterator())
real_results=self._get_real_instances(base_result_objects)
base_result_objects = list(super(PolymorphicQuerySet, self).iterator())
real_results = self._get_real_instances(base_result_objects)
for o in real_results: yield o
but it requests the objects in chunks from the database,
@ -464,6 +464,17 @@ class PolymorphicQuerySet(QuerySet):
return '[ ' + ',\n '.join(result) + ' ]'
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"
if not base_result_objects:
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 %}
"""