Metadata-Version: 1.2
Name: redis-limpyd-extensions
Version: 2.0.dev0
Summary: Some extensions for redis-limpyd, a redis orm (sort of) in python.
Home-page: https://github.com/limpyd/redis-limpyd-extensions
Author: Stephane "Twidi" Angel
Author-email: s.angel@twidi.com
License: WTFPL
Description: |PyPI Version| |Build Status|
        
        redis-limpyd-extensions
        =======================
        
        Some extensions for
        `redis-limpyd <https://github.com/limpyd/redis-limpyd>`__
        (`redis <http://redis.io>`__ orm (sort of) in python)
        
        Where to find it:
        
        -  Github repository: https://github.com/limpyd/redis-limpyd-extensions
        -  Pypi package: https://pypi.python.org/pypi/redis-limpyd-extensions
        -  Documentation: http://documentup.com/limpyd/redis-limpyd-extensions
        
        Install:
        
        Python versions 2.7, and 3.5 to 3.8 are supported (CPython and PyPy).
        
        Redis-server versions >= 3 are supported.
        
        Redis-py versions >= 3 are supported.
        
        Redis-limpyd versions >= 2 are supported.
        
        You can still use limpyd-extensions versions < 2 if you need something older than the above requirements.
        
        .. code:: bash
        
            pip install redis-limpyd-extensions
        
        List of available extensions:
        
        -  Add/remove related on both sides
        -  Dynamic fields
        
        Add/remove related on both sides
        --------------------------------
        
        Say we have the following related models:
        
        .. code:: python
        
                class Person(RelatedModel):
                    database = main_database
                    name = HashableField(indexable=True)
        
                class Group(Relatedmodel):
                    database = main_database
                    name = HashableField(indexable=True)
                    members = M2MSetField(Person, related_name='membership')
        
        And some data:
        
        .. code:: python
        
                somebody = Person(name='foobar')
                group_1 = Group(name='group 1')
                group_2 = Group(name='group 2')
                group_3 = Group(name='group 3')
        
        We can add membership the normal way:
        
        .. code:: python
        
                group_1.members.sadd(somebody)
        
        And retrieving then this way:
        
        .. code:: python
        
                group_1_members = group_1.members()  # somebody !
                somebody_membership = somebody.membership()  # group_1
        
        But say that we want to put a person in many groups at ones, we can do:
        
        .. code:: python
        
                group_2.members.sadd(somebody)
                group_3.members.sadd(somebody)
        
        ``limpyd_extensions`` provide a way to add/remove relations via the
        other side of the relation:
        
        .. code:: python
        
                somebody.membership.sadd(group2, group3)
        
        To use this, simple import the related fields from
        ``limpyd_extensions.related`` instead of ``limpyd.contrib.related``:
        
        .. code:: python
        
            from limpyd_extensions.related import (FKStringField, FKHashableField,
                                                   M2MSetField, M2MListField,
                                                   M2MSortedSetField)
        
        And use them as usual. (Note that for convenience you can also import
        the standard ``RelatedModel`` from there)
        
        The added methods for the reverse side of each related field are:
        
        FKStringField
        ~~~~~~~~~~~~~
        
        -  ``sadd``, to set the reverse relation as the fk of the arguments:
        
        Having:
        
        .. code:: python
        
                class Group(RelatedModel):
                    parent = FKStringField(self, related_name='children')
        
        The standard:
        
        .. code:: python
        
                child_group.parent.set(main_group)
                other_child_group.parent.set(main_group)
        
        is the same as the new:
        
        .. code:: python
        
                main_group.children.sadd(child_group, other_child_group)
        
        -  ``srem`` works the same way as ``sadd`` but for deleting fk:
        
        The standard:
        
        .. code:: python
        
                child_group.parent.delete(main_group)
                other_child_group.parent.delete(main_group)
        
        is the same as the new:
        
        .. code:: python
        
                main_group.children.srem(child_group, other_child_group)
        
        FKHashableField
        ~~~~~~~~~~~~~~~
        
        -  ``sadd``
        -  ``srem``
        
        Both work the exact same way as for FKStringField, the only difference
        is that ``sadd`` emulates a ``hset``, not a ``set``.
        
        M2MSetField
        ~~~~~~~~~~~
        
        -  ``sadd``
        
        The standard:
        
        .. code:: python
        
                group_2.members.sadd(somebody)
                group_3.members.sadd(somebody)
        
        is the same as the new:
        
        .. code:: python
        
                somebody.membership.sadd(group2, group3)
        
        -  ``srem`` works the same way as ``sadd`` but for removing relations:
        
        The standard:
        
        .. code:: python
        
                group_2.members.srem(somebody)
                group_3.members.srem(somebody)
        
        is the same as the new:
        
        .. code:: python
        
                somebody.membership.srem(group2, group3)
        
        M2MListField
        ~~~~~~~~~~~~
        
        -  ``lpush`` and ``rpush``, that works for ``M2MListField`` like
           ``sadd`` for ``M2MSetField``
        
        If in our Person/Group example ``members`` is a ``M2MListField`` instead
        of a ``M2MSetField``,
        
        The standard:
        
        .. code:: python
        
                group_2.members.rpush(somebody)
                group_3.members.rpush(somebody)
        
        is the same as the new:
        
        .. code:: python
        
                somebody.membership.rpush(group2, group3)
        
        -  ``lrem`` works the same way as ``rpush`` and ``lpush`` but for
           removing relations:
        
        The standard:
        
        .. code:: python
        
                group_2.members.lrem(0, somebody)  # 0 for "all occurences"
                group_3.members.lrem(0, somebody)
        
        is the same as the new:
        
        .. code:: python
        
                somebody.membership.lrem(group2, group3)  # the count is forced to 0
        
        M2MSortedSetField
        ~~~~~~~~~~~~~~~~~
        
        -  ``zadd`` that works for ``M2MSortedSetField`` like ``sadd`` for
           ``M2MSetField``, but managing scores. Arguments can be set the same
           way as the normal ``zadd`` command.
        
        If in our Person/Group example ``members`` is a ``M2MSortedSetField``
        instead of a ``M2MSetField``, using the score to save the date of
        membership
        
        The standard:
        
        .. code:: python
        
                group_2.members.zadd({somebody: sometime})  # sometime, a float, can be a call to time.time()
                group_3.members.zadd({somebody: another_time})
        
        is the same as the new:
        
        .. code:: python
        
                somebody.membership.zadd({group2: sometime, group3: another_time})
        
        -  ``zrem`` works the same way as ``zadd``, without the score, but for
           removing relations:
        
        The standard:
        
        .. code:: python
        
                group_2.members.zrem(somebody)
                group_3.members.zrem(somebody)
        
        is the same as the new:
        
        .. code:: python
        
                somebody.membership.zrem(group2, group3)
        
        Dynamic fields
        --------------
        
        Dynamic fields provide a way to add unlimited fields to a model by
        defining a (or many) dynamic field, and use it with a dynamic part. ie a
        dynamic field name "foo" can be used with as many dynamic parts as you
        want to create dynamic variations: "foo\_bar" for the dynamic part
        "bar", "foo\_baz" for the dynamic part "baz", and so on.
        
        A simple API to use them, and filter on them, is provided.
        
        To use a dynamic field, your model must inherit from the following
        mixin: ``ModelWithDynamicFieldMixin``, found in
        ``limpyd_extensions.dynamic.model``. It's a mixin, you should use it
        with another ``RedisModel`` class. Fields are available as field classes
        (``DynamicStringField``, ``DynamicInstanceHashField``,
        ``DynamicListField``, ``DynamicSetField``, ``DynamicSortedSetField``,
        ``DynamicHashField``) or as a mixin (``DynamicFieldMixin``) if you want
        to adapt an external field. You can find them in
        ``limpyd_extensions.dynamic.fields``
        
        A short example on how to define a dynamic field on a model:
        
        .. code:: python
        
            from limpyd.model import RedisModel
        
            from limpyd_extension.dynamic.model import ModelWithDynamicFieldMixin
            from limpyd_extension.dynamic.fields import DynamicSetField
        
        
            class MyModel(ModelWithDynamicFieldMixin, RedisModel):
                foo = DynamicSetField(indexable=True)
        
        As the ``foo`` field is dynamic, you cannot run any command on it, but
        only on its dynamic variations. How to do it ?
        
        There is two ways:
        
        -  use the ``get_field`` method of the model:
        
        .. code:: python
        
            foo_bar = myinstance.get_field('foo_bar')
        
        -  use the ``get_for`` method of the field:
        
        .. code:: python
        
            foo_bar = myinstance.foo.get_for('bar')
        
        The latter is useful if you have a variable instead of known value:
        
        .. code:: python
        
            somebar = 'bar'
            foo_bar = myinstance.foo.get_for(somevar)
        
        Note that you can use this shortcut instead of using ``get_for``:
        
        .. code:: python
        
            foo_bar = myinstance.foo(somevar)
        
        Knowing this, you can do operations on these fields:
        
        .. code:: python
        
            myinstance.foo(somevar).sadd('one', 'two', 'three')
            myinstance.foo(othervar).sadd('four', 'five')
            myotherinstance.foo(somevar).sadd('three', 'thirty')
            print myinstance.foo(somevar).smembers()
            print myinstance.foo(othervar).smembers()
            print myotherinstance.foo(somevar).smembers()
        
        
        To know the existing versions in a dynamic_field, you can use ``scan_fields``.
        
        It takes the same argument as the ``sscan`` command of ``SetField`` (from limpyd), because it is applied on the inventory key where all versions are saved.
        
        So if you have some versions:
        
        .. code::python
        
            myinstance.foo('foo').set('111')
            myinstance.foo('bar').set('222')
            myinstance.foo('baz').set('333')
        
        You can retrieve them all:
        
        .. code::python
        
            set(myinstance.foo.scan_versions())  # returns {'foo', 'bar', 'baz'}
        
        Or only a part:
        
        .. code::python
        
            set(myinstance.foo.scan_versions('b*'))  # returns {'bar', 'baz'}
        
        
        Filtering
        ~~~~~~~~~
        
        To filter on indexable dynamic fields, there is two ways too:
        
        -  use the classic way, if you now the dynamic part in advance:
        
        .. code:: python
        
            MyModel.collection(foo_bar='three')
        
        -  use the new ``dynamic_filter`` method:
        
        .. code:: python
        
            MyModel.collection().dynamic_filter('foo', 'bar', 'three')
        
        Parameters are: the field name, the dynamic part, the value for the
        filter and, not show in the previous example, the index suffix to use.
        
        This suffix is default to ''.
        
        But if what you want to do is
        
        .. code:: python
        
            MyModel.collection(foo_bar__eq='three')
        
        You can use ``dynamic_filter`` this way:
        
        .. code:: python
        
            MyModel.collection().dynamic_filter('foo', 'bar', 'three', 'eq')  # you can use '__eq' too
        
        
        The collection manager used with ``ModelWithDynamicFieldMixin`` depends
        on ``ExtendedCollectionManager``, so you can chain filters and dynamic
        filters on the resulting collection.
        
        Dynamic related fields
        ~~~~~~~~~~~~~~~~~~~~~~
        
        Dynamic fields also work with related fields, exactly the same way.
        There is only two additions:
        
        -  if you pass a model instance in the ``get_for`` method, it will be
           translated to it's pk
        -  the first argument of a "related collection" is the dynamic part (can
           also be an instance)
        
        An exemple using dynamic related fields:
        
        .. code:: python
        
            from limpyd.fields import PKField
            from limpyd_extensions.dynamic.model import ModelWithDynamicFieldMixin
            from limpyd_extensions.dynamic.related import DynamicM2MSetField
        
            class Tag(MyBaseModel):
                slug = PKField()
        
            class Person(MyBaseModel):
                name = PKField()
        
            class Movie(ModelWithDynamicFieldMixin, MyBaseModel):
                name = PKField()
                tags = DynamicM2MSetField(Tag, related_name='movies')
        
            somebody = Person(name='Somebody')
            matrix = Movie(name='Matrix')
            cool = Tag(name='cool')
        
            matrix.tags.get_for(somebody).sadd(cool)
            # same as: matrix.tags(somebody).sadd(cool)
        
            cool_movies_for_somebody = cool.movies(somebody)  # the related collection
            # ['Matrix']
        
        Provided classes
        ~~~~~~~~~~~~~~~~
        
        Here is the list of modules and classes provided with the
        ``limpyd_extensions.dynamic`` module:
        
        -  **model**
        
           -  **mixins**
        
              -  ``ModelWithDynamicFieldMixin(object)`` - A mixin tu use for
                 your model with dynamic fields
        
        -  **collection**
        
           -  **mixins**
        
              -  ``CollectionManagerForModelWithDynamicFieldMixin(object)`` - A
                 mixin to use if you want to add the ``dynamic_filter`` method
                 to your own collection manager
        
           -  **full classes**
        
              -  ``CollectionManagerForModelWithDynamicField(CollectionManagerForModelWithDynamicFieldMixin, ExtendedCollectionManager)``
                 - A simple class inheriting from our mixin and the manager from
                 ``limpyd.contrib.collection``
        
        -  **field**
        
           -  **mixins**
        
              -  ``DynamicFieldMixin(object)`` - A mixin within all the stuff
                 for dynamic fields is done, to use to add dynamic field support
                 to your own fields
        
           -  **full classes** All fields simply inherits from our mixin and the
              wanted base field, without anymore addition:
        
              -  ``DynamicStringField(DynamicFieldMixin, StringField)``
              -  ``DynamicInstanceHashField(DynamicFieldMixin, InstanceHashField)``
              -  ``DynamicListField(DynamicFieldMixin, ListField)``
              -  ``DynamicSetField(DynamicFieldMixin, SetField)``
              -  ``DynamicSortedSetField(DynamicFieldMixin, SortedSetField)``
              -  ``DynamicHashField(DynamicFieldMixin, HashField)``
        
        -  **related**
        
           -  **mixins**
        
              -  ``DynamicRelatedFieldMixin(DynamicFieldMixin)`` - A mixin
                 within all the stuff for dynamic related fields is done, to use
                 to add dynamic field support to your own related fields
        
           -  **full classes**
        
              -  ``DynamicFKStringField(DynamicRelatedFieldMixin, FKStringField)``
              -  ``DynamicFKInstanceHashField(DynamicRelatedFieldMixin, FKInstanceHashField)``
              -  ``DynamicM2MSetField(DynamicRelatedFieldMixin, M2MSetField)``
              -  ``DynamicM2MListField(DynamicRelatedFieldMixin, M2MListField)``
              -  ``DynamicM2MSortedSetField(DynamicRelatedFieldMixin, M2MSortedSetField)``
        
        
        .. |PyPI Version| image:: https://img.shields.io/pypi/v/redis-limpyd-extensions.png
           :target: https://pypi.python.org/pypi/redis-limpyd-extensions
        .. |Build Status| image:: https://travis-ci.org/limpyd/redis-limpyd-extensions.png?branch=master
           :target: https://travis-ci.org/limpyd/redis-limpyd-extensions
        
Keywords: redis,orm
Platform: any
Classifier: Development Status :: 5 - Production/Stable
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Topic :: Database
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 2
Classifier: Programming Language :: Python :: 2.7
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.5
Classifier: Programming Language :: Python :: 3.6
Classifier: Programming Language :: Python :: 3.7
Classifier: Programming Language :: Python :: 3.8
Classifier: Programming Language :: Python :: Implementation :: CPython
Classifier: Programming Language :: Python :: Implementation :: PyPy
Requires-Python: !=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,>=2.7
