This repository has been archived by the owner on Sep 28, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 19
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #100 from postatum/101820116_crud_hooks
Implement CRUD events
- Loading branch information
Showing
15 changed files
with
760 additions
and
36 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,159 @@ | ||
CRUD Events | ||
=========== | ||
|
||
In order to allow users perform custom actions on CRUD request events, nefertari provides CRUD events and hepler functions. | ||
|
||
Nefertari CRUD events module includes a set of events, maps of events, subscriber predicates and helper function to connect it all together. Read further for mode detailed description. | ||
|
||
All the objects are contained in ``nefertari.events`` module. Nefertari CRUD events use Pyramid event system. | ||
|
||
|
||
Events | ||
------ | ||
|
||
``nefertari.events`` defines a set of event classes inherited from ``nefertari.events.RequestEvent``. | ||
|
||
There are two types of nefertari CRUD events: | ||
* "Before" events, which are run after view class is instantiated, but before view method is run, thus before request is processed. | ||
* "After" events, which are run after view method was called. | ||
|
||
All events are named after camel-cased name of view method they are called around and prefixed with "Before" or "After" depending on the place event is triggered from (as described above). E.g. event classed for view method ``update_many`` are called ``BeforeUpdateMany`` and ``AfterUpdateMany``. | ||
|
||
Check API section for a full list of attributes/params events have. | ||
|
||
It's recommended to use ``before`` events to: | ||
* Transform input | ||
* Perform validation | ||
* Apply changes to object that is being affected by request using ``event.set_field_value`` method. | ||
|
||
And ``after`` events to: | ||
* Change DB objects which are not affected by request. | ||
* Perform notifications/logging. | ||
|
||
Complete list of events: | ||
* BeforeIndex | ||
* BeforeShow | ||
* BeforeCreate | ||
* BeforeUpdate | ||
* BeforeReplace | ||
* BeforeDelete | ||
* BeforeUpdateMany | ||
* BeforeDeleteMany | ||
* BeforeItemOptions | ||
* BeforeCollectionOptions | ||
* AfterIndex | ||
* AfterShow | ||
* AfterCreate | ||
* AfterUpdate | ||
* AfterReplace | ||
* AfterDelete | ||
* AfterUpdateMany | ||
* AfterDeleteMany | ||
* AfterItemOptions | ||
* AfterCollectionOptions | ||
|
||
|
||
Predicates | ||
---------- | ||
|
||
Nefertari defines and sets up following subscriber predicates: | ||
|
||
**nefertari.events.ModelClassIs** | ||
Available under ``model`` param when connecting subscribers, it allows to connect subscribers on per-model basis. When subscriber is connected using this predicate, it will only be called when ``view.Model`` is the same class or subclass of this param value. | ||
|
||
**nefertari.events.FieldIsChanged** | ||
Available under ``field`` param when connecting subscribers, it allows to connect subscribers on per-field basis. When subscriber is connected using this predicate, it will only be called when value of this param is present in request JSON body. | ||
|
||
|
||
Utilities | ||
---------- | ||
|
||
**nefertari.events.subscribe_to_events** | ||
Helper function that allows to connect subscriber to multiple events at once. Supports ``model`` and ``field`` subscriber predicate params. Available at ``config.subscribe_to_events``. Subscribers are run in order connected. | ||
|
||
**nefertari.events.BEFORE_EVENTS** | ||
Map of ``{view_method_name: EventClass}`` of "Before" events. E.g. one of its elements is ``'index': BeforeIndex``. | ||
|
||
**nefertari.events.AFTER_EVENTS** | ||
Map of ``{view_method_name: EventClass}`` of "AFter" events. E.g. one of its elements is ``'index': AfterIndex``. | ||
|
||
|
||
|
||
Examples | ||
-------- | ||
|
||
Having subscriber that logs request body: | ||
|
||
.. code-block:: python | ||
import logging | ||
log = logging.getLogger(__name__) | ||
def log_request(event): | ||
log.debug(event.request.body) | ||
**Having access to configurator**, we can connect it to any of nefertari CRUD events. E.g. lets log all collection POST requests (view ``create`` method): | ||
|
||
.. code-block:: python | ||
from nefertari import events | ||
config.subscribe_to_events(log_request, [events.AfterCreate]) | ||
Connected this way ``log_request`` subscriber will be called after every collection POST request. | ||
|
||
In case we want to limit models for which subscriber will be called, we can connect subscriber with a ``model`` predicate: | ||
|
||
.. code-block:: python | ||
from nefertari import events | ||
from .models import User | ||
config.subscribe_to_events( | ||
log_request, [events.AfterCreate], | ||
model=User) | ||
Connected this way ``log_request`` subscriber will only be called when collection POST request comes at endpoint which handles our ``User`` model. | ||
|
||
We can also use ``field`` predicate to make subscriber run only when particular field is present in request JSON body. E.g. if we only want to log collection POST requests for model ``User`` which contain ``first_name`` field, we connect subscriber like so: | ||
|
||
.. code-block:: python | ||
from nefertari import events | ||
from .models import User | ||
config.subscribe_to_events( | ||
log_request, [events.AfterCreate], | ||
model=User, field='first_name') | ||
Predicate ``fields`` can also be used without ``model`` predicate. E.g. if we want to log all POST request bodies of when they have field ``favourite`` we should connect subscriber like so: | ||
|
||
.. code-block:: python | ||
from nefertari import events | ||
from .models import User | ||
config.subscribe_to_events( | ||
log_request, [events.AfterCreate], | ||
field='favourite') | ||
API | ||
--- | ||
|
||
.. autoclass:: nefertari.events.RequestEvent | ||
:members: | ||
:private-members: | ||
|
||
.. autoclass:: nefertari.events.ModelClassIs | ||
:members: | ||
:private-members: | ||
|
||
.. autoclass:: nefertari.events.FieldIsChanged | ||
:members: | ||
:private-members: | ||
|
||
.. autofunction:: nefertari.events.trigger_events | ||
|
||
.. autofunction:: nefertari.events.subscribe_to_events |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,6 +16,7 @@ Table of Content | |
getting_started | ||
views | ||
models | ||
crud_events | ||
auth | ||
making_requests | ||
development_tools | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
def includeme(config): | ||
""" Set up event subscribers. """ | ||
from .models import ( | ||
AuthUserMixin, | ||
random_uuid, | ||
lower_strip, | ||
encrypt_password, | ||
) | ||
from nefertari import events | ||
|
||
subscribe_to = ( | ||
events.BeforeCreate, | ||
events.BeforeUpdate, | ||
events.BeforeReplace, | ||
events.BeforeUpdateMany, | ||
) | ||
|
||
add_sub = config.subscribe_to_events | ||
add_sub(random_uuid, subscribe_to, model=AuthUserMixin, | ||
field='username') | ||
add_sub(lower_strip, subscribe_to, model=AuthUserMixin, | ||
field='username') | ||
add_sub(lower_strip, subscribe_to, model=AuthUserMixin, | ||
field='email') | ||
add_sub(encrypt_password, subscribe_to, model=AuthUserMixin, | ||
field='password') |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.