Table of Contents

Django Deletion Side Effects Documentation

Django Deletion Side Effects provides a framework for gathering side effects of deleting objects in Django.

For example, imagine your website has an interface to delete users. When deleting a user in your app, it will also delete any memberships to groups along with any API credentials of that user. This information would be useful to display before the user is deleted from the website. Django Deletion Side Effects allows you to easily populate this information across your project as it grows.

Setting Up A Side Effect Handler

Similar to registering a signal handler, this package allows you to set up deletion handlers and register them. Each deletion side effect class must define the following:

  1. A deleted_obj_class variable. This variable denotes the class of the object being deleted.
  2. A get_side_effects method. This method is passed a list of objects of deleted_obj_class type that are candidates for deletion. The method returns a tuple of objects that are affected by deletion of the objects for deletion (i.e the side effect objects) and a list of objects that will be cascade deleted if the candidate objects are deleted.
  3. A get_side_effect_message method. This method is passed all of the side effect objects from gathering side effects with the class. The method is responsible for returning a human-readable string of the side effects.

A side effect handler can best be illustrated with an example. Assume we have the following models:

class GroupType(models.Model):
    name = CharField(max_length=64)


class Group(models.Model):
    group_type = models.ForeignKey(GroupType)
    name = CharField(max_length=64)

In this case, a Group will be cascade deleted with the deletion of a GroupType. To set up a side effect for this, do the following:

from deletion_side_effects import register_deletion_side_effects, BaseDeletionSideEffects


class CascadeGroupDeletionSideEffect(BaseDeletionSideEffect):
    deleted_obj_class = GroupType

    def get_side_effects(self, deleted_objs):
        """
        Given a list of deleted group types, return the list of side effect objects
        and the list of objects that will be deleted as a result. In this case, the lists are the same.
        """
        deleted_groups = Group.objects.filter(group_type__in=deleted_objs)
        return deleted_groups, deleted_groups

    def get_side_effect_message(self, side_effect_objs):
        """
        Prints out the message about groups being deleted.
        """
        return u'{0} group{1} will be deleted'.format(len(side_effect_objs), 's' if len(side_effect_objs) > 1 else '')

# Register the side effect. Note that this is best called in the App Config's ready() method
register_deletion_side_effects(CascadeGroupDeletionSideEffect)

In the above example, the side effect class inherits BaseDeletionSideEffects. The side effect handler is registered with the register_deletion_side_effects function. Note that the side effect handlers will need to be connected in the app config’s ready method for your app with side effects.

Gathering Side Effects

In order to gather side effects for a list of deleted objects of the same type, do the following:

from deletion_side_effects import gather_deletion_side_effects


# Find all of the side effects of deleting all group types
side_effects = gather_deletion_side_effects(GroupType, GroupType.objects.all())

print side_effects
[{
    'msg': u'2 groups will be deleted',
    'side_effect_objs': [
        <Group: group1>,
        <Group: group2>
    ]
}]

This case follows with using the models defined in the example above. In this example, we retrieve the side effects of deleting every group type by passing the GroupType model and the iterable of all group types to gather_deletion_side_effects. The return value of the function has a list of all side effects. Each side effect is a dictionary that has a msg field for the side effect message. It also has a list of side effect objects related to the message in the side_effect_objs field.

Installation

To install the latest release, type:

pip install django-deletion-side-effects

To install the latest code directly from source, type:

pip install git+git://github.com/ambitioninc/django-deletion-side-effects.git

Code documentation

deletion_side_effects

class deletion_side_effects.deletion_side_effects.BaseDeletionSideEffects

Provides the interface for a user to make a deletion side effects class. The user must define the following:

  1. A deleted_obj_class variable. This variable denotes the class of the object being deleted.
  2. A ‘get_side_effects` method. This method is passed a list of objects of deleted_obj_class type that are candidates for deletion. The method returns a tuple of objects that are affected by deletion of the objects for deletion (i.e the side effect objects) and a list of objects that will be cascade deleted if the candidate objects are deleted.
  3. A get_side_effect_message method. This method is passed all of the side effect objects from gathering side effects with the class. The method is responsible for returning a human-readable string of he side effects.
get_side_effect_message(side_effect_objects)

Given a list of objects that have this side effect associated with them, return a human readable message about the side effect.

get_side_effects(deleted_objects)

Returns a tuple. The first part of the tuple is list of objects that have side effects associated with them. The second part of the tuple is a list of other objects that will be deleted as a result of the passed objects being deleted. Side effects of other deleted models will be populated when gather_deletion_side_effects is called.

deletion_side_effects.deletion_side_effects.gather_deletion_side_effects(obj_class, objs)

Given an object, gather the side effects of deleting it. The return value is a list of dictionaries, each of which contain the following keys:

  1. msg - This key contains a human-readable message of the side effect.
  2. side_effect_objs: This key contains a list of ever object related to this side effect and the message.
deletion_side_effects.deletion_side_effects.register_deletion_side_effects(*deletion_side_effects_handlers)

Registers deletion side effect handler classes. The class must inherit BaseDeletionSideEffects and define a deleted_obj_class variable.

Contributing

Contributions and issues are most welcome! All issues and pull requests are handled through github on the ambitioninc repository. Also, please check for any existing issues before filing a new one. If you have a great idea but it involves big changes, please file a ticket before making a pull request! We want to make sure you don’t spend your time coding something that might not fit the scope of the project.

Running the tests

To get the source source code and run the unit tests, run:

git clone git://github.com/ambitioninc/django-deletion-side-effects.git
cd django-deletion-side-effects
virtualenv env
. env/bin/activate
python setup.py install
coverage run setup.py test
coverage report --fail-under=100

While 100% code coverage does not make a library bug-free, it significantly reduces the number of easily caught bugs! Please make sure coverage is at 100% before submitting a pull request!

Code Quality

For code quality, please run flake8:

pip install flake8
flake8 .

Code Styling

Please arrange imports with the following style

# Standard library imports
import os

# Third party package imports
from mock import patch
from django.conf import settings

# Local package imports
from deletion_side_effects.version import __version__

Please follow Google’s python style guide wherever possible.

Building the docs

When in the project directory:

pip install -r requirements/docs.txt
python setup.py build_sphinx
open docs/_build/html/index.html

Release Checklist

Before a new release, please go through the following checklist:

  • Bump version in deletion_side_effects/version.py

  • Add a release note in docs/release_notes.rst

  • Git tag the version

  • Upload to pypi:

    pip install wheel
    python setup.py sdist bdist_wheel upload
    

Uploading to pypi can be accomplished by running python publish.py

Vulnerability Reporting

For any security issues, please do NOT file an issue or pull request on github! Please contact security@ambition.com with the GPG key provided on Ambition’s website.

Release Notes

v0.1.1

  • Changed the interface for registering deletion side effects

v0.1.0

  • This is the initial release of django-deletion-side-effects.