{"id":24297136,"url":"https://github.com/gh0st-work/python_redis_orm","last_synced_at":"2025-09-25T23:30:33.015Z","repository":{"id":57457468,"uuid":"402199740","full_name":"gh0st-work/python_redis_orm","owner":"gh0st-work","description":"Python Redis ORM library that gives redis easy-to-use objects with fields and speeds a development up, inspired by Django ORM","archived":false,"fork":false,"pushed_at":"2021-09-21T23:30:14.000Z","size":2023,"stargazers_count":13,"open_issues_count":0,"forks_count":2,"subscribers_count":1,"default_branch":"master","last_synced_at":"2024-10-11T09:12:05.537Z","etag":null,"topics":["database","django","models","orm","python","redis"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/gh0st-work.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2021-09-01T20:51:32.000Z","updated_at":"2024-09-27T22:52:31.000Z","dependencies_parsed_at":"2022-09-06T02:00:42.070Z","dependency_job_id":null,"html_url":"https://github.com/gh0st-work/python_redis_orm","commit_stats":null,"previous_names":[],"tags_count":5,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gh0st-work%2Fpython_redis_orm","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gh0st-work%2Fpython_redis_orm/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gh0st-work%2Fpython_redis_orm/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/gh0st-work%2Fpython_redis_orm/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/gh0st-work","download_url":"https://codeload.github.com/gh0st-work/python_redis_orm/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":234261809,"owners_count":18804615,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["database","django","models","orm","python","redis"],"created_at":"2025-01-16T19:51:25.883Z","updated_at":"2025-09-25T23:30:27.583Z","avatar_url":"https://github.com/gh0st-work.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# python-redis-orm\n\n## **Python Redis ORM library that gives redis easy-to-use objects with fields and speeds a development up, inspired by Django ORM**\n\n\n[![Full test](https://github.com/gh0st-work/python_redis_orm/actions/workflows/python-app.yml/badge.svg?event=push)](https://github.com/gh0st-work/python_redis_orm/actions/workflows/python-app.yml)\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 this library, then there will be a port to Django.\n\n### Working with this library, you are expected:\n\n- Fully works in 2021\n- Django-like architecture\n- Easy adaptation to your needs\n- Adequate informational messages and error messages\n- Built-in RedisRoot class that stores specified models, with:\n    - **redis_instance** setting - your redis connection (from redis-py)\n    - **prefix** setting - prefix of this RedisRoot to be stored in redis\n    - **ignore_deserialization_errors** setting - do not raise errors, while deserializing data\n    - **save_consistency** setting - show structure-first data\n    - **economy** setting - to not return full data and save some requests (usually, speeds up your app on 80%)\n- 13 built-in types of fields:\n    - **RedisField** - base class for nesting\n    - **RedisString** - string\n    - **RedisNumber** - int or float\n    - **RedisId** - instances IDs\n    - **RedisBool** - bool\n    - **RedisDecimal** - working accurately with numbers via decimal\n    - **RedisJson** - for data, that can be JSONed\n    - **RedisList** - list\n    - **RedisDict** - dict\n    - **RedisDateTime** - for work with date and time, via python datetime.datetime\n    - **RedisDate** - for work with date, via python datetime.data\n    - **RedisForeignKey** - for link to other instance\n    - **RedisManyToMany** - for links to other instances\n- All fields supports:\n    - Automatically serialization\n    - Automatically deserialization\n    - TTL (Time To Live) setting\n    - Default values\n    - Providing functions without call, to call, while need\n    - Allow null values setting\n    - Choices\n    - Filtering (and deep filtering):\n        - **exact** - equality\n        - **iexact** - case-independent equality\n        - **contains** - is filter string in the value string\n        - **icontains** - is filter string case-independent in the value string\n        - **in** - is value in the provided list\n        - **gt** - is value greater\n        - **gte** - is value greater or equals\n        - **lt** - is value less\n        - **lte** - is value less or equals\n        - **startswith** - is string starts with\n        - **istartswith** - is string case-independent starts with\n        - **endswith** - is string ends with\n        - **iendswith** - is string case-independent ends wth\n        - **range** - is value in provided range\n        - **isnull** - is value in [\"null\", None]\n- Built-in RedisModel class, with:\n    - All fields that you want\n    - TTL (Time To Live)\n- CRUD (Create Read Update Delete)\n- Non-blocking usage! Any operation gives the same result as the default, but it just creates an asyncio task in the background instead of write inside the call\n\n\n# Installation\n`pip install python-redis-orm`\n\n[Here is PyPI](https://pypi.org/project/python-redis-orm/)\n\nObviously, you need to install and run redis server on your machine, we support v3+ \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    - **ignore_deserialization_errors** (bool) - to ignore deserialization errors or raise exception\n    - **save_consistency** (bool) - to use structure-first data\n    - **economy** (bool) - if True, all update requests will return only instance id \n    - **use_keys** (bool) - to use Redis keys command (uses memory instead of CPU) instead of scan\n2. Create your models\n3. Call **register_models()** on your RedisRoot instance and provide list with your models\n4. Use our CRUD\n\n\n# CRUD\n```python\nexample_instance = ExampleModel(example_field='example_data').save() # - to create an instance and get its data dict\n# or:\nexample_instance = redis_root.create(ExampleModel, example_field='example_data')\nfiltered_example_instances = redis_root.get(ExampleModel, example_field='example_data') # - to get all ExampleModel instances with example_field filter and get its data dict\nordered_instances = redis_root.order(filtered_example_instances, '-id') # - to get ordered filtered_example_instances by id ('-' for reverse)\nupdated_example_instances = redis_root.update(ExampleModel, ordered_instances, example_field='another_example_data') # - to update all ordered_instances example_field with value 'another_example_data' and get its data dict\nredis_root.delete(ExampleModel, updated_example_instances) # - to delete updated_example_instances\n\n# Non-blocking funcs are the same, just add \"_nb\" to the end:\n# ExampleModel(...).save_nb()\n# redis_root.create_nb(...)\n# redis_root.update_nb(...)\n# redis_root.delete_nb(...)\n\n```\n\n\n# Example usage\n\nAll features:\n\n[full_test.py](https://github.com/gh0st-work/python_redis_orm/blob/master/python_redis_orm/tests/full_test.py)\n```python\nimport random\nimport sys\nfrom time import sleep\nimport asyncio\nimport os\n\nfrom python_redis_orm.core import *\n\n\ndef generate_token(chars_count):\n    allowed_chars = 'QWERTYUIOPASDFGHJKLZXCVBNM1234567890'\n    token = f'{\"\".join([random.choice(allowed_chars) for i in range(chars_count)])}'\n    return token\n\n\ndef generate_token_12_chars():\n    return generate_token(12)\n\n\nclass BotSession(RedisModel):\n    session_token = RedisString(default=generate_token_12_chars)\n    created = RedisDateTime(default=datetime.datetime.now)\n\n\nclass TaskChallenge(RedisModel):\n    bot_session = RedisForeignKey(model=BotSession)\n    task_id = RedisNumber(default=0, null=False)\n    status = RedisString(default='in_work', choices={\n        'in_work': 'В работе',\n        'completed': 'Завершён успешно',\n        'completed_frozen_points': 'Завершён успешно, получил поинты в холде',\n        'completed_points': 'Завершён успешно, получил поинты',\n        'completed_decommissioning': 'Завершён успешно, поинты списаны',\n        'failed_bot': 'Зафейлил бот',\n        'failed_task_creator': 'Зафейлил создатель задания',\n    }, null=False)\n    account_checks_count = RedisNumber(default=0)\n    created = RedisDateTime(default=datetime.datetime.now)\n\n\nclass TtlCheckModel(RedisModel):\n    redis_number_with_ttl = RedisNumber(default=0, null=False)\n\n\nclass MetaTtlCheckModel(RedisModel):\n    redis_number = RedisNumber(default=0, null=False)\n    \n    class Meta:\n        ttl = 5\n\n\nclass DictCheckModel(RedisModel):\n    redis_dict = RedisDict()\n\n\nclass ListCheckModel(RedisModel):\n    redis_list = RedisList()\n\n\nclass ForeignKeyCheckModel(RedisModel):\n    task_challenge = RedisForeignKey(model=TaskChallenge)\n\n\nclass ManyToManyCheckModel(RedisModel):\n    task_challenges = RedisManyToMany(model=TaskChallenge)\n\n\nclass ModelWithOverriddenSave(RedisModel):\n    multiplied_max_field = RedisNumber()\n    \n    def save(self):\n        redis_root = self.get('redis_root')  # get value of any field\n        new_value = 1\n        all_instances = redis_root.get(ModelWithOverriddenSave)\n        if all_instances:\n            max_value = max(list(map(lambda instance: instance['multiplied_max_field'], all_instances)))\n            new_value = max_value * 2\n        self.set(multiplied_max_field=new_value)\n        return super().save()\n\n\ndef clean_db_after_test(connection_pool, prefix):\n    redis_instance = redis.Redis(connection_pool=connection_pool)\n    for key in redis_instance.keys(f'{prefix}:*'):\n        redis_instance.delete(key)\n\n\ndef basic_test(connection_pool, prefix):\n    try:\n        redis_root = RedisRoot(\n            prefix=prefix,\n            connection_pool=connection_pool,\n            ignore_deserialization_errors=True\n        )\n        redis_root.register_models([\n            TaskChallenge,\n        ])\n        for i in range(5):\n            TaskChallenge(\n                redis_root=redis_root,\n                status='in_work',\n            ).save()\n        task_challenges_without_keys = redis_root.get(TaskChallenge)\n        task_challenges_with_keys = redis_root.get(TaskChallenge, return_dict=True)\n        have_exception = False\n        if not len(task_challenges_without_keys):\n            have_exception = True\n        if not task_challenges_with_keys:\n            have_exception = True\n        else:\n            if not task_challenges_with_keys.keys():\n                have_exception = True\n            else:\n                if len(list(task_challenges_with_keys.keys())) != len(task_challenges_without_keys):\n                    have_exception = True\n    except BaseException as ex:\n        have_exception = True\n    clean_db_after_test(connection_pool, prefix)\n    return have_exception\n\n\ndef auto_reg_test(connection_pool, prefix):\n    redis_root = RedisRoot(\n        prefix=prefix,\n        connection_pool=connection_pool,\n        ignore_deserialization_errors=True\n    )\n    task_challenge_1 = TaskChallenge(\n        redis_root=redis_root,\n        status='in_work',\n    ).save()\n    try:\n        task_challenges = redis_root.get(TaskChallenge)\n        have_exception = False\n    except BaseException as ex:\n        have_exception = True\n    clean_db_after_test(connection_pool, prefix)\n    return have_exception\n\n\ndef no_connection_pool_test(*args, **kwargs):\n    try:\n        redis_root = RedisRoot(\n            ignore_deserialization_errors=True\n        )\n        task_challenge_1 = TaskChallenge(\n            redis_root=redis_root,\n            status='in_work',\n        )\n        task_challenge_1.save()\n        task_challenges = redis_root.get(TaskChallenge)\n        have_exception = False\n        connection_pool = redis.ConnectionPool(\n            host=os.environ['REDIS_HOST'],\n            port=os.environ['REDIS_PORT'],\n            db=0,\n            decode_responses=True\n        )\n        clean_db_after_test(connection_pool, redis_root.prefix)\n    except BaseException as ex:\n        have_exception = True\n    return have_exception\n\n\ndef choices_test(connection_pool, prefix):\n    redis_root = RedisRoot(\n        prefix=prefix,\n        connection_pool=connection_pool,\n        ignore_deserialization_errors=True\n    )\n    task_challenge_1 = TaskChallenge(\n        redis_root=redis_root,\n        status='bruh',\n    )\n    try:\n        save_result = task_challenge_1.save()\n        task_challenges = redis_root.get(TaskChallenge)\n        have_exception = True\n    except BaseException as ex:\n        have_exception = False\n    clean_db_after_test(connection_pool, prefix)\n    return have_exception\n\n\ndef order_test(connection_pool, prefix):\n    redis_root = RedisRoot(\n        prefix=prefix,\n        connection_pool=connection_pool,\n        ignore_deserialization_errors=True\n    )\n    for i in range(3):\n        TaskChallenge(\n            redis_root=redis_root\n        ).save()\n    have_exception = True\n    try:\n        task_challenges = redis_root.get(TaskChallenge)\n        first_task_challenge = redis_root.order(task_challenges, 'id')[0]\n        last_task_challenge = redis_root.order(task_challenges, '-id')[0]\n        if first_task_challenge['id'] == 1 and last_task_challenge['id'] == len(task_challenges):\n            have_exception = False\n    except BaseException as ex:\n        pass\n    clean_db_after_test(connection_pool, prefix)\n    return have_exception\n\n\ndef filter_test(connection_pool, prefix):\n    redis_root = RedisRoot(\n        prefix=prefix,\n        connection_pool=connection_pool,\n        ignore_deserialization_errors=True\n    )\n    have_exception = True\n    try:\n        same_tokens_count = 2\n        random_tokens_count = 8\n        same_token = generate_token(50)\n        random_tokens = [generate_token(50) for i in range(random_tokens_count)]\n        for i in range(same_tokens_count):\n            BotSession(redis_root, session_token=same_token).save()\n        for random_token in random_tokens:\n            BotSession(redis_root, session_token=random_token).save()\n        task_challenges_with_same_token = redis_root.get(BotSession, session_token=same_token)\n        if len(task_challenges_with_same_token) == same_tokens_count:\n            have_exception = False\n    except BaseException as ex:\n        print(ex)\n    \n    clean_db_after_test(connection_pool, prefix)\n    return have_exception\n\n\ndef update_test(connection_pool, prefix):\n    redis_root = RedisRoot(\n        prefix=prefix,\n        connection_pool=connection_pool,\n        ignore_deserialization_errors=True\n    )\n    have_exception = True\n    try:\n        bot_session_1 = BotSession(redis_root, session_token='123').save()\n        bot_session_1_id = bot_session_1['id']\n        redis_root.update(BotSession, bot_session_1, session_token='234')\n        bot_sessions_filtered = redis_root.get(BotSession, id=bot_session_1_id)\n        if len(bot_sessions_filtered) == 1:\n            bot_session_1_new = bot_sessions_filtered[0]\n            if 'session_token' in bot_session_1_new.keys():\n                if bot_session_1_new['session_token'] == '234':\n                    have_exception = False\n    except BaseException as ex:\n        print(ex)\n    \n    clean_db_after_test(connection_pool, prefix)\n    return have_exception\n\n\ndef functions_like_defaults_test(connection_pool, prefix):\n    redis_root = RedisRoot(\n        prefix=prefix,\n        connection_pool=connection_pool,\n        ignore_deserialization_errors=True\n    )\n    have_exception = False\n    try:\n        bot_session_1 = BotSession(redis_root).save()\n        bot_session_2 = BotSession(redis_root).save()\n        if bot_session_1.session_token == bot_session_2.session_token:\n            have_exception = True\n    except BaseException as ex:\n        pass\n    \n    clean_db_after_test(connection_pool, prefix)\n    return have_exception\n\n\ndef redis_foreign_key_test(connection_pool, prefix):\n    redis_root = RedisRoot(\n        prefix=prefix,\n        connection_pool=connection_pool,\n        ignore_deserialization_errors=True\n    )\n    have_exception = True\n    try:\n        bot_session_1 = BotSession(\n            redis_root=redis_root,\n        ).save()\n        task_challenge_1 = TaskChallenge(\n            redis_root=redis_root,\n            bot_session=bot_session_1\n        ).save()\n        bot_sessions = redis_root.get(BotSession)\n        bot_session = redis_root.order(bot_sessions, '-id')[0]\n        task_challenges = redis_root.get(TaskChallenge)\n        task_challenge = redis_root.order(task_challenges, '-id')[0]\n        if type(task_challenge['bot_session']) == dict:\n            if task_challenge['bot_session'] == bot_session:\n                have_exception = False\n    except BaseException as ex:\n        print(ex)\n    \n    clean_db_after_test(connection_pool, prefix)\n    return have_exception\n\n\ndef delete_test(connection_pool, prefix):\n    redis_root = RedisRoot(\n        prefix=prefix,\n        connection_pool=connection_pool,\n        ignore_deserialization_errors=True\n    )\n    have_exception = True\n    try:\n        bot_session_1 = BotSession(\n            redis_root=redis_root,\n        ).save()\n        task_challenge_1 = TaskChallenge(\n            redis_root=redis_root,\n            bot_session=bot_session_1\n        ).save()\n        redis_root.delete(BotSession, bot_session_1)\n        redis_root.delete(TaskChallenge, task_challenge_1)\n        bot_sessions = redis_root.get(BotSession)\n        task_challenges = redis_root.get(TaskChallenge)\n        if len(bot_sessions) == 0 and len(task_challenges) == 0:\n            have_exception = False\n    except BaseException as ex:\n        print(ex)\n    \n    clean_db_after_test(connection_pool, prefix)\n    return have_exception\n\n\ndef save_consistency_test(connection_pool, prefix):\n    redis_root = RedisRoot(\n        prefix=prefix,\n        connection_pool=connection_pool,\n        ignore_deserialization_errors=True,\n        save_consistency=True,\n    )\n    have_exception = True\n    try:\n        ttl_check_model_1 = TtlCheckModel(\n            redis_root=redis_root,\n        ).save()\n        ttl_check_models = redis_root.get(TtlCheckModel)\n        if len(ttl_check_models):\n            ttl_check_model = ttl_check_models[0]\n            if 'redis_number_with_ttl' in ttl_check_model.keys():\n                sleep(6)\n                ttl_check_models = redis_root.get(TtlCheckModel)\n                if len(ttl_check_models):\n                    ttl_check_model = ttl_check_models[0]\n                    if 'redis_number_with_ttl' in ttl_check_model.keys():  # because consistency is saved\n                        have_exception = False\n    except BaseException as ex:\n        print(ex)\n    \n    clean_db_after_test(connection_pool, prefix)\n    return have_exception\n\n\ndef meta_ttl_test(connection_pool, prefix):\n    redis_root = RedisRoot(\n        prefix=prefix,\n        connection_pool=connection_pool,\n        ignore_deserialization_errors=True,\n    )\n    have_exception = True\n    try:\n        meta_ttl_check_model_1 = MetaTtlCheckModel(\n            redis_root=redis_root,\n        ).save()\n        meta_ttl_check_models = redis_root.get(MetaTtlCheckModel)\n        if len(meta_ttl_check_models):\n            meta_ttl_check_model = meta_ttl_check_models[0]\n            if 'redis_number' in meta_ttl_check_model.keys():\n                sleep(6)\n                meta_ttl_check_models = redis_root.get(MetaTtlCheckModel)\n                if not len(meta_ttl_check_models):\n                    have_exception = False\n    except BaseException as ex:\n        print(ex)\n    \n    clean_db_after_test(connection_pool, prefix)\n    return have_exception\n\n\ndef use_keys_test(connection_pool, prefix):\n    have_exception = True\n    try:\n        \n        redis_root = RedisRoot(\n            prefix=prefix,\n            connection_pool=connection_pool,\n            ignore_deserialization_errors=True,\n            use_keys=True\n        )\n        started_in_keys = datetime.datetime.now()\n        tests_count = 100\n        for i in range(tests_count):\n            task_challenge_1 = TaskChallenge(\n                redis_root=redis_root,\n                status='in_work',\n            ).save()\n            redis_root.update(TaskChallenge, task_challenge_1, account_checks_count=1)\n        ended_in_keys = datetime.datetime.now()\n        keys_time = (ended_in_keys - started_in_keys).total_seconds()\n        clean_db_after_test(connection_pool, prefix)\n        \n        redis_root = RedisRoot(\n            prefix=prefix,\n            connection_pool=connection_pool,\n            ignore_deserialization_errors=True,\n            use_keys=False\n        )\n        started_in_no_keys = datetime.datetime.now()\n        for i in range(tests_count):\n            task_challenge_1 = TaskChallenge(\n                redis_root=redis_root,\n                status='in_work',\n            ).save()\n            redis_root.update(TaskChallenge, task_challenge_1, account_checks_count=1)\n        ended_in_no_keys = datetime.datetime.now()\n        no_keys_time = (ended_in_no_keys - started_in_no_keys).total_seconds()\n        clean_db_after_test(connection_pool, prefix)\n        keys_percent = round((no_keys_time / keys_time - 1) * 100, 2)\n        keys_symbol = ('+' if keys_percent \u003e 0 else '')\n        print(f'Keys usage gives {keys_symbol}{keys_percent}% efficiency')\n        have_exception = False\n    except BaseException as ex:\n        print(ex)\n    \n    clean_db_after_test(connection_pool, prefix)\n    return have_exception\n\n\ndef dict_test(connection_pool, prefix):\n    redis_root = RedisRoot(\n        prefix=prefix,\n        connection_pool=connection_pool,\n        ignore_deserialization_errors=True\n    )\n    have_exception = True\n    try:\n        some_dict = {\n            'age': 19,\n            'weed': True\n        }\n        DictCheckModel(\n            redis_root=redis_root,\n            redis_dict=some_dict\n        ).save()\n        dict_check_model_instance = redis_root.get(DictCheckModel)[0]\n        if 'redis_dict' in dict_check_model_instance.keys():\n            if dict_check_model_instance['redis_dict'] == some_dict:\n                have_exception = False\n    except BaseException as ex:\n        print(ex)\n    \n    clean_db_after_test(connection_pool, prefix)\n    return have_exception\n\n\ndef list_test(connection_pool, prefix):\n    redis_root = RedisRoot(\n        prefix=prefix,\n        connection_pool=connection_pool,\n        ignore_deserialization_errors=True\n    )\n    have_exception = True\n    try:\n        some_list = [5, 9, 's', 4.5, False]\n        ListCheckModel(\n            redis_root=redis_root,\n            redis_list=some_list\n        ).save()\n        list_check_model_instance = redis_root.get(ListCheckModel)[0]\n        if 'redis_list' in list_check_model_instance.keys():\n            if list_check_model_instance['redis_list'] == some_list:\n                have_exception = False\n    except BaseException as ex:\n        print(ex)\n    \n    clean_db_after_test(connection_pool, prefix)\n    return have_exception\n\n\ndef non_blocking_test(connection_pool, prefix):\n    have_exception = True\n    \n    # try:\n    \n    def task(data_count, use_non_blocking):\n        connection_pool = redis.ConnectionPool(\n            host=os.environ['REDIS_HOST'],\n            port=os.environ['REDIS_PORT'],\n            db=0,\n            decode_responses=True\n        )\n        redis_root = RedisRoot(\n            prefix=prefix,\n            connection_pool=connection_pool,\n            ignore_deserialization_errors=True\n        )\n        \n        for i in range(data_count):\n            redis_root.create(\n                ListCheckModel,\n                redis_list=['update_list']\n            )\n            redis_root.create(\n                ListCheckModel,\n                redis_list=['delete_list']\n            )\n        \n        def create_list():\n            if use_non_blocking:\n                list_check_model_instance = redis_root.create_nb(\n                    ListCheckModel,\n                    redis_list=['create_list']\n                )\n            else:\n                list_check_model_instance = redis_root.create(\n                    ListCheckModel,\n                    redis_list=['create_list']\n                )\n        \n        def update_list():\n            to_update = redis_root.get(\n                ListCheckModel,\n                redis_list=['update_list']\n            )\n            if use_non_blocking:\n                updated_instance = redis_root.update_nb(\n                    ListCheckModel,\n                    to_update,\n                    redis_list=['now_updated_list']\n                )\n            else:\n                updated_instance = redis_root.update(\n                    ListCheckModel,\n                    to_update,\n                    redis_list=['now_updated_list']\n                )\n        \n        def delete_list():\n            to_delete = redis_root.get(\n                ListCheckModel,\n                redis_list=['delete_list']\n            )\n            if use_non_blocking:\n                redis_root.delete_nb(\n                    ListCheckModel,\n                    to_delete,\n                )\n            else:\n                redis_root.delete(\n                    ListCheckModel,\n                    to_delete,\n                )\n        \n        tests = [\n            create_list,\n            update_list,\n            delete_list,\n        ]\n        for test in tests:\n            for i in range(data_count):\n                test()\n    \n    data_count = 100\n    clean_db_after_test(connection_pool, prefix)\n    nb_started_in = datetime.datetime.now()\n    task(data_count, True)\n    nb_ended_in = datetime.datetime.now()\n    nb_time = (nb_ended_in - nb_started_in).total_seconds()\n    clean_db_after_test(connection_pool, prefix)\n    b_started_in = datetime.datetime.now()\n    task(data_count, False)\n    b_ended_in = datetime.datetime.now()\n    b_time = (b_ended_in - b_started_in).total_seconds()\n    clean_db_after_test(connection_pool, prefix)\n    \n    nb_percent = round((nb_time / b_time - 1) * 100, 2)\n    nb_symbol = ('+' if nb_percent \u003e 0 else '')\n    print(f'Non blocking gives {nb_symbol}{nb_percent}% efficiency')\n    have_exception = False\n    # except BaseException as ex:\n    #     print(ex)\n    \n    clean_db_after_test(connection_pool, prefix)\n    return have_exception\n\n\ndef foreign_key_test(connection_pool, prefix):\n    redis_root = RedisRoot(\n        prefix=prefix,\n        connection_pool=connection_pool,\n        ignore_deserialization_errors=True,\n    )\n    have_exception = False\n    try:\n        task_id = 12345\n        task_challenge = TaskChallenge(\n            redis_root=redis_root,\n            task_id=task_id\n        ).save()\n        foreign_key_check_instance = redis_root.create(\n            ForeignKeyCheckModel,\n            task_challenge=task_challenge\n        )\n        # Check really created\n        task_challenge_qs = redis_root.get(TaskChallenge, task_id=task_id)\n        if len(task_challenge_qs) != 1:\n            have_exception = True\n        else:\n            task_challenge = task_challenge_qs[0]\n            foreign_key_check_instance_qs = redis_root.get(ForeignKeyCheckModel, task_challenge=task_challenge)\n            if len(foreign_key_check_instance_qs) != 1:\n                have_exception = True\n            else:\n                foreign_key_check_instance = foreign_key_check_instance_qs[0]\n                if foreign_key_check_instance['task_challenge']['task_id'] != task_id:\n                    have_exception = True\n    except BaseException as ex:\n        print(ex)\n        have_exception = True\n    \n    clean_db_after_test(connection_pool, prefix)\n    return have_exception\n\n\ndef many_to_many_test(connection_pool, prefix):\n    redis_root = RedisRoot(\n        prefix=prefix,\n        connection_pool=connection_pool,\n        ignore_deserialization_errors=True,\n    )\n    have_exception = False\n    try:\n        tasks_ids = set([random.randrange(0, 100) for i in range(10)])\n        task_challenges = [\n            TaskChallenge(\n                redis_root=redis_root,\n                task_id=task_id\n            ).save()\n            for task_id in tasks_ids\n        ]\n        many_to_many_check_instance = redis_root.create(\n            ManyToManyCheckModel,\n            task_challenges=task_challenges\n        )\n        # Check really created\n        many_to_many_check_instances_qs = redis_root.get(ManyToManyCheckModel)\n        if len(many_to_many_check_instances_qs) != 1:\n            have_exception = True\n        else:\n            many_to_many_check_instance = many_to_many_check_instances_qs[0]\n            if many_to_many_check_instance['task_challenges'] != task_challenges:\n                have_exception = True\n    except BaseException as ex:\n        print(ex)\n        have_exception = True\n    \n    clean_db_after_test(connection_pool, prefix)\n    return have_exception\n\n\ndef save_override_test(connection_pool, prefix):\n    redis_root = RedisRoot(\n        prefix=prefix,\n        connection_pool=connection_pool,\n        ignore_deserialization_errors=True,\n    )\n    have_exception = False\n    try:\n        instance_1 = redis_root.create(ModelWithOverriddenSave)\n        instance_2 = redis_root.create(ModelWithOverriddenSave)\n        if instance_1['multiplied_max_field'] * 2 != instance_2['multiplied_max_field']:\n            have_exception = True\n    except BaseException as ex:\n        print(ex)\n        have_exception = True\n    \n    clean_db_after_test(connection_pool, prefix)\n    return have_exception\n\n\ndef performance_test(connection_pool, prefix):\n    have_exception = False\n    try:\n        \n        def run_test(count, model):\n            \n            def test(count, model, **test_params):\n                real_test_params = {\n                    'use_keys': True,\n                    'use_non_blocking': True\n                }\n                for key in real_test_params.copy():\n                    if key in test_params.keys():\n                        real_test_params[key] = test_params[key]\n                \n                def create_instances(redis_root, count, use_non_blocking, model):\n                    \n                    if use_non_blocking:\n                        started_in = datetime.datetime.now()\n                        results = [\n                            redis_root.create_nb(model)\n                            for i in range(count)\n                        ]\n                        ended_in = datetime.datetime.now()\n                    else:\n                        started_in = datetime.datetime.now()\n                        results = [\n                            redis_root.create(model)\n                            for i in range(count)\n                        ]\n                        ended_in = datetime.datetime.now()\n                    \n                    time_took = (ended_in - started_in).total_seconds()\n                    fields_count = len(results[0].keys()) * count\n                    clean_db_after_test(connection_pool, prefix)\n                    return [time_took, count, fields_count]\n                \n                redis_root = RedisRoot(\n                    prefix=prefix,\n                    connection_pool=connection_pool,\n                    ignore_deserialization_errors=True,\n                    use_keys=real_test_params['use_keys']\n                )\n                \n                test_result = create_instances(redis_root, count, real_test_params['use_non_blocking'], model)\n                \n                return test_result\n            \n            test_confs = [\n                {\n                    'use_keys': False,\n                    'use_non_blocking': False,\n                },\n                {\n                    'use_keys': False,\n                    'use_non_blocking': True,\n                },\n                {\n                    'use_keys': True,\n                    'use_non_blocking': False,\n                },\n                {\n                    'use_keys': True,\n                    'use_non_blocking': True,\n                },\n            \n            ]\n            \n            test_confs_results = [\n                test(count, model, **test_conf)\n                for test_conf in test_confs\n            ]\n            \n            print(f'\\n\\n\\n'\n                  f'Performance test results on your machine:\\n'\n                  f'Every test creates {test_confs_results[0][1]} instances ({test_confs_results[0][2]} fields) of {model.__name__} model,\\n'\n                  f'Here is the results:\\n'\n                  f'\\n')\n            \n            min_time = min(list(map(lambda result: result[0], test_confs_results)))\n            min_conf_text = ''\n            for i, test_confs_result in enumerate(test_confs_results):\n                test_conf_text = \", \".join([f\"{k} = {v}\" for k, v in test_confs[i].items()])\n                print(f'Configuration: {test_conf_text} took {test_confs_result[0]}s')\n                if test_confs_result[0] == min_time:\n                    min_conf_text = test_conf_text\n            print(f'\\n\\n'\n                  f'The best configuration: {min_conf_text}\\n')\n        \n        count = 1000\n        model = TaskChallenge\n        run_test(count, model)\n    except BaseException as ex:\n        print(ex)\n        have_exception = True\n    \n    clean_db_after_test(connection_pool, prefix)\n    return have_exception\n\n\ndef run_tests():\n    connection_pool = redis.ConnectionPool(\n        host=os.environ['REDIS_HOST'],\n        port=os.environ['REDIS_PORT'],\n        db=0,\n        decode_responses=True\n    )\n    tests = [\n        basic_test,\n        auto_reg_test,\n        no_connection_pool_test,\n        choices_test,\n        order_test,\n        filter_test,\n        functions_like_defaults_test,\n        redis_foreign_key_test,\n        update_test,\n        delete_test,\n        save_consistency_test,\n        meta_ttl_test,\n        use_keys_test,\n        list_test,\n        dict_test,\n        non_blocking_test,\n        foreign_key_test,\n        many_to_many_test,\n        save_override_test,\n        performance_test,\n    ]\n    results = []\n    started_in = datetime.datetime.now()\n    print('STARTING TESTS\\n')\n    for i, test in enumerate(tests):\n        print(f'Starting {int(i + 1)} test: {test.__name__.replace(\"_\", \" \")}')\n        test_started_in = datetime.datetime.now()\n        result = not test(connection_pool, test.__name__)\n        test_ended_in = datetime.datetime.now()\n        test_time = (test_ended_in - test_started_in).total_seconds()\n        print(f'{result = } / {test_time}s\\n')\n        results.append(result)\n    ended_in = datetime.datetime.now()\n    time = (ended_in - started_in).total_seconds()\n    success_message = 'SUCCESS' if all(results) else 'FAILED'\n    print('\\n'\n          f'{success_message}!\\n')\n    results_success_count = 0\n    for i, result in enumerate(results):\n        result_message = 'SUCCESS' if result else 'FAILED'\n        print(f'Test {(i + 1)}/{len(results)}: {result_message} ({tests[i].__name__.replace(\"_\", \" \")})')\n        if result:\n            results_success_count += 1\n    print(f'\\n'\n          f'{results_success_count} / {len(results)} tests ran successfully\\n'\n          f'All tests completed in {time}s\\n')\n    \n    return all(results)\n\n\nif __name__ == '__main__':\n    results = run_tests()\n    if not results:\n        sys.exit(1)\n\n```\n\n\n### Output\n\n```\nSTARTING TESTS\n\nStarting 1 test: basic test\nresult = True / 0.017655s\n\nStarting 2 test: auto reg test\nresult = True / 0.002688s\n\nStarting 3 test: no connection pool test\n2021-09-17 13:33:42.915213 - RedisRoot: No connection_pool provided, trying default config...\nresult = True / 0.003571s\n\nStarting 4 test: choices test\nresult = True / 0.001307s\n\nStarting 5 test: order test\nresult = True / 0.005999s\n\nStarting 6 test: filter test\nresult = True / 0.019395s\n\nStarting 7 test: functions like defaults test\nresult = True / 0.003462s\n\nStarting 8 test: redis foreign key test\nresult = True / 0.006697s\n\nStarting 9 test: update test\nresult = True / 0.003751s\n\nStarting 10 test: delete test\nresult = True / 0.006936s\n\nStarting 11 test: save consistency test\nresult = True / 6.009583s\n\nStarting 12 test: meta ttl test\nresult = True / 6.010543s\n\nStarting 13 test: use keys test\nKeys usage gives +32.47% efficiency\nresult = True / 1.348907s\n\nStarting 14 test: list test\nresult = True / 0.002379s\n\nStarting 15 test: dict test\nresult = True / 0.002316s\n\nStarting 16 test: non blocking test\nNon blocking gives +195.91% efficiency\nresult = True / 13.0517s\n\nStarting 17 test: foreign key test\nresult = True / 0.00598s\n\nStarting 18 test: many to many test\nresult = True / 0.033524s\n\nStarting 19 test: save override test\nresult = True / 0.003881s\n\nStarting 20 test: performance test\n\n\n\nPerformance test results on your machine:\nEvery test creates 1000 instances (6000 fields) of TaskChallenge model,\nHere is the results:\n\n\nConfiguration: use_keys = False, use_non_blocking = False took 40.92255s\nConfiguration: use_keys = False, use_non_blocking = True took 0.234719s\nConfiguration: use_keys = True, use_non_blocking = False took 31.373307s\nConfiguration: use_keys = True, use_non_blocking = True took 0.242484s\n\n\nThe best configuration: use_keys = False, use_non_blocking = True\n\nresult = True / 73.106303s\n\n\nSUCCESS!\n\nTest 1/20: SUCCESS (basic test)\nTest 2/20: SUCCESS (auto reg test)\nTest 3/20: SUCCESS (no connection pool test)\nTest 4/20: SUCCESS (choices test)\nTest 5/20: SUCCESS (order test)\nTest 6/20: SUCCESS (filter test)\nTest 7/20: SUCCESS (functions like defaults test)\nTest 8/20: SUCCESS (redis foreign key test)\nTest 9/20: SUCCESS (update test)\nTest 10/20: SUCCESS (delete test)\nTest 11/20: SUCCESS (save consistency test)\nTest 12/20: SUCCESS (meta ttl test)\nTest 13/20: SUCCESS (use keys test)\nTest 14/20: SUCCESS (list test)\nTest 15/20: SUCCESS (dict test)\nTest 16/20: SUCCESS (non blocking test)\nTest 17/20: SUCCESS (foreign key test)\nTest 18/20: SUCCESS (many to many test)\nTest 19/20: SUCCESS (save override test)\nTest 20/20: SUCCESS (performance test)\n\n20 / 20 tests ran successfully\nAll tests completed in 99.646971s\n\n```","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgh0st-work%2Fpython_redis_orm","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fgh0st-work%2Fpython_redis_orm","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fgh0st-work%2Fpython_redis_orm/lists"}