# -*- coding: utf-8 -*-
from setuptools import setup

packages = \
['django_models_redis_cache']

package_data = \
{'': ['*']}

install_requires = \
['Django>=3.0,<4.0', 'redis>=3.5.3,<4.0.0']

setup_kwargs = {
    'name': 'django-models-redis-cache',
    'version': '3.8.6',
    'description': 'Django Models Redis Cache (DMoReCa), library that gives your specified Django models regular caching via Redis',
    'long_description': '# django-models-redis-cache\n\n## **Django Models Redis Cache (DMoReCa), library that gives your specified Django models regular caching via Redis**\n\nFor one project, I needed to work with redis, but redis-py provides a minimum level of work with redis. I didn\'t find any Django-like ORM for redis, so I wrote library [python-redis-orm](https://github.com/gh0st-work/python_redis_orm/) ([PyPI](https://pypi.org/project/python-redis-orm/)).\n\nAlso, if you are searching for just django-like redis ORM, please check [django-redis-orm](https://github.com/gh0st-work/django_redis_orm/) ([PyPI](https://pypi.org/project/django-redis-orm/)).\n\n**And this library is port to Django that provides easy-to-use Django models caching via Redis**.\n\n### Working with this library, you are expected:\n\n- Fully works in 2021\n- Saving almost all types of fields automatically\n- Efficient data storage (SET model_name:instance_id "JSON string")\n- Async caching\n- Connection pooling\n- Easy adaptation to your needs\n- Adequate informational messages and error messages\n- Built-in RedisRoot class that stores specified models, with (optional):\n    - Async database limit\n    - Ignoring deserialization errors\n    - Use structure-first data\n- Customizing caching settings by model (optional):\n    - Cache every X seconds\n    - Save related models\n    - Fields to exclude from caching\n    - Filter objects to cache\n- CRUD (Create Read Update Delete), that uses your django models\n\n# Installation\n`pip install django-models-redis-cache`\n\n[Here is PyPI](https://pypi.org/project/django-models-redis-cache/)\n\nAdd "django_models_redis_cache" to your INSTALLED_APPS setting like this::\n\n    INSTALLED_APPS = [\n        ...\n        \'django_models_redis_cache\',\n    ]\n\n# Usage\n\n1. Create **RedisRoot** with params:\n    - **prefix** (str) - prefix for your redis root\n    - **connection_pool** (redis.ConnectionPool) - redis-py redis.ConnectionPool instance, with decode_responses=True\n    - **async_db_requests_limit** (int) - your database has max connections limit, please enter it here\n    - **ignore_deserialization_errors** (bool) - to ignore deserialization errors or raise exception\n    - **economy** (bool) - if True, all update requests will return only instance id \n2. Call **register_django_models({...})** on your RedisRoot instance and provide dict, where keys are django models and values are dicts (django_model:dict) with config params (str:value):\n    - **enabled** (bool) - to cache or not\n    - **ttl** (int) - to cache every x seconds\n    - **save_related_models** (bool) - to save ForeignKey-s and ManyToMany-s instances or not\n    - **exclude_fields** (list of strings) - fields to exclude from caching\n    - **filter_by** (dict str:value) - filter objects to cache by something\n    - **delete** (bool) - foolproof\n    - **write_to_django** (bool) - if you need to write data to django, also uses as foolproof\n3. Call **check_cache()** on your RedisRoot instance\n4. Use our CRUD, or just get your cached data\n\n# Example usage\n\n### Settings\n\nYou can set this part in your project settings.py:\n\n```python\nfrom django_models_redis_cache.core import *\n\n\ndef get_connection_pool():\n    host = \'localhost\'\n    port = 6379\n    db = 0\n    connection_pool = redis.ConnectionPool(\n        decode_responses=True,\n        host=host,\n        port=port,\n        db=db,\n    )\n    return connection_pool\n\n\nREDIS_ROOTS = {\n    \'test_caching_root\': RedisRoot(\n        prefix=\'test_caching\',\n        connection_pool=get_connection_pool(),\n        async_db_requests_limit=100,\n        ignore_deserialization_errors=True,\n        save_consistency=False,\n        economy=True\n    )\n}\n\n```\n\n### Run in the background\n\nYou can just copy it to:\n\n`app/management/commands/command_name.py`\n    \nAnd just run with:\n\n`python manage.py command_name`\n    \nHelp:\n\n[Django custom management commands](https://docs.djangoproject.com/en/3.2/howto/custom-management-commands/)\n    \n[How to import something from settings](https://stackoverflow.com/a/14617309)\n\n```python\n\nredis_roots = getattr(settings, "REDIS_ROOTS", None)\nif redis_roots:\n    if type(redis_roots) == dict:\n        test_caching_root = redis_roots[\'test_caching_root\']\n        test_caching_root.register_django_models({\n            CustomUser: {\n                \'enabled\': True,\n                \'ttl\': 60 * 15,\n                \'save_related_models\': True,\n                \'delete\': True,\n                \'write_to_django\': True,\n                \'exclude_fields\': [\n                    \'is_admin\',\n                    \'api_key\',\n                    \'first_name\',\n                    \'last_name\',\n                    \'email\',\n                    \'is_staff\',\n                    \'date_joined\',\n                    \'password\',\n                    \'last_login\',\n                    \'user_permissions\',\n                    \'is_superuser\',\n                    \'groups\',\n                ],\n            },\n            BotSoft: {\n                \'enabled\': True,\n                \'ttl\': 60 * 15,\n                \'save_related_models\': True,\n                \'delete\': True,\n                \'write_to_django\': True,\n                \'exclude_fields\': [\n                    \'name\',\n                    \'image\',\n                    \'image_webp\',\n                    \'developer_url\'\n                ],\n            },\n            Service: {\n                \'enabled\': True,\n                \'ttl\': 60 * 15,\n                \'save_related_models\': True,\n                \'delete\': True,\n                \'write_to_django\': True,\n                \'exclude_fields\': [\n                    \'name_append\',\n                    \'description\',\n                    \'min\',\n                    \'max\',\n                ],\n            },\n            CustomService: {\n                \'enabled\': True,\n                \'ttl\': 60 * 15,\n                \'save_related_models\': True,\n                \'delete\': True,\n                \'write_to_django\': True,\n                \'exclude_fields\': [\n                    \'name_append\',\n                ],\n            },\n            UniqueTask: {\n                \'enabled\': True,\n                \'ttl\': 60 * 5,\n                \'save_related_models\': True,\n                \'delete\': True,\n                \'write_to_django\': True,\n            },\n            Task: {\n                \'enabled\': True,\n                \'ttl\': 60 * 5,\n                \'save_related_models\': True,\n                \'delete\': True,\n                \'write_to_django\': True,\n                \'filter_by\': {\n                    \'status\': \'in_work\',\n                }\n            },\n            Account: {\n                \'enabled\': True,\n                \'ttl\': 60 * 5,\n                \'save_related_models\': True,\n                \'delete\': True,\n                \'write_to_django\': True,\n                \'filter_by\': {\n                    \'last_task_completed_in__gte\': datetime.datetime.now() - datetime.timedelta(days=14),\n                    \'last_checked_in__gte\': datetime.datetime.now() - datetime.timedelta(days=14),\n                }\n            },\n            BotSession: {\n                \'enabled\': True,\n                \'ttl\': 60 * 5,\n                \'save_related_models\': True,\n                \'delete\': True,\n                \'write_to_django\': True,\n            },\n            TaskChallenge: {\n                \'enabled\': True,\n                \'ttl\': 60 * 1,\n                \'save_related_models\': True,\n                \'delete\': True,\n                \'write_to_django\': True,\n            },\n        })\n        roots_to_cache = [\n            test_caching_root,\n        ]\n        print(\'STARTING CACHING\')\n        while True:\n            for redis_root in roots_to_cache:\n                redis_root.check_cache()\n    else:\n        raise Exception(\'redis_roots must be dict\')\nelse:\n    raise Exception(\'No redis_roots\')\n\n```\n\n\n### Use in views\n\nIf you enabled write_to_django and delete, you can fully use redis caching and does not care about writing to the database with \n\n**our CRUD**:\n\n```python\n\n    # Django part\n    gh0st_user = CustomUser.objects.get(username=\'gh0st\')\n    another_user = CustomUser.objects.get(username=\'another_username\')\n    random_service = random.choice(list(Service.objects.all()))\n    placement = random_service.placement\n    if ServiceCustomPrice.objects.filter(user=gh0st_user, service=random_service, active=True):\n        placement = ServiceCustomPrice.objects.get(user=gh0st_user, service=random_service, active=True).price\n    if gh0st_user.sale:\n        placement = placement * gh0st_user.sale\n    task_count = 9999\n    task_price = task_count * placement\n    new_task_1_params = {\n        \'owner\': gh0st_user,\n        \'service\': random_service,\n        \'url\': \'https://github.com/gh0st-work/\',\n        \'count\': task_count,\n        \'price\': task_price,\n        \'status\': \'checking\',\n    }\n    new_task_1 = Task.objects.create(**new_task_1_params)\n    new_task_2_params = {\n        \'owner\': another_user,\n        \'service\': random_service,\n        \'url\': \'https://github.com/gh0st-work/\',\n        \'count\': task_count,\n        \'price\': task_price,\n        \'status\': \'checking\',\n    }\n    new_task_2 = Task.objects.create(**new_task_2_params)\n    \n    \n    \n    # Cache part\n    # Preparations\n    test_caching_root.check_cache()  # Just for testing, if it not runned in the background and not caching right now\n    \n    # Get\n    cached_task_1 = test_caching_root.get(Task, django_id=new_task_1.id)  # filter by django_id, if you leave blank will return all instances\n    print(\'\\n\\n\\n\')\n    cached_task_2 = test_caching_root.get(Task, django_id=new_task_2.id)  # filter by django_id, if you leave blank will return all instances\n    success = False\n    try:\n        cached_task_1 = cached_task_1[0]\n        cached_task_2 = cached_task_2[0]\n        if cached_task_1[\'owner\'][\'django_id\'] == new_task_1.owner.id:\n            if cached_task_1[\'price\'] == new_task_1.price:\n                if cached_task_2[\'owner\'][\'django_id\'] == new_task_2.owner.id:\n                    if cached_task_2[\'price\'] == new_task_2.price:\n                        success = True\n    except:\n        pass\n    print(f\'Get test: {success = }\')  # If create works, will print: "Get test: success = True"\n\n    # Create and deep filtering\n    new_task_params = new_task_1_params\n    new_task_params[\'owner\'] = test_caching_root.get(CustomUser, username=\'gh0st\')\n    new_task_params[\'service\'] = test_caching_root.get(Service, django_id=random_service.id)\n    created_task = test_caching_root.create(Task, **new_task_params)\n    cached_gh0st_tasks = test_caching_root.get(Task, owner__username=\'gh0st\')\n    all_tasks_owner_is_gh0st = all([\n        (task[\'owner\'][\'username\'] == \'gh0st\')\n        for task in cached_gh0st_tasks\n    ])\n    task_created = (created_task in cached_gh0st_tasks)\n    success = (all_tasks_owner_is_gh0st and task_created)\n    print(f\'Create and deep filtering test: {success = }\')  # If works, will print: "Create and deep filtering test: success = True"\n\n    # Update\n    random_price_first_part = Decimal(random.randrange(0, 10000))\n    random_price_second_part = Decimal(random.randrange(0, 1000))\n    random_price = random_price_first_part + random_price_second_part / Decimal(1000)\n    test_caching_root.update(\n        Task, cached_task_2,\n        price=random_price,\n        status=\'completed\',\n    )\n    cached_task_2 = test_caching_root.get(\n        Task,\n        price=random_price,  # filter by price, if you leave blank will return all instances\n        status__in=[\'checking\', \'completed\'],  # if status in the list\n    )\n    success = False\n    try:\n        cached_task_2 = cached_task_2[0]\n        price_is_same = (cached_task_2[\'price\'] == random_price)\n        django_id_is_same = (cached_task_2[\'django_id\'] == new_task_2.id)\n        status_is_completed = (cached_task_2[\'status\'] == \'completed\')\n        if price_is_same and django_id_is_same and status_is_completed:\n            success = True\n    except:\n        pass\n    print(f\'Update test: {success = }\')  # If works, will print: "Update test: success = True"\n\n    # Delete\n    test_caching_root.delete(Task, cached_task_2)\n    old_cached_task_2 = test_caching_root.get(Task, price=random_price)\n    success = (not len(old_cached_task_2))\n    print(f\'Delete test: {success = }\')  # If works, will print: "Delete test: success = True"\n\n\n```\n',
    'author': 'Anton Nechaev',
    'author_email': 'antonnechaev990@gmail.com',
    'maintainer': None,
    'maintainer_email': None,
    'url': 'https://github.com/gh0st-work/django_models_redis_cache',
    'packages': packages,
    'package_data': package_data,
    'install_requires': install_requires,
    'python_requires': '>=3.6,<4.0',
}


setup(**setup_kwargs)
