Package: django-redis-orm
django-redis-ormDjango Redis ORM library that gives redis easy-to-use objects with fields and speeds a development upFor 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 librarypython-redis-orm(PyPI) and this library is 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 deserealizing datasave_consistencysetting - show structure-first dataeconomysetting - to not return full data and save some requests (usually, speeds up your app on 80%)10 built-in types of fields:RedisField- base class for nestingRedisString- stringRedisNumber- int or floatRedisId- instances IDsRedisDateTime- for work with date and time, via python datetimeRedisForeignKey- for links to other redis modelsRedisDjangoForeignKey- for links to other Django modelsRedisJson- for data, that can be JSONedRedisList- listRedisDict- dictAll fields supports:Automatically serializationAutomatically deserializationTTL (Time To Live) settingDefault valuesProviding functions to default valuesAllow null values settingChoicesBuilt-in RedisModel class, with:All fields that you wantTTL (Time To Live), applies if no ttl on fieldCRUD (Create Read Update Delete), in our variation: save, get, filter, order, update, delete:example_instance = ExampleModel(example_field='example_data').save()- to create an instance and get its data dictfiltered_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_instancesInstallationpip install django-redis-ormHere is PyPIAdd "django_redis_orm" to your INSTALLED_APPS setting like this::INSTALLED_APPS = [
    ...
    'django_redis_orm',
]UsageAll features:full_test.pyimportdatetimeimportrandomfromtimeimportsleepfrompython_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,ttl=5)classMetaTtlCheckModel(RedisModel):redis_number=RedisNumber(default=0,null=False)classMeta:ttl=5classDictCheckModel(RedisModel):redis_dict=RedisDict()classListCheckModel(RedisModel):redis_list=RedisList()# class DjangoForeignKeyModel(RedisModel):#     foreign_key = RedisDjangoForeignKey(model=Proxy)defget_redis_instance(connection_pool=None):REDIS_HOST='localhost'REDIS_PORT=6379redis_instance=redis.Redis(decode_responses=True,host=REDIS_HOST,port=REDIS_PORT,db=0,connection_pool=connection_pool)returnredis_instancedefclean_db_after_test(redis_instance,prefix):forkeyinredis_instance.scan_iter(f'{prefix}:*'):redis_instance.delete(key)defbasic_test(redis_instance,prefix):try:redis_root=RedisRoot(prefix=prefix,redis_instance=redis_instance,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(redis_instance,prefix)returnhave_exceptiondefauto_reg_test(redis_instance,prefix):redis_root=RedisRoot(prefix=prefix,redis_instance=redis_instance,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(redis_instance,prefix)returnhave_exceptiondefno_redis_instance_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=Falseclean_db_after_test(redis_root.redis_instance,redis_root.prefix)exceptBaseExceptionasex:have_exception=Truereturnhave_exceptiondefchoices_test(redis_instance,prefix):redis_root=RedisRoot(prefix=prefix,redis_instance=redis_instance,ignore_deserialization_errors=True)task_challenge_1=TaskChallenge(redis_root=redis_root,status='bruh',)try:task_challenge_1.save()task_challenges=redis_root.get(TaskChallenge)have_exception=TrueexceptBaseExceptionasex:have_exception=Falseclean_db_after_test(redis_instance,prefix)returnhave_exceptiondeforder_test(redis_instance,prefix):redis_root=RedisRoot(prefix=prefix,redis_instance=redis_instance,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(redis_instance,prefix)returnhave_exceptiondeffilter_test(redis_instance,prefix):redis_root=RedisRoot(prefix=prefix,redis_instance=redis_instance,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(redis_instance,prefix)returnhave_exceptiondefupdate_test(redis_instance,prefix):redis_root=RedisRoot(prefix=prefix,redis_instance=redis_instance,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(redis_instance,prefix)returnhave_exceptiondeffunctions_like_defaults_test(redis_instance,prefix):redis_root=RedisRoot(prefix=prefix,redis_instance=redis_instance,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(redis_instance,prefix)returnhave_exceptiondefredis_foreign_key_test(redis_instance,prefix):redis_root=RedisRoot(prefix=prefix,redis_instance=redis_instance,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(redis_instance,prefix)returnhave_exception# def django_foreign_key_test(redis_instance, prefix):#     redis_root = RedisRoot(#         prefix=prefix,#         redis_instance=redis_instance,#         ignore_deserialization_errors=True#     )#     have_exception = True#     try:#         proxy = Proxy.objects.all()[0]#         django_foreign_key_model = DjangoForeignKeyModel(#             redis_root=redis_root,#             foreign_key=proxy,#         ).save()#         django_foreign_key_models = redis_root.get(DjangoForeignKeyModel)#         django_foreign_key_model = redis_root.order(django_foreign_key_models, '-id')[0]#         if django_foreign_key_model['foreign_key'] == proxy:#             have_exception = False#     except BaseException as ex:#         print(ex)#     clean_db_after_test(redis_instance, prefix)#     return have_exceptiondefdelete_test(redis_instance,prefix):redis_root=RedisRoot(prefix=prefix,redis_instance=redis_instance,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(redis_instance,prefix)returnhave_exceptiondefttl_test(redis_instance,prefix):redis_root=RedisRoot(prefix=prefix,redis_instance=redis_instance,ignore_deserialization_errors=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'notinttl_check_model.keys():have_exception=FalseexceptBaseExceptionasex:print(ex)clean_db_after_test(redis_instance,prefix)returnhave_exceptiondefsave_consistency_test(redis_instance,prefix):redis_root=RedisRoot(prefix=prefix,redis_instance=redis_instance,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(redis_instance,prefix)returnhave_exceptiondefmeta_ttl_test(redis_instance,prefix):redis_root=RedisRoot(prefix=prefix,redis_instance=redis_instance,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(redis_instance,prefix)returnhave_exceptiondefeconomy_test(redis_instance,prefix):have_exception=Truetry:redis_root=RedisRoot(prefix=prefix,redis_instance=redis_instance,ignore_deserialization_errors=True,economy=True)started_in_economy=datetime.datetime.now()foriinrange(10):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_economy=datetime.datetime.now()economy_time=(ended_in_economy-started_in_economy).total_seconds()clean_db_after_test(redis_instance,prefix)redis_root=RedisRoot(prefix=prefix,redis_instance=redis_instance,ignore_deserialization_errors=True,economy=False)started_in_no_economy=datetime.datetime.now()foriinrange(10):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_economy=datetime.datetime.now()no_economy_time=(ended_in_no_economy-started_in_no_economy).total_seconds()clean_db_after_test(redis_instance,prefix)economy_percent=round((no_economy_time/economy_time-1)*100,2)economy_symbol=('+'ifeconomy_percent>0else'')print(f'Economy gives{economy_symbol}{economy_percent}% efficiency')ifeconomy_symbol=='+':have_exception=FalseexceptBaseExceptionasex:print(ex)clean_db_after_test(redis_instance,prefix)returnhave_exceptiondefdict_test(redis_instance,prefix):redis_root=RedisRoot(prefix=prefix,redis_instance=redis_instance,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(redis_instance,prefix)returnhave_exceptiondeflist_test(redis_instance,prefix):redis_root=RedisRoot(prefix=prefix,redis_instance=redis_instance,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(redis_instance,prefix)returnhave_exceptiondefasync_test(redis_instance,prefix):have_exception=Truetry:asyncdefasync_task(data_count,use_async=True):redis_roots=[]connection_pool=redis.ConnectionPool(host='localhost',port=6379,db=0,decode_responses=True)foriinrange(data_count):redis_instance=get_redis_instance(connection_pool)redis_root=RedisRoot(prefix=prefix,redis_instance=redis_instance,ignore_deserialization_errors=True)redis_roots.append(redis_root)asyncdefcreate_list(redis_roots,redis_root_index,list_length):some_list=[random.choice('ABCDEF')foriinrange(list_length)]redis_root=redis_roots[redis_root_index]list_check_model_instance=ListCheckModel(redis_root=redis_root,redis_list=some_list).save()return{'list':some_list,'id':list_check_model_instance['id']}asyncdefget_object_existence(redis_roots,redis_root_index,lists_with_ids):exists=Falseredis_root=redis_roots[redis_root_index]objects=redis_root.get(ListCheckModel,redis_list=lists_with_ids['list'])ifobjects:iflen(objects)==1:exists=objects[0]['id']==lists_with_ids['id']returnexistsasyncdefupdate_list(redis_roots,redis_root_index,lists_with_ids):result=Falseredis_root=redis_roots[redis_root_index]objects=redis_root.get(ListCheckModel,redis_list=lists_with_ids['list'])ifobjects:iflen(objects)==1:obj=objects[0]new_list=deepcopy(lists_with_ids['list'])new_list.append('check_value')updated_obj=redis_root.update(ListCheckModel,obj,redis_list=new_list)updated_objects=redis_root.get(ListCheckModel,redis_list=new_list)ifupdated_objects:iflen(updated_objects)==1:result=updated_objects[0]['id']==lists_with_ids['id']returnresultasyncdefdelete_list(redis_roots,redis_root_index,id):result=Falseredis_root=redis_roots[redis_root_index]objects=redis_root.get(ListCheckModel,id=id)ifobjects:iflen(objects)==1:obj=objects[0]redis_root.delete(ListCheckModel,obj)objects=redis_root.get(ListCheckModel,id=id)ifnotobjects:result=Truereturnresultasyncdefcreate_lists(lists_count,use_async=True):ifuse_async:async_create_lists_tasks=[create_list(redis_roots,i,i+100)foriinrange(lists_count)]list_of_lists_with_ids=awaitasyncio.gather(*async_create_lists_tasks)else:list_of_lists_with_ids=[awaitcreate_list(redis_roots,i,i+100)foriinrange(lists_count)]returnlist_of_lists_with_idsasyncdefget_objects_existence(list_of_lists_with_ids,use_async=True):ifuse_async:async_get_object_by_list_tasks=[get_object_existence(redis_roots,i,lists_with_ids)fori,lists_with_idsinenumerate(list_of_lists_with_ids)]list_of_results=awaitasyncio.gather(*async_get_object_by_list_tasks)else:list_of_results=[awaitget_object_existence(redis_roots,i,lists_with_ids)fori,lists_with_idsinenumerate(list_of_lists_with_ids)]returnlist_of_resultsasyncdefupdate_lists(list_of_lists_with_ids,use_async=True):ifuse_async:async_update_list_tasks=[update_list(redis_roots,i,lists_with_ids)fori,lists_with_idsinenumerate(list_of_lists_with_ids)]list_of_results=awaitasyncio.gather(*async_update_list_tasks)else:list_of_results=[awaitupdate_list(redis_roots,i,lists_with_ids)fori,lists_with_idsinenumerate(list_of_lists_with_ids)]returnlist_of_resultsasyncdefdelete_lists(list_of_lists_with_ids,use_async=True):ifuse_async:async_delete_list_tasks=[delete_list(redis_roots,i,lists_with_ids['id'])fori,lists_with_idsinenumerate(list_of_lists_with_ids)]list_of_results=awaitasyncio.gather(*async_delete_list_tasks)else:list_of_results=[awaitdelete_list(redis_roots,i,lists_with_ids)fori,lists_with_idsinenumerate(list_of_lists_with_ids)]returnlist_of_resultslist_of_lists_with_ids=awaitcreate_lists(data_count,use_async)list_of_results=awaitget_objects_existence(list_of_lists_with_ids,use_async)ifall(list_of_results):list_of_results=awaitupdate_lists(list_of_lists_with_ids,use_async)ifall(list_of_results):list_of_results=awaitdelete_lists(list_of_lists_with_ids,use_async)returnall(list_of_results)data_count=10async_started_in=datetime.datetime.now()async_result=asyncio.run(async_task(data_count))async_ended_in=datetime.datetime.now()async_time=(async_ended_in-async_started_in).total_seconds()have_exception=notasync_resultsync_started_in=datetime.datetime.now()sync_result=asyncio.run(async_task(data_count,False))sync_ended_in=datetime.datetime.now()sync_time=(sync_ended_in-sync_started_in).total_seconds()async_percent=round((async_time/sync_time-1)*100,2)async_symbol=('+'ifasync_percent>0else'')print(f'Async gives{async_symbol}{async_percent}% efficiency')exceptBaseExceptionasex:print(ex)clean_db_after_test(redis_instance,prefix)returnhave_exceptiondefrun_tests():redis_instance=get_redis_instance()tests=[basic_test,auto_reg_test,no_redis_instance_test,choices_test,order_test,filter_test,functions_like_defaults_test,redis_foreign_key_test,# django_foreign_key_test,update_test,delete_test,ttl_test,save_consistency_test,meta_ttl_test,economy_test,list_test,dict_test,async_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(redis_instance,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')if__name__=='__main__':run_tests()