mirror of
https://github.com/pretix/pretix.git
synced 2026-08-08 10:27:49 +00:00
Add documentation
This commit is contained in:
@@ -0,0 +1,61 @@
|
||||
.. highlight:: python
|
||||
:linenothreshold: 5
|
||||
|
||||
Data sync providers
|
||||
===================
|
||||
|
||||
Pretix provides connectivity to many external services through plugins. A common requirement
|
||||
is unidirectionally sending (order, customer, ticket, ...) data into external systems.
|
||||
The transfer is usually triggered by signals provided by pretix core (e.g. order_created),
|
||||
but performed asynchronously.
|
||||
|
||||
Such plugins should use the :class:`OutboundSyncProvider` API to utilize the queueing, retry and mapping mechanisms as well as the user interface for configuration and monitoring.
|
||||
|
||||
An :class:`OutboundSyncProvider` for registering event participants in a mailing list could start
|
||||
like this, for example:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
class MyListSyncProvider(OutboundSyncProvider):
|
||||
identifier = "my_list"
|
||||
# ...
|
||||
|
||||
|
||||
|
||||
The plugin must register listeners in `signals.py` for all signals that should to trigger a sync and
|
||||
within it has to call `MyListSyncProvider.enqueue_order` to enqueue the order for synchronization:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
@receiver(order_placed, dispatch_uid="mylist_order_placed")
|
||||
def on_order_placed(sender, order, **kwargs):
|
||||
MyListSyncProvider.enqueue_order(order, "order_placed")
|
||||
|
||||
|
||||
|
||||
|
||||
Furthermore, most of these plugins need to transfer data from some pretix objects (e.g. orders)
|
||||
into an external systems' data structures. Sometimes, there is only one reasonable way or the
|
||||
plugin author makes an opinionated decision what information from which objects should be
|
||||
transferred into which data structures in the external system.
|
||||
|
||||
Otherwise, you can use a `PropertyMappingFormSet` to let the user set up a mapping from pretix model fields
|
||||
to external data fields. You could store the mapping information either in the Event settings, or in a separate
|
||||
data model. Your implementation of OutboundSyncProvider.mappings needs to provide a list of Mappings, with at least
|
||||
the properties defined in :class:`pretix.base.datasync.datasync.StaticMapping`.
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
# class MyListSyncProvider, contd.
|
||||
def mappings(self):
|
||||
return [
|
||||
StaticMapping(1, 'Order', 'Contact', 'email', 'email',
|
||||
self.event.settings.mylist_order_mapping))
|
||||
]
|
||||
|
||||
|
||||
Currently, we support Orders and OrderPositions as data sources, with the data fields defined in
|
||||
:func:`pretix.base.datasync.sourcefields.get_data_fields`.
|
||||
|
||||
|
||||
|
||||
@@ -18,5 +18,6 @@ Contents:
|
||||
customview
|
||||
cookieconsent
|
||||
auth
|
||||
datasync
|
||||
general
|
||||
quality
|
||||
|
||||
@@ -144,8 +144,19 @@ class OutboundSyncProvider:
|
||||
def display_name(cls):
|
||||
return str(cls.identifier)
|
||||
|
||||
"""
|
||||
Adds an order to the sync queue. May only be called on derived classes which define an "identifier" attribute.
|
||||
|
||||
Should be called in the appropriate signal receivers, e.g.:
|
||||
|
||||
@receiver(order_placed, dispatch_uid="mysync_order_placed")
|
||||
def on_order_placed(sender, order, **kwargs):
|
||||
MySyncProvider.enqueue_order(order, "order_placed")
|
||||
"""
|
||||
@classmethod
|
||||
def enqueue_order(cls, order, triggered_by, not_before=None):
|
||||
if not hasattr(cls, 'identifier'):
|
||||
raise TypeError('Call this method on a derived class that defines an "identifier" attribute.')
|
||||
OrderSyncQueue.objects.create(
|
||||
order=order,
|
||||
sync_provider=cls.identifier,
|
||||
@@ -247,6 +258,30 @@ class OutboundSyncProvider:
|
||||
for m in property_mapping
|
||||
]
|
||||
|
||||
"""
|
||||
This method is called for each object that needs to be created/updated in the external system -- which these are is
|
||||
determined by the implementation of the `mapping` property.
|
||||
|
||||
TODO: describe the parameters
|
||||
|
||||
This method needs to be idempotent, i.e. calling it multiple times with the same input values should create
|
||||
only a single object in the target system.
|
||||
|
||||
Subsequent calls with the same mapping and pk_value should update the existing object, instead of creating a new one.
|
||||
In a SQL database, you might use an "INSERT OR UPDATE" or "UPSERT" statement; many REST APIs provide an equivalent API call.
|
||||
"""
|
||||
def sync_object_with_properties(
|
||||
self,
|
||||
pk_field,
|
||||
pk_value,
|
||||
properties: list,
|
||||
inputs: dict,
|
||||
mapping,
|
||||
mapped_objects: dict,
|
||||
**kwargs,
|
||||
):
|
||||
raise NotImplementedError()
|
||||
|
||||
def sync_object(
|
||||
self,
|
||||
inputs: dict,
|
||||
@@ -261,7 +296,14 @@ class OutboundSyncProvider:
|
||||
if not pk_value:
|
||||
return None
|
||||
|
||||
info = self.sync_object_with_properties(inputs, mapping, mapped_objects, pk_value, properties)
|
||||
info = self.sync_object_with_properties(
|
||||
pk_field=mapping.external_pk,
|
||||
pk_value=pk_value,
|
||||
properties=properties,
|
||||
inputs=inputs,
|
||||
mapping=mapping,
|
||||
mapped_objects=mapped_objects,
|
||||
)
|
||||
OrderSyncLink.objects.create(
|
||||
order=inputs.get(ORDER), order_position=inputs.get(ORDER_POSITION), sync_provider=self.identifier,
|
||||
external_object_type=info.get('object_type'),
|
||||
|
||||
Reference in New Issue
Block a user