Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Idea: OrderableMixin #42

Open
M0r13n opened this issue Jul 19, 2020 · 0 comments
Open

Idea: OrderableMixin #42

M0r13n opened this issue Jul 19, 2020 · 0 comments

Comments

@M0r13n
Copy link

M0r13n commented Jul 19, 2020

Hey there!

I sometimes need to make object comparable to each other and define a custom order. For example some records should be orderable by an enduser.

Up to now I always wrote some custom logic for every project. Now I decided to make it a bit more abstract and universal.

I thought about something like this:

from sqlalchemy import func, Column, Integer
from sqlalchemy.engine.default import DefaultExecutionContext
from sqlalchemy.sql.expression import select
from sqlalchemy.sql.selectable import Select
from sqlalchemy.engine.base import Engine
from sqlalchemy.engine.result import RowProxy


def default_order_index(context: DefaultExecutionContext) -> int:
    """
    Get the current highest index for each model
    """
    if context:
        engine: Engine = context.engine
        try:
            query: Select = select([func.max(1, func.max(context.current_column.table.c.order_index) + 1)])
            r: RowProxy = engine.execute(query).fetchone()
            i: int = int(r[0])
            return i
        except (TypeError, IndexError):
            return 0
    else:
        return 0


class OrderableMixin(object):
    """
    Mixin to make database models comparable.
    --
    The highest object has the lowest index (e.g. ZERO (0))

    The lowest object has the highest index (.e.g INF (9999999999))

    """

    order_index = Column(Integer,
                         default=default_order_index,
                         index=True)

    @classmethod
    def normalize(cls) -> None:
        """ Normalize all order indexes """

        for idx, item in enumerate(cls.query.order_by(cls.order_index).all()):
            item.order_index = idx

    def move_up(self) -> None:
        """ Move the database object one up -> decreases index by one"""
        if self.order_index == 0:
            return

        # get all items ordered by their index
        items = self.query.order_by(self.__class__.order_index).all()
        idx = items.index(self)

        # swap with item above
        above = items[idx - 1]
        above.order_index, self.order_index = idx, above.order_index

    def move_down(self) -> None:
        """ Move the database object one down -> increases index by on"""

        # get all items ordered by their index
        items = self.query.order_by(self.__class__.order_index).all()
        idx = items.index(self)

        # if item is last do nothing
        if idx == len(items) - 1:
            return

        # swap with item below
        below = items[idx + 1]
        below.order_index, self.order_index = idx, below.order_index

This could then be used as follows:

Base = declarative_base()


class BaseModel(Base, SessionMixin):
    __abstract__ = True


class SampleModel(BaseModel, OrderableMixin):
    __tablename__ = "samples"

    id = sa.Column(sa.Integer, primary_key=True)
    name = sa.Column(sa.String(255))

obj = SampleModel()

obj.move_up()
obj.move_down()

What do you guys think about that?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant