The very basics of the plugin API

This commit is contained in:
Raphael Michel
2014-10-06 23:30:36 +02:00
parent 7ded9e88d8
commit 3c6f8b77cb
17 changed files with 587 additions and 3 deletions

Binary file not shown.

View File

@@ -0,0 +1,10 @@
API details
===========
Contents:
.. toctree::
:maxdepth: 2
plugins
restriction

View File

@@ -0,0 +1,63 @@
.. highlight:: python
:linenothreshold: 5
Plugin basics
=============
It is possible to extend tixl with custom Python code using the official plugin
API. Every plugin has to be implemented as an independent Django 'app' living
either in an own python package either installed like any python module or in
the ``tixlplugins/`` directory of your tixl installation. A plugin may only
require two steps to install:
* Add it to the ``INSTALLED_APPS`` setting of Django in ``tixl/settings.py``
* Perform database migrations by using ``python manage.py migrate``
The communication between tixl and the plugins happens via Django's
`signal dispatcher`_ pattern. The core modules of tixl, ``tixlbase``,
``tixlcontrol`` and ``tixlpresale`` expose a number of signals which are documented
on the next pages.
.. _`pluginsetup`:
Creating a plugin
-----------------
To create a new plugin, create a new python package as a subpackage to ``tixlplugins``.
In order to do so, you can place your module into tixl's :file:`tixlplugins` folder *or
anywhere else in your python import path* inside a folder called ``tixlplugins``.
.. IMPORTANT::
This makes use of a design pattern called `namespace packages`_ which is only
implicitly available as of Python 3.4. As we aim to support Python 3.2 for a bit
longer, you **MUST** put **EXACLTY** the following content into ``tixlplugins/__init__.py``
if you create a new ``tixlplugins`` folder somewhere in your path::
from pkgutil import extend_path
__path__ = extend_path(__path__, __name__)
Otherwise it **will break** on Python 3.2 systems *depending on the python path's order*,
which is not tolerable behaviour. Also, even on Python 3.4 the test runner seems to have
problems without this workaround.
Inside your newly created folder, you'll probably need the three python modules ``__init__.py``,
``models.py`` and ``signals.py``, although this is up to you. You can take the following
example, taken from the time restriction module (see next chapter) as a template for your
``__init__.py`` module::
from django.apps import AppConfig
class TimeRestrictionApp(AppConfig):
name = 'tixlplugins.timerestriction'
verbose_name = "Time restriction"
def ready(self):
from . import signals
default_app_config = 'tixlplugins.timerestriction.TimeRestrictionApp'
.. _signal dispatcher: https://docs.djangoproject.com/en/1.7/topics/signals/
.. _namespace packages: http://legacy.python.org/dev/peps/pep-0420/

View File

@@ -0,0 +1,105 @@
.. highlight:: python
:linenothreshold: 5
Writing a restriction plugin
============================
Please make sure you have read and understood the :ref:`basic idea being tixl's restrictions
<restrictionconcept>`. In this document, we will walk through the creation of a restriction
plugin using the example of a restriction by date and time.
Also, read :ref:`Creating a plugin <pluginsetup>` first.
The restriction model
---------------------
It is very likely that your new restriction plugin needs to store data. In order to do
so, it should define its own model with a name related to what your restriction does,
e.g. ``TimeRestriction``. This model should be a child class of ``tixlbase.models.BaseRestriction``.
You do not need to define custom fields, but you should create at least an empty model.
In our example, we put the following into :file:`tixlplugins/timerestriction/models.py`::
from django.db import models
from django.utils.translation import ugettext_lazy as _
from tixlbase.models import BaseRestriction
class TimeRestriction(BaseRestriction):
"""
This restriction makes an item or variation only available
within a given time frame. The price of the item can be modified
during this time frame.
"""
timeframe_from = models.DateTimeField(
verbose_name=_("Start of time frame"),
)
timeframe_to = models.DateTimeField(
verbose_name=_("End of time frame"),
)
price = models.DecimalField(
null=True, blank=True,
max_digits=7, decimal_places=2,
verbose_name=_("Price in time frame"),
)
The basic signals
-----------------
Availability determination
^^^^^^^^^^^^^^^^^^^^^^^^^^
This is the one signal *every* restriction plugin has to listen for, as your plugin does not
restrict anything without doing so. It is available as ``tixlbase.signals.determine_availability``
and is sent out every time some component of tixl wants to know whether a specific item or
variation is available for sell.
It is sent out with several arguments:
item
The instance of ``tixlbase.models.Item`` in question.
variations
A list of dictionaries in the same format as ``Item.get_all_variations``:
The list contains one dictionary per variation, where the ``Property`` IDs are
keys and the ``PropertyValue`` objects are values. If an ``ItemVariation`` object
exists, it is available in the dictionary via the special key ``'variation'``. If
the item does not have any properties, the list will contain exactly one empty
dictionary. Please not: this is *not* the list of all possible variations, this is
only the list of all variations the frontend likes to determine the status for.
context
A yet-to-defined context object containing information about the user and the order
process. This is required to implement coupon-systems or similar restrictions.
cache
An object very similar to Django's own caching API (see tip below)
All receivers **have to** return a copy of the given list of variation dictionaries where each
dictionary can be extended by the following two keys:
available
A boolean value whether or not this plugin allows this variation to be on sale. Defaults
to ``True``.
price
A price to be set for this variation. Set to ``None`` or omit to keep the default price
of the variation or the item's base price.
.. IMPORTANT::
As this signal might be called *a lot* under heavy load, you are expected to implement
your receiver with an eye to performance. We highly recommend making use of Django's
`caching feature`_. We cannot do this for you, as the possibility of caching highly
depends on the details of your restriction.
**Attention:** Please use the **cache object provided in the signal** instead of importing
it directly from django, so we can take care of invalidation whenever the organizer changes
the event or item settings. Please also **prefix all your cache keys** with your
plugin name.
In our example, the implementation could look like this::
TBD
.. IMPORTANT::
Please note the copying of the ``variations`` list in the example above.
.. _caching feature: https://docs.djangoproject.com/en/1.7/topics/cache/

View File

@@ -1,4 +1,4 @@
Implementation Concepts
Implementation concepts
=======================
Basic terminology
@@ -74,6 +74,8 @@ An item can be extended using **questions**. Questions enable items to be extend
additional information which can be entered by the user. Examples of possible questions
include 'Name' or 'age'.
.. _restrictionconcept:
Restrictions
^^^^^^^^^^^^

View File

@@ -11,3 +11,4 @@ Contents:
setup
style
structure
api/index