Metadata-Version: 2.1
Name: django_lockable
Version: 1.1.3
Summary: django-lockable provides ``Lockable``, a base django model that allow to forbid some fields to be updated
Home-page: https://gitlab.com/magency/products/mtp-back-modules/-/tree/master/django-lockable
Author: Stephane "Twidi" Angel
Author-email: s.angel@twidi.com
License: BSD
Description: ===============
        Django Lockable
        ===============
        
        Purpose
        =======
        
        The ``django-lockable`` app provides a ``Lockable`` model mixin allowing to "lock" a model by
        denying the update of its fields under certain conditions to be defined in the models using it.
        
        About
        =====
        
        How it works
        ************
        
        When a model inherits from the ``Lockable`` mixin, automatically all its fields will be locked
        against update, as well as ``ManyToMany`` relations.
        
        This is done, for normal fields, by an override of the attribute setter (the ``__setattr__``
        method), and for ``ManyToMany``, by catching the ``m2m_changed`` signal ("pre*" actions).
        
        Then, the ``is_field_locked`` method of the model is called, with the name of the field to update
        as argument (for a ``ManyToManyField`` that points to the current model, it will be the
        ``related_name`` of this field).
        
        This method will:
        
        - return ``False`` if the model is currently being loaded (ie during the execution of the
          ``__init__`` and ``refresh_from_db`` methods)
        - return ``False`` if the field is "not lockable" (see below)
        - return ``True`` in all other cases (It's here that you can override the logic)
        
        
        Exclude fields
        **************
        
        To exclude some fields from the lockable ones, simply add their name to ``non_lockable_fields``
        attribute of the model. It's a list (or other iterable), and when a field name is present in it,
        the ``is_field_locked`` will always return ``False`` when called with this name.
        
        Note that this list does not need to includes fields to ignore already defined in parent classes:
        
        .. code-block:: python
        
            class ParentModel(Lockable):
                field1 = models.CharField()
                field2 = models.CharField()
        
                non_lockable_fields = ['field2']
        
            class ChildModel(ParentModel):
                field3 = models.CharField()
                field4 = models.CharField()
        
                non_lockable_fields = ['field4']  # ``field2`` is already included
        
        
        Specific logic
        **************
        
        To add some logic to decide when to lock or not a model, simply override ``is_field_locked`` by
        first calling ``super``, and if the result is ``True``, manage your own logic:
        
        .. code-block:: python
        
            class MyModel(Lockable):
                """Example that lock the model when a flag is True."""
        
                is_published = models.BooleanField()
                field = models.CharField()
        
                def is_field_locked(self, field_name):
                    locked = super().is_field_locked(field_name)
        
                    if locked:
                        locked = self.is_published
        
                    return locked
        
        
        Update a locked model
        *********************
        
        In some case you may want to update a locked model, for example in a shell to debug something, or
        in tests.
        
        The ``django_lockable.utils`` module provides a context manager, ``_no_fields_locking`` for this,
        and it's what is used to manage the initialization of a object (``__ini__`` and
        ``__refresh_from_db__``).
        
        It's really not recommended to use it in normal code as it's in violation of the whole principle of
        this app. It's why it is prefixed with a ``_`` to mark is a private, but it's documented because of
        its usefulness in some cases.
        
        Use it this way:
        
        .. code-block:: python
        
            # Will deactivate locking for the instance only
            with _no_fields_locking(instance):
                instance.foo = 'bar'
        
            # Will deactivate locking for all instances of the model
            with _no_fields_locking(MyLokableModel):
                instance.foo = 'bar'
        
            # Will deactivate locking for all instances of all lockable models
            with _no_fields_locking(Lockable):
                instance.foo = 'bar'
        
        
        Installation
        ============
        
        The ``django-lockable`` package is only available on the Magency private pypi server.
        
        .. code-block:: sh
        
            pip install -i https://login:password@pypi.magency.ninja/some/index django-lockable
        
        
        Developement
        ============
        
        
        The code can be found on the `Magency mtp-back-modules repository
        <https://gitlab.com/magency/products/mtp-back-modules/-/tree/master/django-lockable>`_
        
        Install the required packages:
        
        .. code-block:: sh
        
            pip install -i https://login:password@pypi.magency.ninja/some/index -e .[dev]
        
        
        
        To run tests, simply launch the ``runtests.sh`` script.
        
        And for pylint:
        
        .. code-block:: sh
        
            PYTHONPATH="$PYTHONPATH:." pylint django_lockable tests
        
        
        When ready, update the version in ``setup.cfg`` then create the package:
        
        .. code-block:: sh
        
            ./setup.py sdist bdist_wheel
        
        You can now upload it to ``devpi``:
        
        .. code-block:: sh
        
            devpi use https://login:password@pypi.magency.ninja
            devpi login yourlogin
            devpi use yourlogin/dev
            devpi upload dist/django_lockable-VERSION*
        
        
        Support
        =======
        
        python>=3.6
        django>=2.2
        
Platform: UNKNOWN
Classifier: Development Status :: 5 - Production/Stable
Classifier: Operating System :: OS Independent
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: BSD License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.6
Classifier: Topic :: Software Development :: Libraries
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Framework :: Django
Classifier: Framework :: Django :: 2.2
Provides-Extra: dev
