Module lib.repository

Repository types reside in this module

Expand source code
"""
Repository types reside in this module
"""
from abc import abstractmethod

layers = []


def _call_layers(action, repo_cls, model_id=None, new_model=None, id_attr='id'):  # pylint: disable=too-many-branches
    """
    Utility method to call all layers

    Pylint: too-many-branches disabled because a dictionary dispatch system would not work here.
    :param action: str. Identifies what layer action to take
    :param repo_cls: The repository class where the action is done.
    :param model_id: Optional. ID desired
    :param new_model: Optional. If a new model is made or read, this is handed to the layer method
    :param id_attr: Optional. Name of ID attribute (default is 'id')
    :return: None
    """
    if model_id is not None and new_model is not None:
        raise ValueError('Either model_id or new_model needs to be specified!')

    if action == 'create':
        for layer in layers:
            layer.on_create(repo_cls, new_model)
        return
    if action == 'read_one':
        for layer in layers:
            layer.on_read_one(repo_cls, new_model)
        return
    if action == 'read_many':
        for model in new_model:
            for layer in layers:
                layer.on_read_one(repo_cls, model)
        return
    if action == 'update':
        for layer in layers:
            layer.on_update(repo_cls, new_model, id_attr=id_attr)
        return
    if action == 'destroy':
        for layer in layers:
            layer.on_destroy(repo_cls, model_id, id_attr=id_attr)
    else:
        raise ValueError('Invalid action specified')


class CanMutateData:
    """
    Attribute for Repositories that allow for data mutation after reading
    """

    @classmethod
    @abstractmethod
    def after_read(cls, model_read):
        """
        Called after a model is read
        :param model_read: the model object read from data source
        :return: raises Exception if interrupt is desired
        """
        raise NotImplementedError

    @classmethod
    @abstractmethod
    def after_read_many(cls, models_read):
        """
        Called after many models are read
        :param models_read: model objects read from data source
        :return: raises Exception if interrupt is desired
        """
        raise NotImplementedError


class Repository(CanMutateData):
    """
    Base Repository Class for basic CRUD operations
    """
    id_attr: str = 'id'

    @classmethod
    def on_create(cls, model):
        """
        Calls the on_create method on all registered layers

        Repository implementations MUST call this manually, as layers
        are an opt-in process
        :param model: model newly created
        :return: None
        """
        _call_layers('create', cls, new_model=model)

    @classmethod
    def on_update(cls, model):
        """
        Calls the on_update method on all registered layers

        Repository implementations MUST call this manually, as layers
        are an opt-in process
        :param model: model newly updated
        :return: None
        """
        _call_layers('update', cls, new_model=model, id_attr=cls.id_attr)

    @classmethod
    def on_destroy(cls, model_id):
        """
        Calls the on_destroy method on all registered layers

        Repository implementations MUST call this manually, as layers
        are an opt-in process
        :param model_id: ID of deleted model (or to-be deleted)
        :return: None
        """
        _call_layers('destroy', cls, model_id=model_id, id_attr=cls.id_attr)

    @classmethod
    @abstractmethod
    def create(cls, model):
        """
        Creates the model in the data sink of choice
        :param model: instance with data
        :return: model
        """
        raise NotImplementedError

    @classmethod
    @abstractmethod
    def read(cls, model_id):
        """
        Read a single Model by it's ID
        :param model_id: model ID
        :param id_attr: Optional. Name of ID attribute (default is 'id')
        :return: model
        """
        raise NotImplementedError

    @classmethod
    @abstractmethod
    def read_by(cls, filters=None):
        """
        Reads all models that fit into the filters
        :return: list of models
        """
        raise NotImplementedError

    @classmethod
    @abstractmethod
    def read_all(cls):
        """
        Reads all models
        :return: list of models
        """
        raise NotImplementedError

    @classmethod
    @abstractmethod
    def update(cls, model):
        """
        Uses the 'id' column to update the model
        :param model: model instance
        :return: model
        """
        raise NotImplementedError

    @classmethod
    @abstractmethod
    def destroy(cls, model_id):
        """
        Deletes the model from the data sink
        :param model_id: model's ID to destroy
        :return: None
        """
        raise NotImplementedError

    @classmethod
    def after_read(cls, model_read):
        _call_layers('read_one', cls, new_model=model_read, id_attr=cls.id_attr)

    @classmethod
    def after_read_many(cls, models_read):
        _call_layers('read_many', cls, new_model=models_read)

    @property
    @classmethod
    @abstractmethod
    def resource_uri(cls):
        """
        This is a helper to make the table/file/URI name more obvious.

        For example, the table() function in a DatabaseRepo should use
        this property to find the table name
        :return: Resource URI
        """
        raise AttributeError('resource_uri: No resource URI set!')

Sub-modules

lib.repository.db

Database Repository with helper methods for setup

lib.repository.validator

Contains all the validation logic for the backend

Classes

class CanMutateData

Attribute for Repositories that allow for data mutation after reading

Expand source code
class CanMutateData:
    """
    Attribute for Repositories that allow for data mutation after reading
    """

    @classmethod
    @abstractmethod
    def after_read(cls, model_read):
        """
        Called after a model is read
        :param model_read: the model object read from data source
        :return: raises Exception if interrupt is desired
        """
        raise NotImplementedError

    @classmethod
    @abstractmethod
    def after_read_many(cls, models_read):
        """
        Called after many models are read
        :param models_read: model objects read from data source
        :return: raises Exception if interrupt is desired
        """
        raise NotImplementedError

Subclasses

Static methods

def after_read(model_read)

Called after a model is read :param model_read: the model object read from data source :return: raises Exception if interrupt is desired

Expand source code
@classmethod
@abstractmethod
def after_read(cls, model_read):
    """
    Called after a model is read
    :param model_read: the model object read from data source
    :return: raises Exception if interrupt is desired
    """
    raise NotImplementedError
def after_read_many(models_read)

Called after many models are read :param models_read: model objects read from data source :return: raises Exception if interrupt is desired

Expand source code
@classmethod
@abstractmethod
def after_read_many(cls, models_read):
    """
    Called after many models are read
    :param models_read: model objects read from data source
    :return: raises Exception if interrupt is desired
    """
    raise NotImplementedError
class Repository

Base Repository Class for basic CRUD operations

Expand source code
class Repository(CanMutateData):
    """
    Base Repository Class for basic CRUD operations
    """
    id_attr: str = 'id'

    @classmethod
    def on_create(cls, model):
        """
        Calls the on_create method on all registered layers

        Repository implementations MUST call this manually, as layers
        are an opt-in process
        :param model: model newly created
        :return: None
        """
        _call_layers('create', cls, new_model=model)

    @classmethod
    def on_update(cls, model):
        """
        Calls the on_update method on all registered layers

        Repository implementations MUST call this manually, as layers
        are an opt-in process
        :param model: model newly updated
        :return: None
        """
        _call_layers('update', cls, new_model=model, id_attr=cls.id_attr)

    @classmethod
    def on_destroy(cls, model_id):
        """
        Calls the on_destroy method on all registered layers

        Repository implementations MUST call this manually, as layers
        are an opt-in process
        :param model_id: ID of deleted model (or to-be deleted)
        :return: None
        """
        _call_layers('destroy', cls, model_id=model_id, id_attr=cls.id_attr)

    @classmethod
    @abstractmethod
    def create(cls, model):
        """
        Creates the model in the data sink of choice
        :param model: instance with data
        :return: model
        """
        raise NotImplementedError

    @classmethod
    @abstractmethod
    def read(cls, model_id):
        """
        Read a single Model by it's ID
        :param model_id: model ID
        :param id_attr: Optional. Name of ID attribute (default is 'id')
        :return: model
        """
        raise NotImplementedError

    @classmethod
    @abstractmethod
    def read_by(cls, filters=None):
        """
        Reads all models that fit into the filters
        :return: list of models
        """
        raise NotImplementedError

    @classmethod
    @abstractmethod
    def read_all(cls):
        """
        Reads all models
        :return: list of models
        """
        raise NotImplementedError

    @classmethod
    @abstractmethod
    def update(cls, model):
        """
        Uses the 'id' column to update the model
        :param model: model instance
        :return: model
        """
        raise NotImplementedError

    @classmethod
    @abstractmethod
    def destroy(cls, model_id):
        """
        Deletes the model from the data sink
        :param model_id: model's ID to destroy
        :return: None
        """
        raise NotImplementedError

    @classmethod
    def after_read(cls, model_read):
        _call_layers('read_one', cls, new_model=model_read, id_attr=cls.id_attr)

    @classmethod
    def after_read_many(cls, models_read):
        _call_layers('read_many', cls, new_model=models_read)

    @property
    @classmethod
    @abstractmethod
    def resource_uri(cls):
        """
        This is a helper to make the table/file/URI name more obvious.

        For example, the table() function in a DatabaseRepo should use
        this property to find the table name
        :return: Resource URI
        """
        raise AttributeError('resource_uri: No resource URI set!')

Ancestors

Subclasses

Class variables

var id_attr : str

Static methods

def create(model)

Creates the model in the data sink of choice :param model: instance with data :return: model

Expand source code
@classmethod
@abstractmethod
def create(cls, model):
    """
    Creates the model in the data sink of choice
    :param model: instance with data
    :return: model
    """
    raise NotImplementedError
def destroy(model_id)

Deletes the model from the data sink :param model_id: model's ID to destroy :return: None

Expand source code
@classmethod
@abstractmethod
def destroy(cls, model_id):
    """
    Deletes the model from the data sink
    :param model_id: model's ID to destroy
    :return: None
    """
    raise NotImplementedError
def on_create(model)

Calls the on_create method on all registered layers

Repository implementations MUST call this manually, as layers are an opt-in process :param model: model newly created :return: None

Expand source code
@classmethod
def on_create(cls, model):
    """
    Calls the on_create method on all registered layers

    Repository implementations MUST call this manually, as layers
    are an opt-in process
    :param model: model newly created
    :return: None
    """
    _call_layers('create', cls, new_model=model)
def on_destroy(model_id)

Calls the on_destroy method on all registered layers

Repository implementations MUST call this manually, as layers are an opt-in process :param model_id: ID of deleted model (or to-be deleted) :return: None

Expand source code
@classmethod
def on_destroy(cls, model_id):
    """
    Calls the on_destroy method on all registered layers

    Repository implementations MUST call this manually, as layers
    are an opt-in process
    :param model_id: ID of deleted model (or to-be deleted)
    :return: None
    """
    _call_layers('destroy', cls, model_id=model_id, id_attr=cls.id_attr)
def on_update(model)

Calls the on_update method on all registered layers

Repository implementations MUST call this manually, as layers are an opt-in process :param model: model newly updated :return: None

Expand source code
@classmethod
def on_update(cls, model):
    """
    Calls the on_update method on all registered layers

    Repository implementations MUST call this manually, as layers
    are an opt-in process
    :param model: model newly updated
    :return: None
    """
    _call_layers('update', cls, new_model=model, id_attr=cls.id_attr)
def read(model_id)

Read a single Model by it's ID :param model_id: model ID :param id_attr: Optional. Name of ID attribute (default is 'id') :return: model

Expand source code
@classmethod
@abstractmethod
def read(cls, model_id):
    """
    Read a single Model by it's ID
    :param model_id: model ID
    :param id_attr: Optional. Name of ID attribute (default is 'id')
    :return: model
    """
    raise NotImplementedError
def read_all()

Reads all models :return: list of models

Expand source code
@classmethod
@abstractmethod
def read_all(cls):
    """
    Reads all models
    :return: list of models
    """
    raise NotImplementedError
def read_by(filters=None)

Reads all models that fit into the filters :return: list of models

Expand source code
@classmethod
@abstractmethod
def read_by(cls, filters=None):
    """
    Reads all models that fit into the filters
    :return: list of models
    """
    raise NotImplementedError
def update(model)

Uses the 'id' column to update the model :param model: model instance :return: model

Expand source code
@classmethod
@abstractmethod
def update(cls, model):
    """
    Uses the 'id' column to update the model
    :param model: model instance
    :return: model
    """
    raise NotImplementedError

Instance variables

var resource_uri

classmethod(function) -> method

Convert a function to be a class method.

A class method receives the class as implicit first argument, just like an instance method receives the instance. To declare a class method, use this idiom:

class C: @classmethod def f(cls, arg1, arg2, …): …

It can be called either on the class (e.g. C.f()) or on an instance (e.g. C().f()). The instance is ignored except for its class. If a class method is called for a derived class, the derived class object is passed as the implied first argument.

Class methods are different than C++ or Java static methods. If you want those, see the staticmethod builtin.

Inherited members