{"id":13758105,"url":"https://github.com/kiddouk/redisco","last_synced_at":"2025-05-10T06:31:10.153Z","repository":{"id":2170868,"uuid":"3117399","full_name":"kiddouk/redisco","owner":"kiddouk","description":"A Python Library for Simple Models and Containers Persisted in Redis ","archived":false,"fork":true,"pushed_at":"2020-01-17T05:09:02.000Z","size":994,"stargazers_count":439,"open_issues_count":41,"forks_count":78,"subscribers_count":33,"default_branch":"master","last_synced_at":"2024-11-16T14:34:45.376Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":"iamteem/redisco","license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/kiddouk.png","metadata":{"files":{"readme":"README.rst","changelog":"changelog.txt","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":"2012-01-06T11:00:58.000Z","updated_at":"2024-05-25T01:12:34.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/kiddouk/redisco","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kiddouk%2Fredisco","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kiddouk%2Fredisco/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kiddouk%2Fredisco/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/kiddouk%2Fredisco/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/kiddouk","download_url":"https://codeload.github.com/kiddouk/redisco/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253376604,"owners_count":21898930,"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":[],"created_at":"2024-08-03T12:01:00.656Z","updated_at":"2025-05-10T06:31:09.890Z","avatar_url":"https://github.com/kiddouk.png","language":"Python","readme":"=======\nRedisco\n=======\nPython Containers and Simple Models for Redis\n\nDescription\n-----------\nRedisco allows you to store objects in Redis_. It is inspired by the Ruby library\nOhm_ and its design and code are loosely based on Ohm and the Django ORM.\nIt is built on top of redis-py_. It includes container classes that allow\neasier access to Redis sets, lists, and sorted sets.\n\n\nInstallation\n------------\nRedisco requires redis-py 2.0.0 so get it first.\n\n    pip install redis\n\nThen install redisco.\n\n    pip install redisco\n\n\n\nDocumentation\n-------------\nThe documentation is available at : https://redisco.readthedocs.org\n\n\nWhich version should I consider ?\n---------------------------------\n\n- v0.1\n\n.. image:: https://secure.travis-ci.org/kiddouk/redisco.png?branch=0.1\nIf you want something that is compatible with the original project developed by\nyou should consider v0.1. It works.\n\n- v0.2\n\n.. image:: https://secure.travis-ci.org/kiddouk/redisco.png?branch=0.2\nIf you are adventurous and want to try a version that is going closer to Ohm\nproject you should consider v0.2. Warning, your indexing keys will be broken\n(if you are planning to migrate).\n\n- master\n\n.. image:: https://secure.travis-ci.org/kiddouk/redisco.png?branch=master\nWell, expect things to be broken. Really broken.\n\n\nModels\n------\n\n::\n\n    from redisco import models\n    class Person(models.Model):\n        name = models.Attribute(required=True)\n        created_at = models.DateTimeField(auto_now_add=True)\n        fave_colors = models.ListField(str)\n\n    \u003e\u003e\u003e person = Person(name=\"Conchita\")\n    \u003e\u003e\u003e person.is_valid()\n    True\n    \u003e\u003e\u003e person.save()\n    True\n    \u003e\u003e\u003e conchita = Person.objects.filter(name='Conchita')[0]\n    \u003e\u003e\u003e conchita.name\n    'Conchita'\n    \u003e\u003e\u003e conchita.created_at\n    datetime.datetime(2010, 5, 24, 16, 0, 31, 954704)\n\n\nModel Attributes\n----------------\n\nAttribute\n    Stores unicode strings. If used for large bodies of text,\n    turn indexing of this field off by setting indexed=False.\n\nIntegerField\n    Stores an int. Ints are stringified using unicode() before saving to\n    Redis.\n\nCounter\n    An IntegerField that can only be accessed via Model.incr and Model.decr.\n\nDateTimeField\n    Can store a DateTime object. Saved in the Redis store as a float.\n\nDateField\n    Can store a Date object. Saved in Redis as a float.\n\nTimeDeltaField\n    Can store a TimeDelta object. Saved in Redis as a float.\n\nFloatField\n    Can store floats.\n\nBooleanField\n    Can store bools. Saved in Redis as 1's and 0's.\n\nReferenceField\n    Can reference other redisco model.\n\nListField\n    Can store a list of unicode, int, float, as well as other redisco models.\n\n\nAttribute Options\n-----------------\n\nrequired\n    If True, the attirbute cannot be None or empty. Strings are stripped to\n    check if they are empty. Default is False.\n\ndefault\n    Sets the default value of the attribute. Default is None.\n\nindexed\n    If True, redisco will create index entries for the attribute. Indexes\n    are used in filtering and ordering results of queries. For large bodies\n    of strings, this should be set to False. Default is True.\n\nvalidator\n    Set this to a callable that accepts two arguments -- the field name and\n    the value of the attribute. The callable should return a list of tuples\n    with the first item is the field name, and the second item is the error.\n\nunique\n    The field must be unique. Default is False.\n\nDateField and DateTimeField Options\n\nauto_now_add\n    Automatically set the datetime/date field to now/today when the object\n    is first created. Default is False.\n\nauto_now\n    Automatically set the datetime/date field to now/today everytime the object\n    is saved. Default is False.\n\n\nClass options\n-------------\n\nYou can specify some options in your Model to control the behaviour of the\nback scene.\n\n::\n\n    class User(models.Model):\n        firstname = models.Attribute()\n        lastname = models.Attribute()\n        \n        @property\n        def fullname(self):\n            return \"%s %s\" % (self.firstname, self.lastname)\n\n        class Meta:\n            indices = ['fullname']\n            db = redis.Redis(host=\"localhost\", db=\"6666\")\n            key = 'Account'\n\n\n``indices`` is used to add extra indices that will be saved in the model.\n``db`` object will be used instead of the global redisco ``redis_client``\n``key`` will be used as the main key in the redis Hash (and sub objects)\ninstead of the class name.\n\n\n\nCustom Managers\n---------------\n\nManagers are attached to Model attributes by looking for a ``__attr_name__``\nclass attribute. If not present, then it defaults to the lowercase attribute\nname in the Model.\n\n::\n\n    class User(models.Model):\n        firstname = models.Attribute()\n        lastname = models.Attribute()\n        active = models.BooleanField(default=True)        \n\n        class History(models.managers.Manager):\n            pass\n\n        class ObjectsManager(models.managers.Manager):\n            __attr_name__ = \"objects\"\n            def get_model_set(self):\n                return super(User.ObjectsManager, self).\\\n                    get_model_set().filter(active=True)\n\n\nSaving and Validating\n---------------------\n\nTo save an object, call its save method. This returns True on success (i.e. when\nthe object is valid) and False otherwise.\n\nCalling Model.is_valid will validate the attributes and lists. Model.is_valid\nis called when the instance is being saved. When there are invalid fields,\nModel.errors will hold the list of tuples containing the invalid fields and\nthe reason for its invalidity. E.g.\n[('name', 'required'),('name', 'too short')]\n\nFields can be validated using the validator argument of the attribute. Just\npass a callable that accepts two arguments -- the field name and the value\nof the attribute. The callable should return a list of errors.\n\nModel.validate will also be called before saving the instance. Override it\nto validate instances not related to attributes.\n\n::\n\n    def not_me(field_name, value):\n        if value == 'Me':\n            return ((field_name, 'it is me'),)\n\n    class Person(models.Model):\n        name = models.Attribute(required=True, validator=not_me)\n        age = models.IntegerField()\n\n        def validate(self):\n            if self.age and self.age \u003c 21:\n                self._errors.append(('age', 'below 21'))\n\n    \u003e\u003e\u003e person = Person(name='Me')\n    \u003e\u003e\u003e person.is_valid()\n    False\n    \u003e\u003e\u003e person.errors\n    [('name', 'it is me')]\n\n\nQueries\n-------\n\nQueries are executed using a manager, accessed via the objects class\nattribute.\n\n::\n\n    Person.objects.all()\n    Person.objects.filter(name='Conchita')\n    Person.objects.filter(name='Conchita').first()\n    Person.objects.all().order('name')\n    Person.objects.filter(fave_colors='Red')\n\nRanged Queries\n--------------\n\nRedisco has a limited support for queries involving ranges -- it can only\nfilter fields that are numeric, i.e. DateField, DateTimeField, IntegerField,\nand FloatField. The zfilter method of the manager is used for these queries.\n\n::\n\n    Person.objects.zfilter(created_at__lt=datetime(2010, 4, 20, 5, 2, 0))\n    Person.objects.zfilter(created_at__gte=datetime(2010, 4, 20, 5, 2, 0))\n    Person.objects.zfilter(created_at__in=(datetime(2010, 4, 20, 5, 2, 0), datetime(2010, 5, 1)))\n\n\nContainers\n----------\nRedisco has three containers that roughly match Redis's supported data\nstructures: lists, sets, sorted set. Anything done to the container is\npersisted to Redis.\n\nSets\n    \u003e\u003e\u003e from redisco.containers import Set\n    \u003e\u003e\u003e s = Set('myset')\n    \u003e\u003e\u003e s.add('apple')\n    \u003e\u003e\u003e s.add('orange')\n    \u003e\u003e\u003e s.add('bananas', 'tomatoes')\n    \u003e\u003e\u003e s.add(['blackberries', 'strawberries'])\n    \u003e\u003e\u003e s.members\n    set(['apple', 'blackberries', 'strawberries', 'orange', 'tomatoes', 'bananas'])\n    \u003e\u003e\u003e s.remove('apple', 'orange')\n    True\n    set(['strawberries', 'bananas', 'tomatoes', 'blackberries'])\n    \u003e\u003e\u003e s.remove(['bananas', 'blackberries'])\n    True\n    \u003e\u003e s.members\n    set(['strawberries', 'bananas', 'tomatoes'])\n    \u003e\u003e\u003e t = Set('nset')\n    \u003e\u003e\u003e t.add('kiwi')\n    \u003e\u003e\u003e t.add('guava')\n    \u003e\u003e\u003e t.members\n    set(['kiwi', 'guava'])\n    \u003e\u003e\u003e s.update(t)\n    \u003e\u003e\u003e s.members\n    set(['kiwi', 'orange', 'guava', 'apple'])\n\nLists\n    \u003e\u003e\u003e from redisco.containers import List\n    \u003e\u003e\u003e l = List('alpha')\n    \u003e\u003e\u003e l.append('a')\n    \u003e\u003e\u003e l.append(['b', 'c'])\n    \u003e\u003e\u003e l.append('d', 'e', 'f')\n    \u003e\u003e\u003e 'a' in l\n    True\n    \u003e\u003e\u003e 'd' in l\n    False\n    \u003e\u003e\u003e len(l)\n    6\n    \u003e\u003e\u003e l.index('b')\n    1\n    \u003e\u003e\u003e l.members\n    ['a', 'b', 'c', 'd', 'e', 'f']\n\n\nSorted Sets\n    \u003e\u003e\u003e zset = SortedSet('zset')\n    \u003e\u003e\u003e zset.members\n    ['d', 'a', 'b', 'c']\n    \u003e\u003e\u003e 'e' in zset\n    False\n    \u003e\u003e\u003e 'a' in zset\n    True\n    \u003e\u003e\u003e zset.rank('d')\n    0\n    \u003e\u003e\u003e zset.rank('b')\n    2\n    \u003e\u003e\u003e zset[1]\n    'a'\n    \u003e\u003e\u003e zset.add({'f' : 200, 'e' : 201})\n    \u003e\u003e\u003e zset.members\n    ['d', 'a', 'b', 'c', 'f', 'e']\n    \u003e\u003e\u003e zset.add('d', 99)\n    \u003e\u003e\u003e zset.members\n    ['a', 'b', 'c', 'd', 'f', 'e']\n\n\nDicts/Hashes\n    \u003e\u003e\u003e h = cont.Hash('hkey')\n    \u003e\u003e\u003e len(h)\n    0\n    \u003e\u003e\u003e h['name'] = \"Richard Cypher\"\n    \u003e\u003e\u003e h['real_name'] = \"Richard Rahl\"\n    \u003e\u003e\u003e h\n    \u003cHash 'hkey' {'name': 'Richard Cypher', 'real_name': 'Richard Rahl'}\u003e\n    \u003e\u003e\u003e h.dict\n    {'name': 'Richard Cypher', 'real_name': 'Richard Rahl'}\n\n\nAdditional Info on Containers\n-----------------------------\n\nSome methods of the Redis client that require the key as the first argument\ncan be accessed from the container itself.\n\n    \u003e\u003e\u003e l = List('mylist')\n    \u003e\u003e\u003e l.lrange(0, -1)\n    0\n    \u003e\u003e\u003e l.rpush('b')\n    \u003e\u003e\u003e l.rpush('c')\n    \u003e\u003e\u003e l.lpush('a')\n    \u003e\u003e\u003e l.lrange(0, -1)\n    ['a', 'b', 'c']\n    \u003e\u003e\u003e h = Hash('hkey')\n    \u003e\u003e\u003e h.hset('name', 'Richard Rahl')\n    \u003e\u003e\u003e h\n    \u003cHash 'hkey' {'name': 'Richard Rahl'}\u003e\n\n\nConnecting to Redis\n-------------------\n\nAll models and containers use a global Redis client object to\ninteract with the key-value storage. By default, it connects\nto localhost:6379, selecting db 0. If you wish to specify settings:\n\n::\n\n    import redisco\n    redisco.connection_setup(host='localhost', port=6380, db=10)\n\nThe arguments to connect are simply passed to the redis.Redis init method.\n\nFor the containers, you can specify a second argument as the Redis client.\nThat client object will be used instead of the default.\n\n    \u003e\u003e\u003e import redis\n    \u003e\u003e\u003e r = redis.Redis(host='localhost', port=6381)\n    \u003e\u003e\u003e Set('someset', r)\n\n\nUnit tests\n----------\n\nRedisco uses nose for testing. \n\nInstall nosetests:\n\n$ pip install nose\n\nAnd test:\n\n$ nosetests\n\n\nCredits\n-------\n\nMost of the concepts are taken from `Soveran`_'s Redis related Ruby libraries.\ncyx_ for sharing his expertise in indexing in Redis.\nDjango, of course, for the popular model API.\n\n.. _Redis: http://code.google.com/p/redis/\n.. _Ohm: http://github.com/soveran/ohm/\n.. _redis-py: http://github.com/andymccurdy/redis-py/\n.. _`Soveran`: http://github.com/soveran\n.. _cyx: http://github.com/cyx\n","funding_links":[],"categories":["ORM","资源列表","Python","ODM, ORM, Active Record","ORM [🔝](#readme)","Awesome Python","Web Frameworks \u0026 RESTful API"],"sub_categories":["ORM"],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkiddouk%2Fredisco","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkiddouk%2Fredisco","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkiddouk%2Fredisco/lists"}