django-ib-cache 0.0.5
Django cache application
License: MIT
Requires Python: >=3.6

Django-cache
============

|PyPI version| |python version| |license| |Code Health| |Coverage| |Project Status| |downloads|

**Django-cache** implement configurable caching mechanics.

Minimal start
-------------

Installation:

.. code:: shell

    $ pip install django-cache

Register application in settings:

.. code:: python

    INSTALLED_APPS = [
        "django_cache",
    ]

Run migrations:

.. code:: shell

    $ python manage.py migrate

Initialize worker:

.. code:: python

    from django_cache.contrib import CacheWorker

    from my_application import get_all_foo_list

    all_foos_cache = CacheWorker(
        label="all_foos",  # Unique cache worker label
        structure_getter=get_all_foo_list,  # Function which generate cache value
        expires=100000,  # Cache live in seconds
        delay_invalidation=True
    )

or in settings:

.. code:: python

    DJANGO_CACHE_WORKERS = {
        "all_foos": {
            "label": "",
            "structure_getter": "my_application.get_all_foo_list",
            "expires": 100000,
            "delay_invalidation": True,
        }
    }

**NOTE**: Be careful with circle import while using settings declaration.

Use caching value in your code:

.. code:: python

    from django_cache.shortcuts import get_cache

    def get_foos(requests):
        ...
        foos =  get_cache("all_foos")
        ...

Worker parameters
-----------------

* ``structure_getter`` - [Callable[..., Any]] Function or something callable which create cache value, must receive serializable arguments, which can be converted in string presentation.
* ``label`` - [str] Unique caching worker label.
* ``expires`` - [int] Cache key live time.
* ``key_gen`` - [Not required] Function which generate key by getting arguments.
* ``cached_entity`` - [Not required][bool] Default False. Will return CacheEntity as cache value.
* ``tick_amount`` - [Not required][int] Default 10. Count of ticks while concurrent getting cache value.
* ``tick`` - [Not required][float/int] Default 0,1. Tick size in seconds.
* ``relevance_invalidation`` - [Not required][bool] Default False. Enable invalidation by relevance.
* ``relevance_expires`` - [Not required][int] Default 60. Cache value relevance time in seconds.
* ``delay_logging`` - [Not required][bool] Default False. Run CreatedCache object creation in delay celery task.
* ``is_concurrent`` - [Not required][bool] Default True. Enable concurrent cache getting mechanic.

You can change global default value in settings:

* ``DJANGO_CACHE_DEFAULT_TICK_AMOUNT``
* ``DJANGO_CACHE_DEFAULT_TICK_SIZE``
* ``DJANGO_CACHE_DEFAULT_KEYGEN``
* ``DJANGO_CACHE_DEFAULT_EXPIRES``
* ``DJANGO_CACHE_DEFAULT_DELAY_INVALIDATION``
* ``DJANGO_CACHE_DEFAULT_RELEVANCE_INVALIDATION``
* ``DJANGO_CACHE_DEFAULT_RELEVANCE_EXPIRES``
* ``DJANGO_CACHE_DEFAULT_DELAY_COUNTDOWN``
* ``DJANGO_CACHE_DEFAULT_DELAY_LOGGING``
* ``DJANGO_CACHE_IS_CONCURRENT``

Automatic invalidation
----------------------

For automatic invalidation you must initialize invalidation arguments getters.

Change your model:

.. code:: python

    from django.db import models

    from model_subscription.models import SubscriptionModelMixin, SubscriptionQuerySet


    class Foo(SubscriptionModelMixin, models.Model):
        attr1 = models.IntegerField()
        attr2 = models.CharField(max_length=255)
        attr3 = models.FloatField(null=True, blank=True)

        objects = SubscriptionQuerySet.as_manager()

Configure invalidation:

.. code:: python

    from django_cache.contrib import CacheWorker, automatic
    from django_cache.contrib.automatic import (
        default_outdated_getter, default_newcomers_getter
    )

    from my_application.models import Foo


    # Getter without arguments
    def get_all_foo_list():
        return Foo.objects.all()


    all_foos_cache = CacheWorker(
        label="all_foos",  # Unique cache worker label
        structure_getter=get_all_foo_list,  # Function which generation cache value
        expires=100000,  # Cache live in seconds
        delay_invalidation=True
    )


    # Filtering by arguments
    def filter_foos(attr1, attr2, **kwargs):
        return Foo.objects.filter(attr1=attr1, attr2=attr2)


    filtered_foos = CacheWorker(
        label="filtered_foos",  # Unique cache worker label
        structure_getter=filter_foos,  # Function which generation cache value
        expires=100000,  # Cache live in seconds
        delay_invalidation=True
    )


    def filtered_foos_outdated_getter(instance: Foo, attrs: Dict) -> Dict:
        default_attrs = default_outdated_getter()
        return {
            "attr1": default_attrs.get("attr1"),
            "attr2": default_attrs.get("attr2"),
        }


    def filtered_foos_newcomers_getter(instance: Foo, attrs: Dict) -> Dict:
        default_attrs = default_newcomers_getter()
        return {
            "attr1": default_attrs.get("attr1"),
            "attr2": default_attrs.get("attr2"),
        }


    automatic.register = automatic.register(
        Foo, {
            "all_foos": {"is_empty": True},
            "filtered_foos": {
                # Callable or string (path to callable)
                "instance_getter": lambda instance: {
                    "attr1": instance.attr1, "attr2": instance.attr2
                },
                # Callable or string (path to callable)
                "outdated_getter": filtered_foos_outdated_getter,
                "newcomers_getter": filtered_foos_newcomers_getter,
            }
        }
    )

NOTES
-----

* If you are using delay invalidation with celery, be careful with cache backend. Memcache has two different instances in celery and django, so using redis or rabbitmq backends.

* If you initialize cache worker using ``django_cache.contrib.CacheWorker``, this module must me received by application.

.. |PyPI version| image:: https://badge.fury.io/py/django-ib-cache.svg
   :target: https://badge.fury.io/py/django-ib-cache
.. |python version| image:: https://img.shields.io/pypi/pyversions/django-ib-cache
   :target: https://pypi.org/project/django-ib-cache
.. |license| image:: https://img.shields.io/pypi/l/django-ib-cache.svg
   :alt: Software license
   :target: https://github.com/Cguilliman/django-cache/blob/master/LICENSE
.. |Code Health| image:: https://app.codacy.com/project/badge/Grade/c407b57a01ed4b4eac16bc91620d403b
   :target: https://www.codacy.com/gh/Cguilliman/django-cache
.. |Coverage| image:: https://app.codacy.com/project/badge/Coverage/c407b57a01ed4b4eac16bc91620d403b
   :target: https://www.codacy.com/gh/Cguilliman/django-cache/dashboard
   :alt: Code coverage
.. |Project Status| image:: https://img.shields.io/pypi/status/django-ib-cache.svg
   :target: https://pypi.org/project/django-ib-cache/
   :alt: Project Status
.. |downloads| image:: https://img.shields.io/pypi/dm/django-ib-cache.svg
   :alt: Project Status