Package: python-redis-orm
python-redis-ormPython Redis ORM library that gives redis easy-to-use objects with fields and speeds a development up, inspired by Django ORMFor 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.Working with this library, you are expected:Fully works in 2021Django-like architectureEasy adaptation to your needsAdequate informational messages and error messagesBuilt-in RedisRoot class that stores specified models, with:redis_instancesetting - your redis connection (from redis-py)prefixsetting - prefix of this RedisRoot to be stored in redisignore_deserialization_errorssetting - do not raise errors, while deserializing datasave_consistencysetting - show structure-first dataeconomysetting - to not return full data and save some requests (usually, speeds up your app on 80%)13 built-in types of fields:RedisField- base class for nestingRedisString- stringRedisNumber- int or floatRedisId- instances IDsRedisBool- boolRedisDecimal- working accurately with numbers via decimalRedisJson- for data, that can be JSONedRedisList- listRedisDict- dictRedisDateTime- for work with date and time, via python datetime.datetimeRedisDate- for work with date, via python datetime.dataRedisForeignKey- for link to other instanceRedisManyToMany- for links to other instancesAll fields supports:Automatically serializationAutomatically deserializationTTL (Time To Live) settingDefault valuesProviding functions without call, to call, while needAllow null values settingChoicesFiltering (and deep filtering):exact- equalityiexact- case-independent equalitycontains- is filter string in the value stringicontains- is filter string case-independent in the value stringin- is value in the provided listgt- is value greatergte- is value greater or equalslt- is value lesslte- is value less or equalsstartswith- is string starts withistartswith- is string case-independent starts withendswith- is string ends withiendswith- is string case-independent ends wthrange- is value in provided rangeisnull- is value in ["null", None]Built-in RedisModel class, with:All fields that you wantTTL (Time To Live)CRUD (Create Read Update Delete)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 callInstallationpip install python-redis-ormHere is PyPIObviously, you need to install and run redis server on your machine, we support v3+UsageCreateRedisRootwith params:prefix(str) - prefix for your redis rootconnection_pool(redis.ConnectionPool) - redis-py redis.ConnectionPool instance, with decode_responses=Trueignore_deserialization_errors(bool) - to ignore deserialization errors or raise exceptionsave_consistency(bool) - to use structure-first dataeconomy(bool) - if True, all update requests will return only instance iduse_keys(bool) - to use Redis keys command (uses memory instead of CPU) instead of scanCreate your modelsCallregister_models()on your RedisRoot instance and provide list with your modelsUse our CRUDCRUDexample_instance=ExampleModel(example_field='example_data').save()# - to create an instance and get its data dict# or:example_instance=redis_root.create(ExampleModel,example_field='example_data')filtered_example_instances=redis_root.get(ExampleModel,example_field='example_data')# - to get all ExampleModel instances with example_field filter and get its data dictordered_instances=redis_root.order(filtered_example_instances,'-id')# - to get ordered filtered_example_instances by id ('-' for reverse)updated_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 dictredis_root.delete(ExampleModel,updated_example_instances)# - to delete updated_example_instances# Non-blocking funcs are the same, just add "_nb" to the end:# ExampleModel(...).save_nb()# redis_root.create_nb(...)# redis_root.update_nb(...)# redis_root.delete_nb(...)Example usageAll features:full_test.pyimportrandomimportsysfromtimeimportsleepimportasyncioimportosfrompython_redis_orm.coreimport*defgenerate_token(chars_count):allowed_chars='QWERTYUIOPASDFGHJKLZXCVBNM1234567890'token=f'{"".join([random.choice(allowed_chars)foriinrange(chars_count)])}'returntokendefgenerate_token_12_chars():returngenerate_token(12)classBotSession(RedisModel):session_token=RedisString(default=generate_token_12_chars)created=RedisDateTime(default=datetime.datetime.now)classTaskChallenge(RedisModel):bot_session=RedisForeignKey(model=BotSession)task_id=RedisNumber(default=0,null=False)status=RedisString(default='in_work',choices={'in_work':'В работе','completed':'Завершён успешно','completed_frozen_points':'Завершён успешно, получил поинты в холде','completed_points':'Завершён успешно, получил поинты','completed_decommissioning':'Завершён успешно, поинты списаны','failed_bot':'Зафейлил бот','failed_task_creator':'Зафейлил создатель задания',},null=False)account_checks_count=RedisNumber(default=0)created=RedisDateTime(default=datetime.datetime.now)classTtlCheckModel(RedisModel):redis_number_with_ttl=RedisNumber(default=0,null=False)classMetaTtlCheckModel(RedisModel):redis_number=RedisNumber(default=0,null=False)classMeta:ttl=5classDictCheckModel(RedisModel):redis_dict=RedisDict()classListCheckModel(RedisModel):redis_list=RedisList()classForeignKeyCheckModel(RedisModel):task_challenge=RedisForeignKey(model=TaskChallenge)classManyToManyCheckModel(RedisModel):task_challenges=RedisManyToMany(model=TaskChallenge)classModelWithOverriddenSave(RedisModel):multiplied_max_field=RedisNumber()defsave(self):redis_root=self.get('redis_root')# get value of any fieldnew_value=1all_instances=redis_root.get(ModelWithOverriddenSave)ifall_instances:max_value=max(list(map(lambdainstance:instance['multiplied_max_field'],all_instances)))new_value=max_value*2self.set(multiplied_max_field=new_value)returnsuper().save()defclean_db_after_test(connection_pool,prefix):redis_instance=redis.Redis(connection_pool=connection_pool)forkeyinredis_instance.keys(f'{prefix}:*'):redis_instance.delete(key)defbasic_test(connection_pool,prefix):try:redis_root=RedisRoot(prefix=prefix,connection_pool=connection_pool,ignore_deserialization_errors=True)redis_root.register_models([TaskChallenge,])foriinrange(5):TaskChallenge(redis_root=redis_root,status='in_work',).save()task_challenges_without_keys=redis_root.get(TaskChallenge)task_challenges_with_keys=redis_root.get(TaskChallenge,return_dict=True)have_exception=Falseifnotlen(task_challenges_without_keys):have_exception=Trueifnottask_challenges_with_keys:have_exception=Trueelse:ifnottask_challenges_with_keys.keys():have_exception=Trueelse:iflen(list(task_challenges_with_keys.keys()))!=len(task_challenges_without_keys):have_exception=TrueexceptBaseExceptionasex:have_exception=Trueclean_db_after_test(connection_pool,prefix)returnhave_exceptiondefauto_reg_test(connection_pool,prefix):redis_root=RedisRoot(prefix=prefix,connection_pool=connection_pool,ignore_deserialization_errors=True)task_challenge_1=TaskChallenge(redis_root=redis_root,status='in_work',).save()try:task_challenges=redis_root.get(TaskChallenge)have_exception=FalseexceptBaseExceptionasex:have_exception=Trueclean_db_after_test(connection_pool,prefix)returnhave_exceptiondefno_connection_pool_test(*args,**kwargs):try:redis_root=RedisRoot(ignore_deserialization_errors=True)task_challenge_1=TaskChallenge(redis_root=redis_root,status='in_work',)task_challenge_1.save()task_challenges=redis_root.get(TaskChallenge)have_exception=Falseconnection_pool=redis.ConnectionPool(host=os.environ['REDIS_HOST'],port=os.environ['REDIS_PORT'],db=0,decode_responses=True)clean_db_after_test(connection_pool,redis_root.prefix)exceptBaseExceptionasex:have_exception=Truereturnhave_exceptiondefchoices_test(connection_pool,prefix):redis_root=RedisRoot(prefix=prefix,connection_pool=connection_pool,ignore_deserialization_errors=True)task_challenge_1=TaskChallenge(redis_root=redis_root,status='bruh',)try:save_result=task_challenge_1.save()task_challenges=redis_root.get(TaskChallenge)have_exception=TrueexceptBaseExceptionasex:have_exception=Falseclean_db_after_test(connection_pool,prefix)returnhave_exceptiondeforder_test(connection_pool,prefix):redis_root=RedisRoot(prefix=prefix,connection_pool=connection_pool,ignore_deserialization_errors=True)foriinrange(3):TaskChallenge(redis_root=redis_root).save()have_exception=Truetry:task_challenges=redis_root.get(TaskChallenge)first_task_challenge=redis_root.order(task_challenges,'id')[0]last_task_challenge=redis_root.order(task_challenges,'-id')[0]iffirst_task_challenge['id']==1andlast_task_challenge['id']==len(task_challenges):have_exception=FalseexceptBaseExceptionasex:passclean_db_after_test(connection_pool,prefix)returnhave_exceptiondeffilter_test(connection_pool,prefix):redis_root=RedisRoot(prefix=prefix,connection_pool=connection_pool,ignore_deserialization_errors=True)have_exception=Truetry:same_tokens_count=2random_tokens_count=8same_token=generate_token(50)random_tokens=[generate_token(50)foriinrange(random_tokens_count)]foriinrange(same_tokens_count):BotSession(redis_root,session_token=same_token).save()forrandom_tokeninrandom_tokens:BotSession(redis_root,session_token=random_token).save()task_challenges_with_same_token=redis_root.get(BotSession,session_token=same_token)iflen(task_challenges_with_same_token)==same_tokens_count:have_exception=FalseexceptBaseExceptionasex:print(ex)clean_db_after_test(connection_pool,prefix)returnhave_exceptiondefupdate_test(connection_pool,prefix):redis_root=RedisRoot(prefix=prefix,connection_pool=connection_pool,ignore_deserialization_errors=True)have_exception=Truetry:bot_session_1=BotSession(redis_root,session_token='123').save()bot_session_1_id=bot_session_1['id']redis_root.update(BotSession,bot_session_1,session_token='234')bot_sessions_filtered=redis_root.get(BotSession,id=bot_session_1_id)iflen(bot_sessions_filtered)==1:bot_session_1_new=bot_sessions_filtered[0]if'session_token'inbot_session_1_new.keys():ifbot_session_1_new['session_token']=='234':have_exception=FalseexceptBaseExceptionasex:print(ex)clean_db_after_test(connection_pool,prefix)returnhave_exceptiondeffunctions_like_defaults_test(connection_pool,prefix):redis_root=RedisRoot(prefix=prefix,connection_pool=connection_pool,ignore_deserialization_errors=True)have_exception=Falsetry:bot_session_1=BotSession(redis_root).save()bot_session_2=BotSession(redis_root).save()ifbot_session_1.session_token==bot_session_2.session_token:have_exception=TrueexceptBaseExceptionasex:passclean_db_after_test(connection_pool,prefix)returnhave_exceptiondefredis_foreign_key_test(connection_pool,prefix):redis_root=RedisRoot(prefix=prefix,connection_pool=connection_pool,ignore_deserialization_errors=True)have_exception=Truetry:bot_session_1=BotSession(redis_root=redis_root,).save()task_challenge_1=TaskChallenge(redis_root=redis_root,bot_session=bot_session_1).save()bot_sessions=redis_root.get(BotSession)bot_session=redis_root.order(bot_sessions,'-id')[0]task_challenges=redis_root.get(TaskChallenge)task_challenge=redis_root.order(task_challenges,'-id')[0]iftype(task_challenge['bot_session'])==dict:iftask_challenge['bot_session']==bot_session:have_exception=FalseexceptBaseExceptionasex:print(ex)clean_db_after_test(connection_pool,prefix)returnhave_exceptiondefdelete_test(connection_pool,prefix):redis_root=RedisRoot(prefix=prefix,connection_pool=connection_pool,ignore_deserialization_errors=True)have_exception=Truetry:bot_session_1=BotSession(redis_root=redis_root,).save()task_challenge_1=TaskChallenge(redis_root=redis_root,bot_session=bot_session_1).save()redis_root.delete(BotSession,bot_session_1)redis_root.delete(TaskChallenge,task_challenge_1)bot_sessions=redis_root.get(BotSession)task_challenges=redis_root.get(TaskChallenge)iflen(bot_sessions)==0andlen(task_challenges)==0:have_exception=FalseexceptBaseExceptionasex:print(ex)clean_db_after_test(connection_pool,prefix)returnhave_exceptiondefsave_consistency_test(connection_pool,prefix):redis_root=RedisRoot(prefix=prefix,connection_pool=connection_pool,ignore_deserialization_errors=True,save_consistency=True,)have_exception=Truetry:ttl_check_model_1=TtlCheckModel(redis_root=redis_root,).save()ttl_check_models=redis_root.get(TtlCheckModel)iflen(ttl_check_models):ttl_check_model=ttl_check_models[0]if'redis_number_with_ttl'inttl_check_model.keys():sleep(6)ttl_check_models=redis_root.get(TtlCheckModel)iflen(ttl_check_models):ttl_check_model=ttl_check_models[0]if'redis_number_with_ttl'inttl_check_model.keys():# because consistency is savedhave_exception=FalseexceptBaseExceptionasex:print(ex)clean_db_after_test(connection_pool,prefix)returnhave_exceptiondefmeta_ttl_test(connection_pool,prefix):redis_root=RedisRoot(prefix=prefix,connection_pool=connection_pool,ignore_deserialization_errors=True,)have_exception=Truetry:meta_ttl_check_model_1=MetaTtlCheckModel(redis_root=redis_root,).save()meta_ttl_check_models=redis_root.get(MetaTtlCheckModel)iflen(meta_ttl_check_models):meta_ttl_check_model=meta_ttl_check_models[0]if'redis_number'inmeta_ttl_check_model.keys():sleep(6)meta_ttl_check_models=redis_root.get(MetaTtlCheckModel)ifnotlen(meta_ttl_check_models):have_exception=FalseexceptBaseExceptionasex:print(ex)clean_db_after_test(connection_pool,prefix)returnhave_exceptiondefuse_keys_test(connection_pool,prefix):have_exception=Truetry:redis_root=RedisRoot(prefix=prefix,connection_pool=connection_pool,ignore_deserialization_errors=True,use_keys=True)started_in_keys=datetime.datetime.now()tests_count=100foriinrange(tests_count):task_challenge_1=TaskChallenge(redis_root=redis_root,status='in_work',).save()redis_root.update(TaskChallenge,task_challenge_1,account_checks_count=1)ended_in_keys=datetime.datetime.now()keys_time=(ended_in_keys-started_in_keys).total_seconds()clean_db_after_test(connection_pool,prefix)redis_root=RedisRoot(prefix=prefix,connection_pool=connection_pool,ignore_deserialization_errors=True,use_keys=False)started_in_no_keys=datetime.datetime.now()foriinrange(tests_count):task_challenge_1=TaskChallenge(redis_root=redis_root,status='in_work',).save()redis_root.update(TaskChallenge,task_challenge_1,account_checks_count=1)ended_in_no_keys=datetime.datetime.now()no_keys_time=(ended_in_no_keys-started_in_no_keys).total_seconds()clean_db_after_test(connection_pool,prefix)keys_percent=round((no_keys_time/keys_time-1)*100,2)keys_symbol=('+'ifkeys_percent>0else'')print(f'Keys usage gives{keys_symbol}{keys_percent}% efficiency')have_exception=FalseexceptBaseExceptionasex:print(ex)clean_db_after_test(connection_pool,prefix)returnhave_exceptiondefdict_test(connection_pool,prefix):redis_root=RedisRoot(prefix=prefix,connection_pool=connection_pool,ignore_deserialization_errors=True)have_exception=Truetry:some_dict={'age':19,'weed':True}DictCheckModel(redis_root=redis_root,redis_dict=some_dict).save()dict_check_model_instance=redis_root.get(DictCheckModel)[0]if'redis_dict'indict_check_model_instance.keys():ifdict_check_model_instance['redis_dict']==some_dict:have_exception=FalseexceptBaseExceptionasex:print(ex)clean_db_after_test(connection_pool,prefix)returnhave_exceptiondeflist_test(connection_pool,prefix):redis_root=RedisRoot(prefix=prefix,connection_pool=connection_pool,ignore_deserialization_errors=True)have_exception=Truetry:some_list=[5,9,'s',4.5,False]ListCheckModel(redis_root=redis_root,redis_list=some_list).save()list_check_model_instance=redis_root.get(ListCheckModel)[0]if'redis_list'inlist_check_model_instance.keys():iflist_check_model_instance['redis_list']==some_list:have_exception=FalseexceptBaseExceptionasex:print(ex)clean_db_after_test(connection_pool,prefix)returnhave_exceptiondefnon_blocking_test(connection_pool,prefix):have_exception=True# try:deftask(data_count,use_non_blocking):connection_pool=redis.ConnectionPool(host=os.environ['REDIS_HOST'],port=os.environ['REDIS_PORT'],db=0,decode_responses=True)redis_root=RedisRoot(prefix=prefix,connection_pool=connection_pool,ignore_deserialization_errors=True)foriinrange(data_count):redis_root.create(ListCheckModel,redis_list=['update_list'])redis_root.create(ListCheckModel,redis_list=['delete_list'])defcreate_list():ifuse_non_blocking:list_check_model_instance=redis_root.create_nb(ListCheckModel,redis_list=['create_list'])else:list_check_model_instance=redis_root.create(ListCheckModel,redis_list=['create_list'])defupdate_list():to_update=redis_root.get(ListCheckModel,redis_list=['update_list'])ifuse_non_blocking:updated_instance=redis_root.update_nb(ListCheckModel,to_update,redis_list=['now_updated_list'])else:updated_instance=redis_root.update(ListCheckModel,to_update,redis_list=['now_updated_list'])defdelete_list():to_delete=redis_root.get(ListCheckModel,redis_list=['delete_list'])ifuse_non_blocking:redis_root.delete_nb(ListCheckModel,to_delete,)else:redis_root.delete(ListCheckModel,to_delete,)tests=[create_list,update_list,delete_list,]fortestintests:foriinrange(data_count):test()data_count=100clean_db_after_test(connection_pool,prefix)nb_started_in=datetime.datetime.now()task(data_count,True)nb_ended_in=datetime.datetime.now()nb_time=(nb_ended_in-nb_started_in).total_seconds()clean_db_after_test(connection_pool,prefix)b_started_in=datetime.datetime.now()task(data_count,False)b_ended_in=datetime.datetime.now()b_time=(b_ended_in-b_started_in).total_seconds()clean_db_after_test(connection_pool,prefix)nb_percent=round((nb_time/b_time-1)*100,2)nb_symbol=('+'ifnb_percent>0else'')print(f'Non blocking gives{nb_symbol}{nb_percent}% efficiency')have_exception=False# except BaseException as ex:#     print(ex)clean_db_after_test(connection_pool,prefix)returnhave_exceptiondefforeign_key_test(connection_pool,prefix):redis_root=RedisRoot(prefix=prefix,connection_pool=connection_pool,ignore_deserialization_errors=True,)have_exception=Falsetry:task_id=12345task_challenge=TaskChallenge(redis_root=redis_root,task_id=task_id).save()foreign_key_check_instance=redis_root.create(ForeignKeyCheckModel,task_challenge=task_challenge)# Check really createdtask_challenge_qs=redis_root.get(TaskChallenge,task_id=task_id)iflen(task_challenge_qs)!=1:have_exception=Trueelse:task_challenge=task_challenge_qs[0]foreign_key_check_instance_qs=redis_root.get(ForeignKeyCheckModel,task_challenge=task_challenge)iflen(foreign_key_check_instance_qs)!=1:have_exception=Trueelse:foreign_key_check_instance=foreign_key_check_instance_qs[0]ifforeign_key_check_instance['task_challenge']['task_id']!=task_id:have_exception=TrueexceptBaseExceptionasex:print(ex)have_exception=Trueclean_db_after_test(connection_pool,prefix)returnhave_exceptiondefmany_to_many_test(connection_pool,prefix):redis_root=RedisRoot(prefix=prefix,connection_pool=connection_pool,ignore_deserialization_errors=True,)have_exception=Falsetry:tasks_ids=set([random.randrange(0,100)foriinrange(10)])task_challenges=[TaskChallenge(redis_root=redis_root,task_id=task_id).save()fortask_idintasks_ids]many_to_many_check_instance=redis_root.create(ManyToManyCheckModel,task_challenges=task_challenges)# Check really createdmany_to_many_check_instances_qs=redis_root.get(ManyToManyCheckModel)iflen(many_to_many_check_instances_qs)!=1:have_exception=Trueelse:many_to_many_check_instance=many_to_many_check_instances_qs[0]ifmany_to_many_check_instance['task_challenges']!=task_challenges:have_exception=TrueexceptBaseExceptionasex:print(ex)have_exception=Trueclean_db_after_test(connection_pool,prefix)returnhave_exceptiondefsave_override_test(connection_pool,prefix):redis_root=RedisRoot(prefix=prefix,connection_pool=connection_pool,ignore_deserialization_errors=True,)have_exception=Falsetry:instance_1=redis_root.create(ModelWithOverriddenSave)instance_2=redis_root.create(ModelWithOverriddenSave)ifinstance_1['multiplied_max_field']*2!=instance_2['multiplied_max_field']:have_exception=TrueexceptBaseExceptionasex:print(ex)have_exception=Trueclean_db_after_test(connection_pool,prefix)returnhave_exceptiondefperformance_test(connection_pool,prefix):have_exception=Falsetry:defrun_test(count,model):deftest(count,model,**test_params):real_test_params={'use_keys':True,'use_non_blocking':True}forkeyinreal_test_params.copy():ifkeyintest_params.keys():real_test_params[key]=test_params[key]defcreate_instances(redis_root,count,use_non_blocking,model):ifuse_non_blocking:started_in=datetime.datetime.now()results=[redis_root.create_nb(model)foriinrange(count)]ended_in=datetime.datetime.now()else:started_in=datetime.datetime.now()results=[redis_root.create(model)foriinrange(count)]ended_in=datetime.datetime.now()time_took=(ended_in-started_in).total_seconds()fields_count=len(results[0].keys())*countclean_db_after_test(connection_pool,prefix)return[time_took,count,fields_count]redis_root=RedisRoot(prefix=prefix,connection_pool=connection_pool,ignore_deserialization_errors=True,use_keys=real_test_params['use_keys'])test_result=create_instances(redis_root,count,real_test_params['use_non_blocking'],model)returntest_resulttest_confs=[{'use_keys':False,'use_non_blocking':False,},{'use_keys':False,'use_non_blocking':True,},{'use_keys':True,'use_non_blocking':False,},{'use_keys':True,'use_non_blocking':True,},]test_confs_results=[test(count,model,**test_conf)fortest_confintest_confs]print(f'\n\n\n'f'Performance test results on your machine:\n'f'Every test creates{test_confs_results[0][1]}instances ({test_confs_results[0][2]}fields) of{model.__name__}model,\n'f'Here is the results:\n'f'\n')min_time=min(list(map(lambdaresult:result[0],test_confs_results)))min_conf_text=''fori,test_confs_resultinenumerate(test_confs_results):test_conf_text=", ".join([f"{k}={v}"fork,vintest_confs[i].items()])print(f'Configuration:{test_conf_text}took{test_confs_result[0]}s')iftest_confs_result[0]==min_time:min_conf_text=test_conf_textprint(f'\n\n'f'The best configuration:{min_conf_text}\n')count=1000model=TaskChallengerun_test(count,model)exceptBaseExceptionasex:print(ex)have_exception=Trueclean_db_after_test(connection_pool,prefix)returnhave_exceptiondefrun_tests():connection_pool=redis.ConnectionPool(host=os.environ['REDIS_HOST'],port=os.environ['REDIS_PORT'],db=0,decode_responses=True)tests=[basic_test,auto_reg_test,no_connection_pool_test,choices_test,order_test,filter_test,functions_like_defaults_test,redis_foreign_key_test,update_test,delete_test,save_consistency_test,meta_ttl_test,use_keys_test,list_test,dict_test,non_blocking_test,foreign_key_test,many_to_many_test,save_override_test,performance_test,]results=[]started_in=datetime.datetime.now()print('STARTING TESTS\n')fori,testinenumerate(tests):print(f'Starting{int(i+1)}test:{test.__name__.replace("_"," ")}')test_started_in=datetime.datetime.now()result=nottest(connection_pool,test.__name__)test_ended_in=datetime.datetime.now()test_time=(test_ended_in-test_started_in).total_seconds()print(f'{result= }/{test_time}s\n')results.append(result)ended_in=datetime.datetime.now()time=(ended_in-started_in).total_seconds()success_message='SUCCESS'ifall(results)else'FAILED'print('\n'f'{success_message}!\n')results_success_count=0fori,resultinenumerate(results):result_message='SUCCESS'ifresultelse'FAILED'print(f'Test{(i+1)}/{len(results)}:{result_message}({tests[i].__name__.replace("_"," ")})')ifresult:results_success_count+=1print(f'\n'f'{results_success_count}/{len(results)}tests ran successfully\n'f'All tests completed in{time}s\n')returnall(results)if__name__=='__main__':results=run_tests()ifnotresults:sys.exit(1)OutputSTARTING TESTS

Starting 1 test: basic test
result = True / 0.017655s

Starting 2 test: auto reg test
result = True / 0.002688s

Starting 3 test: no connection pool test
2021-09-17 13:33:42.915213 - RedisRoot: No connection_pool provided, trying default config...
result = True / 0.003571s

Starting 4 test: choices test
result = True / 0.001307s

Starting 5 test: order test
result = True / 0.005999s

Starting 6 test: filter test
result = True / 0.019395s

Starting 7 test: functions like defaults test
result = True / 0.003462s

Starting 8 test: redis foreign key test
result = True / 0.006697s

Starting 9 test: update test
result = True / 0.003751s

Starting 10 test: delete test
result = True / 0.006936s

Starting 11 test: save consistency test
result = True / 6.009583s

Starting 12 test: meta ttl test
result = True / 6.010543s

Starting 13 test: use keys test
Keys usage gives +32.47% efficiency
result = True / 1.348907s

Starting 14 test: list test
result = True / 0.002379s

Starting 15 test: dict test
result = True / 0.002316s

Starting 16 test: non blocking test
Non blocking gives +195.91% efficiency
result = True / 13.0517s

Starting 17 test: foreign key test
result = True / 0.00598s

Starting 18 test: many to many test
result = True / 0.033524s

Starting 19 test: save override test
result = True / 0.003881s

Starting 20 test: performance test



Performance test results on your machine:
Every test creates 1000 instances (6000 fields) of TaskChallenge model,
Here is the results:


Configuration: use_keys = False, use_non_blocking = False took 40.92255s
Configuration: use_keys = False, use_non_blocking = True took 0.234719s
Configuration: use_keys = True, use_non_blocking = False took 31.373307s
Configuration: use_keys = True, use_non_blocking = True took 0.242484s


The best configuration: use_keys = False, use_non_blocking = True

result = True / 73.106303s


SUCCESS!

Test 1/20: SUCCESS (basic test)
Test 2/20: SUCCESS (auto reg test)
Test 3/20: SUCCESS (no connection pool test)
Test 4/20: SUCCESS (choices test)
Test 5/20: SUCCESS (order test)
Test 6/20: SUCCESS (filter test)
Test 7/20: SUCCESS (functions like defaults test)
Test 8/20: SUCCESS (redis foreign key test)
Test 9/20: SUCCESS (update test)
Test 10/20: SUCCESS (delete test)
Test 11/20: SUCCESS (save consistency test)
Test 12/20: SUCCESS (meta ttl test)
Test 13/20: SUCCESS (use keys test)
Test 14/20: SUCCESS (list test)
Test 15/20: SUCCESS (dict test)
Test 16/20: SUCCESS (non blocking test)
Test 17/20: SUCCESS (foreign key test)
Test 18/20: SUCCESS (many to many test)
Test 19/20: SUCCESS (save override test)
Test 20/20: SUCCESS (performance test)

20 / 20 tests ran successfully
All tests completed in 99.646971s