Repository: proteanhq/protean

Issue:
Make Model Definitions Optional
- Entities will be registered against a provider, and not models (`default` if not specified)
- The registration process will synthesize a Model from BaseModel directly, in memory
- Meta attributes like `model_name` are derived dynamically during registration
- Optional Models can be used to:
  - Point database column names that are different from attribute names
  - Specifying meta options like `order_by` or `model_name`
  - Any database specific constructs
- A Model’s meta attributes can also be specified directly during registration

Discussion:
abhishek-ram: There seem to be some issues. I think this is because I merged the other change
subhashb: Changes synced, @abhishek-ram 
codecov-io: # [Codecov](https://codecov.io/gh/proteanhq/protean/pull/129?src=pr&el=h1) Report
> Merging [#129](https://codecov.io/gh/proteanhq/protean/pull/129?src=pr&el=desc) into [master](https://codecov.io/gh/proteanhq/protean/commit/f1b626969bc07a0b44722e188ea23241ef001a4d?src=pr&el=desc) will **increase** coverage by `0.14%`.
> The diff coverage is `88.77%`.

[![Impacted file tree graph](https://codecov.io/gh/proteanhq/protean/pull/129/graphs/tree.svg?width=650&token=0sFuFdLBOx&height=150&src=pr)](https://codecov.io/gh/proteanhq/protean/pull/129?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##           master     #129      +/-   ##
==========================================
+ Coverage   82.54%   82.68%   +0.14%     
==========================================
  Files          46       46              
  Lines        2177     2160      -17     
  Branches      319      317       -2     
==========================================
- Hits         1797     1786      -11     
+ Misses        258      254       -4     
+ Partials      122      120       -2
```


| [Impacted Files](https://codecov.io/gh/proteanhq/protean/pull/129?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [src/protean/core/field/base.py](https://codecov.io/gh/proteanhq/protean/pull/129/diff?src=pr&el=tree#diff-c3JjL3Byb3RlYW4vY29yZS9maWVsZC9iYXNlLnB5) | `86.59% <ø> (ø)` | :arrow_up: |
| [src/protean/core/field/basic.py](https://codecov.io/gh/proteanhq/protean/pull/129/diff?src=pr&el=tree#diff-c3JjL3Byb3RlYW4vY29yZS9maWVsZC9iYXNpYy5weQ==) | `91.75% <ø> (ø)` | :arrow_up: |
| [src/protean/core/repository/base.py](https://codecov.io/gh/proteanhq/protean/pull/129/diff?src=pr&el=tree#diff-c3JjL3Byb3RlYW4vY29yZS9yZXBvc2l0b3J5L2Jhc2UucHk=) | `100% <100%> (ø)` | :arrow_up: |
| [src/protean/core/repository/\_\_init\_\_.py](https://codecov.io/gh/proteanhq/protean/pull/129/diff?src=pr&el=tree#diff-c3JjL3Byb3RlYW4vY29yZS9yZXBvc2l0b3J5L19faW5pdF9fLnB5) | `100% <100%> (ø)` | :arrow_up: |
| [src/protean/core/repository/model.py](https://codecov.io/gh/proteanhq/protean/pull/129/diff?src=pr&el=tree#diff-c3JjL3Byb3RlYW4vY29yZS9yZXBvc2l0b3J5L21vZGVsLnB5) | `100% <100%> (+19.35%)` | :arrow_up: |
| [src/protean/core/provider/base.py](https://codecov.io/gh/proteanhq/protean/pull/129/diff?src=pr&el=tree#diff-c3JjL3Byb3RlYW4vY29yZS9wcm92aWRlci9iYXNlLnB5) | `100% <100%> (ø)` | :arrow_up: |
| [src/protean/core/field/utils.py](https://codecov.io/gh/proteanhq/protean/pull/129/diff?src=pr&el=tree#diff-c3JjL3Byb3RlYW4vY29yZS9maWVsZC91dGlscy5weQ==) | `75% <66.66%> (ø)` | :arrow_up: |
| [src/protean/core/queryset.py](https://codecov.io/gh/proteanhq/protean/pull/129/diff?src=pr&el=tree#diff-c3JjL3Byb3RlYW4vY29yZS9xdWVyeXNldC5weQ==) | `89.26% <66.66%> (-0.15%)` | :arrow_down: |
| [src/protean/core/entity.py](https://codecov.io/gh/proteanhq/protean/pull/129/diff?src=pr&el=tree#diff-c3JjL3Byb3RlYW4vY29yZS9lbnRpdHkucHk=) | `91.32% <88%> (+0.18%)` | :arrow_up: |
| [src/protean/core/repository/factory.py](https://codecov.io/gh/proteanhq/protean/pull/129/diff?src=pr&el=tree#diff-c3JjL3Byb3RlYW4vY29yZS9yZXBvc2l0b3J5L2ZhY3RvcnkucHk=) | `90.62% <91.66%> (-0.15%)` | :arrow_down: |
| ... and [1 more](https://codecov.io/gh/proteanhq/protean/pull/129/diff?src=pr&el=tree-more) | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/proteanhq/protean/pull/129?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/proteanhq/protean/pull/129?src=pr&el=footer). Last update [f1b6269...112b1c8](https://codecov.io/gh/proteanhq/protean/pull/129?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).


Patch:
diff --git a/CHANGELOG.rst b/CHANGELOG.rst
index 090aa1f6..7656c170 100644
--- a/CHANGELOG.rst
+++ b/CHANGELOG.rst
@@ -8,6 +8,8 @@ dev
 * Rename Repository abstract methods to be public (Ex. `_create_object` → `create`)
 * Add `delete_all()` method to Entity to support Repository cleanup
 * Add support for `raw` queries on Entity repositories
+* Remove requirement for explicit Model definitions for Entities
+* Move Model options into Entity `Meta` class
 
 0.0.10 (2019-04-05)
 -------------------
diff --git a/requirements/dev.txt b/requirements/dev.txt
index b54cf866..b447add5 100644
--- a/requirements/dev.txt
+++ b/requirements/dev.txt
@@ -1,7 +1,6 @@
 check-manifest==0.37
 coverage==4.5.1
 docutils==0.14
-flake8==3.5.0
 pytest-flake8==1.0.4
 sphinx==1.8.3
 tox==3.1.2
diff --git a/setup.cfg b/setup.cfg
index d91b20d6..411dfb9d 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -1,11 +1,10 @@
 [bdist_wheel]
 universal = 1
 
-
 [flake8]
 max-line-length = 140
 exclude = */migrations/*
-ignore = E731
+extend-ignore = E731
 
 [tool:pytest]
 testpaths = tests
diff --git a/src/protean/core/entity.py b/src/protean/core/entity.py
index 4d85e56b..75fd0be1 100644
--- a/src/protean/core/entity.py
+++ b/src/protean/core/entity.py
@@ -10,6 +10,8 @@ from protean.core.field import Field
 from protean.core.field import Reference
 from protean.core.field import ReferenceField
 from protean.core.queryset import QuerySet
+from protean.core.repository import repo_factory
+from protean.utils import inflection
 
 logger = logging.getLogger('protean.core.entity')
 
@@ -41,7 +43,7 @@ class EntityBase(type):
         # Gather `Meta` class/object if defined
         attr_meta = attrs.pop('Meta', None)
         meta = attr_meta or getattr(new_class, 'Meta', None)
-        setattr(new_class, 'meta_', EntityMeta(meta))
+        setattr(new_class, 'meta_', EntityMeta(name, meta))
 
         # Load declared fields
         new_class._load_fields(attrs)
@@ -138,13 +140,31 @@ class EntityBase(type):
 
 
 class EntityMeta:
-    """ Metadata information for the entity including any options defined."""
+    """ Metadata info for the entity.
+
+    Options:
+    - ``abstract``: Indicates that this is an abstract entity (Ignores all other meta options)
+    - ``schema_name``: name of the schema (table/index/doc) used for persistence of this entity
+        defaults to underscore version of the Entity name.
+    - ``provider``: the name of the datasource associated with this
+        entity, default value is `default`.
+    - ``order_by``: default ordering of objects returned by filter queries.
+    """
+
+    def __init__(self, entity_name, meta):
+        self.abstract = getattr(meta, 'abstract', None) or False
+        self.schema_name = (getattr(meta, 'schema_name', None) or
+                            inflection.underscore(entity_name))
+        self.provider = getattr(meta, 'provider', None) or 'default'
 
-    def __init__(self, meta):
-        self.meta = meta
+        # `order_by` can be provided either as a string or a tuple
+        ordering = getattr(meta, 'order_by', ())
+        if isinstance(ordering, str):
+            self.order_by = ordering,
+        else:
+            self.order_by = tuple(ordering)
 
         # Initialize Options
-        self.entity_cls = None
         self.declared_fields = {}
         self.attributes = {}
         self.id_field = None
@@ -320,17 +340,6 @@ class Entity(metaclass=EntityBase):
         return {field_name: getattr(self, field_name, None)
                 for field_name in self.meta_.declared_fields}
 
-    @classmethod
-    def _retrieve_model(cls):
-        """Retrieve model details associated with this Entity"""
-        from protean.core.repository import repo_factory  # FIXME Move to a better placement
-
-        # Fetch Model class and connected repository from Repository Factory
-        model_cls = repo_factory.get_model(cls)
-        repository = repo_factory.get_repository(cls)
-
-        return (model_cls, repository)
-
     def clone(self):
         """Deepclone the entity, but reset state"""
         clone_copy = copy.deepcopy(self)
@@ -410,7 +419,8 @@ class Entity(metaclass=EntityBase):
         logger.debug(
             f'Creating new `{cls.__name__}` object using data {kwargs}')
 
-        model_cls, repository = cls._retrieve_model()
+        model_cls = repo_factory.get_model(cls)
+        repository = repo_factory.get_repository(cls)
 
         try:
             # Build the entity from the input arguments
@@ -436,7 +446,7 @@ class Entity(metaclass=EntityBase):
             entity.state_.mark_saved()
 
             return entity
-        except ValidationError as exc:
+        except ValidationError:
             # FIXME Log Exception
             raise
 
@@ -449,7 +459,8 @@ class Entity(metaclass=EntityBase):
             f'Saving `{self.__class__.__name__}` object')
 
         # Fetch Model class and connected repository from Repository Factory
-        model_cls, repository = self.__class__._retrieve_model()
+        model_cls = repo_factory.get_model(self.__class__)
+        repository = repo_factory.get_repository(self.__class__)
 
         try:
             # Do unique checks, update the record and return the Entity
@@ -471,7 +482,7 @@ class Entity(metaclass=EntityBase):
             self.state_.mark_saved()
 
             return self
-        except Exception as exc:
+        except Exception:
             # FIXME Log Exception
             raise
 
@@ -492,7 +503,8 @@ class Entity(metaclass=EntityBase):
         logger.debug(f'Updating existing `{self.__class__.__name__}` object with id {self.id}')
 
         # Fetch Model class and connected repository from Repository Factory
-        model_cls, repository = self.__class__._retrieve_model()
+        model_cls = repo_factory.get_model(self.__class__)
+        repository = repo_factory.get_repository(self.__class__)
 
         try:
             # Update entity's data attributes
@@ -506,15 +518,12 @@ class Entity(metaclass=EntityBase):
             self.state_.mark_saved()
 
             return self
-        except Exception as exc:
+        except Exception:
             # FIXME Log Exception
             raise
 
     def _validate_unique(self, create=True):
         """ Validate the unique constraints for the entity """
-        # Fetch Model class and connected-repository from Repository Factory
-        model_cls, _ = self.__class__._retrieve_model()
-
         # Build the filters from the unique constraints
         filters, excludes = {}, {}
 
@@ -534,7 +543,7 @@ class Entity(metaclass=EntityBase):
             if self.exists(excludes, **{filter_key: lookup_value}):
                 field_obj = self.meta_.declared_fields[filter_key]
                 field_obj.fail('unique',
-                               model_name=model_cls.opts_.model_name,
+                               entity_name=self.__class__.__name__,
                                field_name=filter_key)
 
     def delete(self):
@@ -545,7 +554,8 @@ class Entity(metaclass=EntityBase):
         Throws ObjectNotFoundError if the object was not found in the repository.
         """
         # Fetch Model class and connected repository from Repository Factory
-        model_cls, repository = self.__class__._retrieve_model()
+        model_cls = repo_factory.get_model(self.__class__)
+        repository = repo_factory.get_repository(self.__class__)
 
         try:
             if not self.state_.is_destroyed:
@@ -556,7 +566,7 @@ class Entity(metaclass=EntityBase):
                 self.state_.mark_destroyed()
 
             return self
-        except Exception as exc:
+        except Exception:
             # FIXME Log Exception
             raise
 
@@ -567,10 +577,10 @@ class Entity(metaclass=EntityBase):
         Will skip callbacks and validations.
         """
         # Fetch connected repository from Repository Factory
-        _, repository = cls._retrieve_model()
+        repository = repo_factory.get_repository(cls)
 
         try:
             repository.delete_all()
-        except Exception as exc:
+        except Exception:
             # FIXME Log Exception
             raise
diff --git a/src/protean/core/field/base.py b/src/protean/core/field/base.py
index b907dbc3..f129ce3b 100644
--- a/src/protean/core/field/base.py
+++ b/src/protean/core/field/base.py
@@ -35,7 +35,7 @@ class Field(FieldDescriptorMixin, metaclass=ABCMeta):
     # Default error messages for various kinds of errors.
     default_error_messages = {
         'invalid': 'Value is not a valid type for this field.',
-        'unique': '`{model_name:s}` with this `{field_name:s}` already exists.',
+        'unique': '`{entity_name:s}` with this `{field_name:s}` already exists.',
         'required': 'This field is required.',
         'invalid_choice': 'Value `{value!r}` is not a valid choice. '
                           'Must be one of {choices!r}',
diff --git a/src/protean/core/field/basic.py b/src/protean/core/field/basic.py
index ec53beab..ad192439 100644
--- a/src/protean/core/field/basic.py
+++ b/src/protean/core/field/basic.py
@@ -14,6 +14,7 @@ class String(Field):
     :param max_length: The maximum allowed length for the field.
     :param min_length: The minimum allowed length for the field.
 
+    FIXME Should max_length be optional?
     """
     default_error_messages = {
         'invalid': '{value}" value must be a string.',
diff --git a/src/protean/core/field/utils.py b/src/protean/core/field/utils.py
index 548e256a..37898c4a 100644
--- a/src/protean/core/field/utils.py
+++ b/src/protean/core/field/utils.py
@@ -3,14 +3,14 @@ from protean.core.repository import repo_factory
 
 
 def fetch_entity_cls_from_registry(entity):
-        """Util Method to fetch an Entity class from an entity's name"""
-        # Defensive check to ensure we only process if `to_cls` is a string
-        if isinstance(entity, str):
-            try:
-                return repo_factory.get_entity(entity)
-            except AssertionError:
-                # Entity has not been registered (yet)
-                # FIXME print a helpful debug message
-                raise
-        else:
-            return entity
+    """Util Method to fetch an Entity class from an entity's name"""
+    # Defensive check to ensure we only process if `to_cls` is a string
+    if isinstance(entity, str):
+        try:
+            return repo_factory.get_entity(entity)
+        except AssertionError:
+            # Entity has not been registered (yet)
+            # FIXME print a helpful debug message
+            raise
+    else:
+        return entity
diff --git a/src/protean/core/provider/base.py b/src/protean/core/provider/base.py
index d3a95220..f079586d 100644
--- a/src/protean/core/provider/base.py
+++ b/src/protean/core/provider/base.py
@@ -56,9 +56,6 @@ class BaseProvider(RegisterLookupMixin, metaclass=ABCMeta):
     def get_repository(self, model_cls):
         """ Return a repository object configured with a live connection"""
 
-    def get_model(self, model_cls):
-        """ Return the fully baked model, with any additions necessary
-
-        This is a placeholder method that can be overridden in each provider
-        """
-        return model_cls
+    @abstractmethod
+    def get_model(self, entity_cls):
+        """ Return associated Model Class"""
diff --git a/src/protean/core/queryset.py b/src/protean/core/queryset.py
index 700d2565..1703f4e7 100644
--- a/src/protean/core/queryset.py
+++ b/src/protean/core/queryset.py
@@ -33,7 +33,7 @@ class QuerySet:
     :return Returns a `Pagination` object that holds the query results
     """
 
-    def __init__(self, entity_cls: 'Entity', criteria=None, page: int = 1, per_page: int = 10,
+    def __init__(self, entity_cls, criteria=None, page: int = 1, per_page: int = 10,
                  order_by: set = None):
         """Initialize either with empty preferences (when invoked on an Entity)
             or carry forward filters and preferences when chained
@@ -109,14 +109,6 @@ class QuerySet:
 
         return clone
 
-    def _retrieve_model(self):
-        """Retrieve model details associated with this Entity"""
-        # Fetch Model class and connected repository from Repository Factory
-        model_cls = repo_factory.get_model(self._entity_cls)
-        repository = repo_factory.get_repository(self._entity_cls)
-
-        return (model_cls, repository)
-
     def all(self):
         """Primary method to fetch data based on filters
 
@@ -133,10 +125,11 @@ class QuerySet:
         self._result_cache = None
 
         # Fetch Model class and connected repository from Repository Factory
-        model_cls, repository = self._retrieve_model()
+        model_cls = repo_factory.get_model(self._entity_cls)
+        repository = repo_factory.get_repository(self._entity_cls)
 
         # order_by clause must be list of keys
-        order_by = model_cls.opts_.order_by if not self._order_by else self._order_by
+        order_by = self._entity_cls.meta_.order_by if not self._order_by else self._order_by
 
         # Call the read method of the repository
         results = repository.filter(self._criteria, self._page, self._per_page, order_by)
@@ -170,7 +163,7 @@ class QuerySet:
             for item in items:
                 item.update(*data, **kwargs)
                 updated_item_count += 1
-        except Exception as exc:
+        except Exception:
             # FIXME Log Exception
             raise
 
@@ -193,7 +186,8 @@ class QuerySet:
         self._result_cache = None
 
         # Fetch Model class and connected repository from Repository Factory
-        model_cls, repository = self._retrieve_model()
+        model_cls = repo_factory.get_model(self._entity_cls)
+        repository = repo_factory.get_repository(self._entity_cls)
 
         try:
             # Call the read method of the repository
@@ -232,7 +226,7 @@ class QuerySet:
             for item in items:
                 item.delete()
                 deleted_item_count += 1
-        except Exception as exc:
+        except Exception:
             # FIXME Log Exception
             raise
 
@@ -250,10 +244,12 @@ class QuerySet:
             updated if objects rows already have the new value).
         """
         updated_item_count = 0
-        _, repository = self._retrieve_model()
+
+        repository = repo_factory.get_repository(self._entity_cls)
+
         try:
             updated_item_count = repository.update_all(self._criteria, *args, **kwargs)
-        except Exception as exc:
+        except Exception:
             # FIXME Log Exception
             raise
 
@@ -268,10 +264,10 @@ class QuerySet:
         Returns the number of objects matched and deleted.
         """
         deleted_item_count = 0
-        _, repository = self._retrieve_model()
+        repository = repo_factory.get_repository(self._entity_cls)
         try:
             deleted_item_count = repository.delete_all(self._criteria)
-        except Exception as exc:
+        except Exception:
             # FIXME Log Exception
             raise
 
diff --git a/src/protean/core/repository/__init__.py b/src/protean/core/repository/__init__.py
index e3ff154c..5b266812 100644
--- a/src/protean/core/repository/__init__.py
+++ b/src/protean/core/repository/__init__.py
@@ -5,9 +5,7 @@ from .factory import RepositoryFactory
 from .factory import repo_factory
 from .lookup import BaseLookup
 from .model import BaseModel
-from .model import BaseModelMeta
-from .model import ModelOptions
 from .pagination import Pagination
 
-__all__ = ('BaseRepository', 'BaseModel', 'BaseModelMeta', 'BaseLookup', 'ModelOptions',
+__all__ = ('BaseRepository', 'BaseModel', 'BaseLookup',
            'RepositoryFactory', 'repo_factory', 'Pagination')
diff --git a/src/protean/core/repository/base.py b/src/protean/core/repository/base.py
index b9330d36..fbf34606 100644
--- a/src/protean/core/repository/base.py
+++ b/src/protean/core/repository/base.py
@@ -18,12 +18,12 @@ class BaseRepository(metaclass=ABCMeta):
     :param model_cls: The model class registered with this repository
     """
 
-    def __init__(self, provider, model_cls):
+    def __init__(self, provider, entity_cls, model_cls):
         self.provider = provider
         self.conn = self.provider.get_connection()
         self.model_cls = model_cls
-        self.entity_cls = model_cls.opts_.entity_cls
-        self.model_name = model_cls.opts_.model_name
+        self.entity_cls = entity_cls
+        self.schema_name = entity_cls.meta_.schema_name
 
     @abstractmethod
     def filter(self, criteria: Q, page: int = 1, per_page: int = 10,
@@ -50,7 +50,7 @@ class BaseRepository(metaclass=ABCMeta):
         """Delete a Record from the Repository"""
 
     @abstractmethod
-    def delete_all(self, criteria: Q):
+    def delete_all(self, criteria: Q = None):
         """Delete a Record from the Repository"""
 
     @abstractmethod
diff --git a/src/protean/core/repository/factory.py b/src/protean/core/repository/factory.py
index d3b668df..b98e007f 100644
--- a/src/protean/core/repository/factory.py
+++ b/src/protean/core/repository/factory.py
@@ -23,28 +23,27 @@ class RepositoryFactory:
     #   in the registry dictionary.
     EntityRecord = namedtuple(
         'EntityRecord',
-        'name, qualname, entity_cls, provider_name, model_cls, fully_baked_model')
+        'name, qualname, entity_cls, provider_name, model_cls')
 
     def __init__(self):
         """"Initialize repository factory"""
         self._registry = {}
         self._connections = local()
 
-    def register(self, model_cls, provider_name=None):
+    def register(self, entity_cls, provider_name=None):
         """ Register the given model with the factory
         :param model_cls: class of the model to be registered
         :param adapter_cls: Optional adapter class to use if not the
         `Adapter` defined by the provider is user
         """
-        self._validate_model_cls(model_cls)
+        self._validate_entity_cls(entity_cls)
 
-        # Register the model if it does not exist
-        model_name = model_cls.__name__
-        entity_name = fully_qualified_name(model_cls.opts_.entity_cls)
-        provider_name = provider_name or model_cls.opts_.bind or 'default'
+        # Register the entity if not registered already
+        entity_name = fully_qualified_name(entity_cls)
+        provider_name = provider_name or entity_cls.meta_.provider or 'default'
 
         try:
-            entity = self._get_entity_by_class(model_cls.opts_.entity_cls)
+            entity = self._get_entity_by_class(entity_cls)
 
             if entity:
                 # This probably is an accidental re-registration of the entity
@@ -54,17 +53,15 @@ class RepositoryFactory:
         except AssertionError:
             # Entity has not been registered yet. Let's go ahead and add it to the registry.
             entity_record = RepositoryFactory.EntityRecord(
-                name=model_cls.opts_.entity_cls.__name__,
+                name=entity_cls.__name__,
                 qualname=entity_name,
-                entity_cls=model_cls.opts_.entity_cls,
+                entity_cls=entity_cls,
                 provider_name=provider_name,
-                model_cls=model_cls,
-                fully_baked_model=False
+                model_cls=None
             )
             self._registry[entity_name] = entity_record
             logger.debug(
-                f'Registered model {model_name} for entity {entity_name} with provider'
-                f' {provider_name}.')
+                f'Registered entity {entity_name} with provider {provider_name}')
 
     def _find_entity_in_records_by_class_name(self, entity_name):
         """Fetch by Entity Name in values"""
@@ -100,29 +97,30 @@ class RepositoryFactory:
         else:
             return self._find_entity_in_records_by_class_name(entity_name)
 
-    def _validate_model_cls(self, model_cls):
-        """Validate that Model is a valid class"""
+    def _validate_entity_cls(self, entity_cls):
+        """Validate that Entity is a valid class"""
         # Import here to avoid cyclic dependency
-        from .model import BaseModel
+        from protean.core.entity import Entity
 
-        if not issubclass(model_cls, BaseModel):
+        if not issubclass(entity_cls, Entity):
             raise AssertionError(
-                f'Model {model_cls} must be subclass of `BaseModel`')
+                f'Entity {entity_cls} must be subclass of `Entity`')
 
     def get_model(self, entity_cls):
         """Retrieve Model class connected to Entity"""
         entity_record = self._get_entity_by_class(entity_cls)
 
         model_cls = None
-        if entity_record.fully_baked_model:
+        if entity_record.model_cls:
             model_cls = entity_record.model_cls
         else:
+            # We should ask the Provider to give a fully baked model the first time
+            #   that has been initialized properly for this entity
             provider = self.get_provider(entity_record.provider_name)
-            baked_model_cls = provider.get_model(entity_record.model_cls)
+            baked_model_cls = provider.get_model(entity_record.entity_cls)
 
             # Record for future reference
-            new_entity_record = entity_record._replace(model_cls=baked_model_cls,
-                                                       fully_baked_model=True)
+            new_entity_record = entity_record._replace(model_cls=baked_model_cls)
             self._registry[entity_record.qualname] = new_entity_record
 
             model_cls = baked_model_cls
@@ -142,7 +140,7 @@ class RepositoryFactory:
         entity_record = self._get_entity_by_class(entity_cls)
         provider = self.get_provider(entity_record.provider_name)
 
-        return provider.get_repository(entity_record.model_cls)
+        return provider.get_repository(entity_record.entity_cls)
 
 
 repo_factory = RepositoryFactory()
diff --git a/src/protean/core/repository/model.py b/src/protean/core/repository/model.py
index 9340c672..7f71ac54 100644
--- a/src/protean/core/repository/model.py
+++ b/src/protean/core/repository/model.py
@@ -1,79 +1,18 @@
 """ Module containing Model related Class Definitions """
 
 from abc import ABCMeta
+from abc import abstractmethod
 
-from protean.core.exceptions import ConfigurationError
-from protean.utils import inflection
 
-
-class ModelOptions(object):
-    """class Meta options for the :class:`BaseModel`."""
-
-    def __init__(self, meta, model_cls):
-        self.entity_cls = getattr(meta, 'entity', None)
-
-        # Import here to avoid cyclic deps
-        from protean.core.entity import Entity
-        if not self.entity_cls or not issubclass(self.entity_cls, Entity):
-            raise ConfigurationError(
-                '`entity` option must be set and be a subclass of `Entity`.')
-
-        # Get the model name to be used, if not provided default it
-        self.model_name = getattr(meta, 'model_name', None)
-        if not self.model_name:
-            self.model_name = inflection.underscore(model_cls.__name__)
-
-        # Get the database bound to this model
-        self.bind = getattr(meta, 'bind', 'default')
-
-        # Default ordering of the filter response
-        self.order_by = getattr(meta, 'order_by', ())
-
-
-class BaseModelMeta(ABCMeta):
-    """ Metaclass for the BaseModel, sets options and registers the model """
-    def __new__(mcs, name, bases, attrs):
-        klass = super().__new__(mcs, name, bases, attrs)
-
-        # Get the Meta class attribute defined for the base class
-        meta = getattr(klass, 'Meta', None)
-
-        # Load the meta class attributes for non base schemas
-        is_base = getattr(meta, 'base', False)
-        if not is_base:
-            # Set klass.opts by initializing the `options_cls` with the meta
-            klass.opts_ = klass.options_cls(meta, klass)
-
-        return klass
-
-
-class BaseModel(metaclass=BaseModelMeta):
-    """Model that defines an index/table in the repository"""
-    options_cls = ModelOptions
-    opts_ = None
-
-    class Meta(object):
-        """Options object for a Model.
-        Example usage: ::
-            class Meta:
-                entity = Dog
-        Available options:
-        - ``base``: Indicates that this is a base model so ignore the meta
-        - ``entity``: the entity associated with this model.
-        - ``model_name``: name of this model that will be used as table/index
-        names, defaults to underscore version of the class name.
-        - ``bind``: the name of the repository connection associated with this
-        model, default value is `default`.
-        - ``order_by``: default ordering of objects returned by filter queries.
-        """
-        base = True
+class BaseModel(metaclass=ABCMeta):
+    """Model representing a schema in the database"""
 
     @classmethod
+    @abstractmethod
     def from_entity(cls, entity):
         """Initialize Repository Model object from Entity object"""
-        raise NotImplementedError()
 
     @classmethod
+    @abstractmethod
     def to_entity(cls, *args, **kwargs):
         """Convert Repository Model Object to Entity Object"""
-        raise NotImplementedError()
diff --git a/src/protean/impl/repository/dict_repo.py b/src/protean/impl/repository/dict_repo.py
index 88965352..0b5e9aa0 100644
--- a/src/protean/impl/repository/dict_repo.py
+++ b/src/protean/impl/repository/dict_repo.py
@@ -7,6 +7,7 @@ from itertools import count
 from operator import itemgetter
 from threading import Lock
 
+from protean.core.entity import Entity
 from protean.core.exceptions import ObjectNotFoundError
 from protean.core.provider.base import BaseProvider
 from protean.core.repository import BaseLookup
@@ -22,6 +23,23 @@ _locks = {}
 _counters = defaultdict(count)
 
 
+class DictModel(BaseModel):
+    """ A model for the dictionary repository"""
+
+    @classmethod
+    def from_entity(cls, entity: Entity) -> 'DictModel':
+        """ Convert the entity to a dictionary record """
+        dict_obj = {}
+        for field_name in entity.meta_.attributes:
+            dict_obj[field_name] = getattr(entity, field_name)
+        return dict_obj
+
+    @classmethod
+    def to_entity(cls, item: 'DictModel') -> Entity:
+        """ Convert the dictionary record to an entity """
+        return cls.entity_cls(item)
+
+
 class DictProvider(BaseProvider):
     """Provider class for Dict Repositories"""
 
@@ -46,9 +64,17 @@ class DictProvider(BaseProvider):
         """ Close connection does nothing on the repo """
         pass
 
-    def get_repository(self, model_cls):
+    def get_model(self, entity_cls):
+        """ Return associated, fully-baked Model class"""
+        cls = DictModel
+        cls.entity_cls = entity_cls
+
+        return cls
+
+    def get_repository(self, entity_cls):
         """ Return a repository object configured with a live connection"""
-        return DictRepository(self, model_cls)
+        model_cls = self.get_model(entity_cls)
+        return DictRepository(self, entity_cls, model_cls)
 
 
 class DictRepository(BaseRepository):
@@ -58,7 +84,7 @@ class DictRepository(BaseRepository):
         """ Set the values of the auto field using counter"""
         for field_name, field_obj in \
                 self.entity_cls.meta_.auto_fields:
-            counter_key = f'{self.model_name}_{field_name}'
+            counter_key = f'{self.schema_name}_{field_name}'
             if not (field_name in model_obj and model_obj[field_name] is not None):
                 # Increment the counter and it should start from 1
                 counter = next(self.conn['counters'][counter_key])
@@ -76,7 +102,7 @@ class DictRepository(BaseRepository):
         # Add the entity to the repository
         identifier = model_obj[self.entity_cls.meta_.id_field.field_name]
         with self.conn['lock']:
-            self.conn['data'][self.model_name][identifier] = model_obj
+            self.conn['data'][self.schema_name][identifier] = model_obj
 
         return model_obj
 
@@ -132,9 +158,9 @@ class DictRepository(BaseRepository):
         """ Read the repository and return results as per the filer"""
 
         if criteria.children:
-            items = list(self._filter(criteria, self.conn['data'][self.model_name]).values())
+            items = list(self._filter(criteria, self.conn['data'][self.schema_name]).values())
         else:
-            items = list(self.conn['data'][self.model_name].values())
+            items = list(self.conn['data'][self.schema_name].values())
 
         # Sort the filtered results based on the order_by clause
         for o_key in order_by:
@@ -162,24 +188,24 @@ class DictRepository(BaseRepository):
         identifier = model_obj[self.entity_cls.meta_.id_field.field_name]
         with self.conn['lock']:
             # Check if object is present
-            if identifier not in self.conn['data'][self.model_name]:
+            if identifier not in self.conn['data'][self.schema_name]:
                 raise ObjectNotFoundError(
                     f'`{self.__class__.__name__}` object with identifier {identifier} '
                     f'does not exist.')
 
-            self.conn['data'][self.model_name][identifier] = model_obj
+            self.conn['data'][self.schema_name][identifier] = model_obj
         return model_obj
 
     def update_all(self, criteria: Q, *args, **kwargs):
         """ Update all objects satisfying the criteria """
-        items = self._filter(criteria, self.conn['data'][self.model_name])
+        items = self._filter(criteria, self.conn['data'][self.schema_name])
 
         update_count = 0
         for key in items:
             item = items[key]
             item.update(*args)
             item.update(kwargs)
-            self.conn['data'][self.model_name][key] = item
+            self.conn['data'][self.schema_name][key] = item
 
             update_count += 1
 
@@ -190,30 +216,30 @@ class DictRepository(BaseRepository):
         identifier = model_obj[self.entity_cls.meta_.id_field.field_name]
         with self.conn['lock']:
             # Check if object is present
-            if identifier not in self.conn['data'][self.model_name]:
+            if identifier not in self.conn['data'][self.schema_name]:
                 raise ObjectNotFoundError(
                     f'`{self.__class__.__name__}` object with identifier {identifier} '
                     f'does not exist.')
 
-            del self.conn['data'][self.model_name][identifier]
+            del self.conn['data'][self.schema_name][identifier]
         return model_obj
 
     def delete_all(self, criteria: Q = None):
         """ Delete the dictionary object by its criteria"""
         if criteria:
             # Delete the object from the dictionary and return the deletion count
-            items = self._filter(criteria, self.conn['data'][self.model_name])
+            items = self._filter(criteria, self.conn['data'][self.schema_name])
 
             # Delete all the matching identifiers
             with self.conn['lock']:
                 for identifier in items:
-                    self.conn['data'][self.model_name].pop(identifier, None)
+                    self.conn['data'][self.schema_name].pop(identifier, None)
 
             return len(items)
         else:
             with self.conn['lock']:
-                if self.model_name in self.conn['data']:
-                    del self.conn['data'][self.model_name]
+                if self.schema_name in self.conn['data']:
+                    del self.conn['data'][self.schema_name]
 
     def raw(self, query_string: str):
         """Run raw query on Repository.
@@ -222,7 +248,7 @@ class DictRepository(BaseRepository):
         criteria with straigh-forward equality checks. Individual criteria are always ANDed
         and the result is always a subset of the full repository.
         """
-        input_db = self.conn['data'][self.model_name]
+        input_db = self.conn['data'][self.schema_name]
         result = None
 
         try:
@@ -367,20 +393,3 @@ class In(DefaultDictLookup):
         """Ensure target is a list or tuple"""
         assert type(self.target) in (list, tuple)
         return super().process_target()
-
-
-class DictModel(BaseModel):
-    """ A model for the dictionary repository"""
-
-    @classmethod
-    def from_entity(cls, entity):
-        """ Convert the entity to a dictionary record """
-        dict_obj = {}
-        for field_name in entity.__class__.meta_.attributes:
-            dict_obj[field_name] = getattr(entity, field_name)
-        return dict_obj
-
-    @classmethod
-    def to_entity(cls, item):
-        """ Convert the dictionary record to an entity """
-        return cls.opts_.entity_cls(item)


Test patch:
diff --git a/tests/conftest.py b/tests/conftest.py
index 227d0c8f..ae0fc827 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -13,32 +13,29 @@ def register_models():
        Run only once for the entire test suite
     """
     from protean.core.repository import repo_factory
-    from tests.support.dog import (DogModel, RelatedDogModel, DogRelatedByEmailModel,
-                                   HasOneDog1Model, HasOneDog2Model, HasOneDog3Model,
-                                   HasManyDog1Model, HasManyDog2Model, HasManyDog3Model,
-                                   ThreadedDogModel)
-    from tests.support.human import (HumanModel, HasOneHuman1Model,
-                                     HasOneHuman2Model, HasOneHuman3Model,
-                                     HasManyHuman1Model, HasManyHuman2Model,
-                                     HasManyHuman3Model)
+    from tests.support.dog import (Dog, RelatedDog, DogRelatedByEmail, HasOneDog1,
+                                   HasOneDog2, HasOneDog3, HasManyDog1, HasManyDog2,
+                                   HasManyDog3, ThreadedDog)
+    from tests.support.human import (Human, HasOneHuman1, HasOneHuman2, HasOneHuman3,
+                                     HasManyHuman1, HasManyHuman2, HasManyHuman3)
 
-    repo_factory.register(DogModel)
-    repo_factory.register(RelatedDogModel)
-    repo_factory.register(DogRelatedByEmailModel)
-    repo_factory.register(HasOneDog1Model)
-    repo_factory.register(HasOneDog2Model)
-    repo_factory.register(HasOneDog3Model)
-    repo_factory.register(HasManyDog1Model)
-    repo_factory.register(HasManyDog2Model)
-    repo_factory.register(HasManyDog3Model)
-    repo_factory.register(HumanModel)
-    repo_factory.register(HasOneHuman1Model)
-    repo_factory.register(HasOneHuman2Model)
-    repo_factory.register(HasOneHuman3Model)
-    repo_factory.register(HasManyHuman1Model)
-    repo_factory.register(HasManyHuman2Model)
-    repo_factory.register(HasManyHuman3Model)
-    repo_factory.register(ThreadedDogModel)
+    repo_factory.register(Dog)
+    repo_factory.register(RelatedDog)
+    repo_factory.register(DogRelatedByEmail)
+    repo_factory.register(HasOneDog1)
+    repo_factory.register(HasOneDog2)
+    repo_factory.register(HasOneDog3)
+    repo_factory.register(HasManyDog1)
+    repo_factory.register(HasManyDog2)
+    repo_factory.register(HasManyDog3)
+    repo_factory.register(Human)
+    repo_factory.register(HasOneHuman1)
+    repo_factory.register(HasOneHuman2)
+    repo_factory.register(HasOneHuman3)
+    repo_factory.register(HasManyHuman1)
+    repo_factory.register(HasManyHuman2)
+    repo_factory.register(HasManyHuman3)
+    repo_factory.register(ThreadedDog)
 
 
 @pytest.fixture(autouse=True)
diff --git a/tests/core/test_entity.py b/tests/core/test_entity.py
index 94ca6ea5..27d3c416 100644
--- a/tests/core/test_entity.py
+++ b/tests/core/test_entity.py
@@ -11,9 +11,9 @@ from tests.support.human import Human
 
 from protean.core import field
 from protean.core.entity import Entity
-from protean.core.queryset import QuerySet
 from protean.core.exceptions import ObjectNotFoundError
 from protean.core.exceptions import ValidationError
+from protean.core.queryset import QuerySet
 
 
 class TestEntity:
@@ -345,7 +345,7 @@ class TestEntity:
             Dog.create(
                 id=2, name='Johnny', owner='Carey')
         assert err.value.normalized_messages == {
-            'name': ['`dogs` with this `name` already exists.']}
+            'name': ['`Dog` with this `name` already exists.']}
 
     def test_query_init(self):
         """Test the initialization of a QuerySet"""
@@ -629,9 +629,86 @@ class TestEntity:
 
 
 class TestEntityMetaAttributes:
-
     """Class that holds testcases for Entity's meta attributes"""
 
+    def test_entity_structure(self):
+        """Test the meta structure of an Entity class"""
+        # Test that an entity has Meta information
+        assert hasattr(Dog, 'meta_')
+
+        meta = getattr(Dog, 'meta_')
+        assert hasattr(meta, 'abstract')
+        # Test that meta has correct defaults
+
+    def test_meta_overriding_abstract(self):
+        """Test that `abstract` flag can be overridden"""
+
+        # Class with overridden meta info
+        class Foo(Entity):
+            bar = field.String(max_length=25)
+
+            class Meta:
+                abstract = True
+
+        # Test that `abstract` is False by default
+        assert getattr(Dog.meta_, 'abstract') is False
+
+        # Test that the option in meta is overridden
+        assert hasattr(Foo.meta_, 'abstract')
+        assert getattr(Foo.meta_, 'abstract') is True
+
+    def test_meta_overriding_schema_name(self):
+        """Test that `schema_name` can be overridden"""
+
+        # Class with overridden meta info
+        class Foo(Entity):
+            bar = field.String(max_length=25)
+
+            class Meta:
+                schema_name = 'foosball'
+
+        # Test that `schema_name` is False by default
+        assert getattr(Dog.meta_, 'schema_name') == 'dog'
+        assert getattr(HasOneHuman1.meta_, 'schema_name') == 'has_one_human1'
+
+        # Test that the option in meta is overridden
+        assert hasattr(Foo.meta_, 'schema_name')
+        assert getattr(Foo.meta_, 'schema_name') == 'foosball'
+
+    def test_meta_overriding_provider(self):
+        """Test that `provider` can be overridden"""
+
+        # Class with overridden meta info
+        class Foo(Entity):
+            bar = field.String(max_length=25)
+
+            class Meta:
+                provider = 'non-default'
+
+        # Test that `provider` is set to `default` by default
+        assert getattr(Dog.meta_, 'provider') == 'default'
+
+        # Test that the option in meta is overridden
+        assert hasattr(Foo.meta_, 'provider')
+        assert getattr(Foo.meta_, 'provider') == 'non-default'
+
+    def test_meta_overriding_order_by(self):
+        """Test that `order_by` can be overridden"""
+
+        # Class with overridden meta info
+        class Foo(Entity):
+            bar = field.String(max_length=25)
+
+            class Meta:
+                order_by = 'bar'
+
+        # Test that `order_by` is an empty tuple by default
+        assert getattr(Dog.meta_, 'order_by') == ()
+
+        # Test that the option in meta is overridden
+        assert hasattr(Foo.meta_, 'order_by')
+        assert getattr(Foo.meta_, 'order_by') == ('bar', )
+
     def test_meta_on_init(self):
         """Test that `meta` attribute is available after initialization"""
         dog = Dog(id=1, name='John Doe', age=10, owner='Jimmy')
diff --git a/tests/core/test_entity_state.py b/tests/core/test_entity_state.py
index 4babc252..ae8401e0 100644
--- a/tests/core/test_entity_state.py
+++ b/tests/core/test_entity_state.py
@@ -35,7 +35,7 @@ class TestState:
         try:
             del dog.name
             dog.save()
-        except ValidationError as exc:
+        except ValidationError:
             assert dog.state_.is_new
 
     def test_persisted_after_create(self):
diff --git a/tests/core/test_repository.py b/tests/core/test_repository.py
index 1cda244c..3166785d 100644
--- a/tests/core/test_repository.py
+++ b/tests/core/test_repository.py
@@ -20,7 +20,7 @@ class TestRepository:
 
         Dog.query.all()
         current_db = dict(repo_factory.get_repository(Dog).conn)
-        assert current_db['data'] == {'dogs': {}}
+        assert current_db['data'] == {'dog': {}}
 
     def test_create_error(self):
         """ Add an entity to the repository missing a required attribute"""
@@ -63,7 +63,7 @@ class TestRepository:
             Dog.create(
                 id=2, name='Johnny', owner='Carey')
         assert err.value.normalized_messages == {
-            'name': ['`dogs` with this `name` already exists.']}
+            'name': ['`Dog` with this `name` already exists.']}
 
     def test_filter(self):
         """ Query the repository using filters """
diff --git a/tests/core/test_usecase.py b/tests/core/test_usecase.py
index f5526bc0..d2c8d106 100644
--- a/tests/core/test_usecase.py
+++ b/tests/core/test_usecase.py
@@ -194,7 +194,7 @@ class TestCreateUseCase:
         assert not response.success
         assert response.value == {
             'code': 422,
-            'message': {'id': ['`dogs` with this `id` already exists.']}}
+            'message': {'id': ['`Dog` with this `id` already exists.']}}
 
 
 class TestUpdateUseCase:
@@ -252,7 +252,7 @@ class TestUpdateUseCase:
         assert response.value == {
             'code': 422,
             'message': {
-                'name': ['`dogs` with this `name` already exists.']}}
+                'name': ['`Dog` with this `name` already exists.']}}
 
 
 class TestDeleteUseCase:
diff --git a/tests/support/dog.py b/tests/support/dog.py
index 02049e82..0924a572 100644
--- a/tests/support/dog.py
+++ b/tests/support/dog.py
@@ -8,7 +8,6 @@ from tests.support.human import Human
 
 from protean.core import field
 from protean.core.entity import Entity
-from protean.impl.repository.dict_repo import DictModel
 
 
 class Dog(Entity):
@@ -18,15 +17,6 @@ class Dog(Entity):
     owner = field.String(required=True, max_length=15)
 
 
-class DogModel(DictModel):
-    """ Model for the Dog Entity"""
-
-    class Meta:
-        """ Meta class for model options"""
-        entity = Dog
-        model_name = 'dogs'
-
-
 class RelatedDog(Entity):
     """This is a dummy Dog Entity class with an association"""
     name = field.String(required=True, unique=True, max_length=50)
@@ -34,15 +24,6 @@ class RelatedDog(Entity):
     owner = field.Reference(Human)
 
 
-class RelatedDogModel(DictModel):
-    """ Model for the RelatedDog Entity"""
-
-    class Meta:
-        """ Meta class for model options"""
-        entity = RelatedDog
-        model_name = 'related_dogs'
-
-
 class RelatedDog2(Entity):
     """This is a dummy RelatedDog2 Entity class with reference definition
        containing the target class name as string
@@ -52,15 +33,6 @@ class RelatedDog2(Entity):
     owner = field.Reference('tests.support.human.Human')
 
 
-class RelatedDog2Model(DictModel):
-    """ Model for the RelatedDog2 Entity"""
-
-    class Meta:
-        """ Meta class for model options"""
-        entity = RelatedDog2
-        model_name = 'related_dogs2'
-
-
 class DogRelatedByEmail(Entity):
     """This is a dummy Dog Entity class with an association"""
     name = field.String(required=True, unique=True, max_length=50)
@@ -68,15 +40,6 @@ class DogRelatedByEmail(Entity):
     owner = field.Reference(Human, via='email')
 
 
-class DogRelatedByEmailModel(DictModel):
-    """ Model for the DogRelatedByEmail Entity"""
-
-    class Meta:
-        """ Meta class for model options"""
-        entity = DogRelatedByEmail
-        model_name = 'related_dogs_by_email'
-
-
 class HasOneDog1(Entity):
     """This is a dummy Dog Entity class to test HasOne Association"""
     name = field.String(required=True, unique=True, max_length=50)
@@ -84,15 +47,6 @@ class HasOneDog1(Entity):
     has_one_human1 = field.Reference(HasOneHuman1)
 
 
-class HasOneDog1Model(DictModel):
-    """ Model for the HasOneDog1 Entity"""
-
-    class Meta:
-        """ Meta class for model options"""
-        entity = HasOneDog1
-        model_name = 'has_one_dogs1'
-
-
 class HasOneDog2(Entity):
     """This is a dummy Dog Entity class to test HasOne Association, where the associated
        has defined a `via` attribute to finetune linkage
@@ -102,15 +56,6 @@ class HasOneDog2(Entity):
     human = field.Reference(HasOneHuman2)
 
 
-class HasOneDog2Model(DictModel):
-    """ Model for the HasOneDog2 Entity"""
-
-    class Meta:
-        """ Meta class for model options"""
-        entity = HasOneDog2
-        model_name = 'has_one_dogs2'
-
-
 class HasOneDog3(Entity):
     """This is a dummy Dog Entity class to test HasOne Association"""
     name = field.String(required=True, unique=True, max_length=50)
@@ -118,15 +63,6 @@ class HasOneDog3(Entity):
     human_id = field.Integer()
 
 
-class HasOneDog3Model(DictModel):
-    """ Model for the HasOneDog3 Entity"""
-
-    class Meta:
-        """ Meta class for model options"""
-        entity = HasOneDog3
-        model_name = 'has_one_dogs3'
-
-
 class HasManyDog1(Entity):
     """This is a dummy Dog Entity class to test HasMany Association"""
     name = field.String(required=True, unique=True, max_length=50)
@@ -134,15 +70,6 @@ class HasManyDog1(Entity):
     has_many_human1 = field.Reference(HasManyHuman1)
 
 
-class HasManyDog1Model(DictModel):
-    """ Model for the HasManyDog1 Entity"""
-
-    class Meta:
-        """ Meta class for model options"""
-        entity = HasManyDog1
-        model_name = 'has_many_dogs1'
-
-
 class HasManyDog2(Entity):
     """This is a dummy Dog Entity class to test HasMany Association, where the associated
        has defined a `via` attribute to finetune linkage
@@ -152,15 +79,6 @@ class HasManyDog2(Entity):
     human = field.Reference(HasManyHuman2)
 
 
-class HasManyDog2Model(DictModel):
-    """ Model for the HasManyDog2 Entity"""
-
-    class Meta:
-        """ Meta class for model options"""
-        entity = HasManyDog2
-        model_name = 'has_many_dogs2'
-
-
 class HasManyDog3(Entity):
     """This is a dummy Dog Entity class to test HasMany Association"""
     name = field.String(required=True, unique=True, max_length=50)
@@ -168,25 +86,7 @@ class HasManyDog3(Entity):
     human_id = field.Integer()
 
 
-class HasManyDog3Model(DictModel):
-    """ Model for the HasManyDog3 Entity"""
-
-    class Meta:
-        """ Meta class for model options"""
-        entity = HasManyDog3
-        model_name = 'has_many_dogs3'
-
-
 class ThreadedDog(Entity):
     """This is a dummy Dog Entity class"""
     name = field.String(required=True, max_length=50)
     created_by = field.String(required=True, max_length=15)
-
-
-class ThreadedDogModel(DictModel):
-    """ Model for the ThreadedDog Entity"""
-
-    class Meta:
-        """ Meta class for schema options"""
-        entity = ThreadedDog
-        model_name = 'threaded_dogs'
diff --git a/tests/support/human.py b/tests/support/human.py
index ee64d064..e07dece7 100644
--- a/tests/support/human.py
+++ b/tests/support/human.py
@@ -3,7 +3,6 @@
 from protean.core import field
 from protean.core.entity import Entity
 from protean.core.field import association
-from protean.impl.repository.dict_repo import DictModel
 
 
 class Human(Entity):
@@ -13,15 +12,6 @@ class Human(Entity):
     email = field.String(required=True, unique=True, max_length=50)
 
 
-class HumanModel(DictModel):
-    """ Model for the Human Entity"""
-
-    class Meta:
-        """ Meta class for model options"""
-        entity = Human
-        model_name = 'humans'
-
-
 class HasOneHuman1(Entity):
     """This is a dummy Human Entity class to test HasOne association"""
     first_name = field.String(required=True, unique=True, max_length=50)
@@ -30,15 +20,6 @@ class HasOneHuman1(Entity):
     dog = association.HasOne('tests.support.dog.HasOneDog1')
 
 
-class HasOneHuman1Model(DictModel):
-    """ Model for the HasOneHuman1 Entity"""
-
-    class Meta:
-        """ Meta class for model options"""
-        entity = HasOneHuman1
-        model_name = 'has_one_humans1'
-
-
 class HasOneHuman2(Entity):
     """This is a dummy Human Entity class to test HasOne association
        with a custom attribute defined in `via` argument to field
@@ -49,15 +30,6 @@ class HasOneHuman2(Entity):
     dog = association.HasOne('tests.support.dog.HasOneDog2', via='human_id')
 
 
-class HasOneHuman2Model(DictModel):
-    """ Model for the HasOneHuman2 Entity"""
-
-    class Meta:
-        """ Meta class for model options"""
-        entity = HasOneHuman2
-        model_name = 'has_one_humans2'
-
-
 class HasOneHuman3(Entity):
     """This is a dummy Human Entity class to test HasOne association
        when there is no corresponding Reference defined in the target class
@@ -68,15 +40,6 @@ class HasOneHuman3(Entity):
     dog = association.HasOne('tests.support.dog.HasOneDog3', via='human_id')
 
 
-class HasOneHuman3Model(DictModel):
-    """ Model for the HasOneHuman3 Entity"""
-
-    class Meta:
-        """ Meta class for model options"""
-        entity = HasOneHuman3
-        model_name = 'has_one_humans3'
-
-
 class HasManyHuman1(Entity):
     """This is a dummy Human Entity class to test HasMany association"""
     first_name = field.String(required=True, unique=True, max_length=50)
@@ -85,15 +48,6 @@ class HasManyHuman1(Entity):
     dogs = association.HasMany('tests.support.dog.HasManyDog1')
 
 
-class HasManyHuman1Model(DictModel):
-    """ Model for the HasManyHuman1 Entity"""
-
-    class Meta:
-        """ Meta class for model options"""
-        entity = HasManyHuman1
-        model_name = 'has_many_humans1'
-
-
 class HasManyHuman2(Entity):
     """This is a dummy Human Entity class to test HasMany association
        with a custom attribute defined in `via` argument to field
@@ -104,15 +58,6 @@ class HasManyHuman2(Entity):
     dogs = association.HasMany('HasManyDog2', via='human_id')
 
 
-class HasManyHuman2Model(DictModel):
-    """ Model for the HasManyHuman2 Entity"""
-
-    class Meta:
-        """ Meta class for model options"""
-        entity = HasManyHuman2
-        model_name = 'has_many_humans2'
-
-
 class HasManyHuman3(Entity):
     """This is a dummy Human Entity class to test HasMany association
        when there is no corresponding Reference defined in the target class
@@ -121,12 +66,3 @@ class HasManyHuman3(Entity):
     last_name = field.String(required=True, unique=True, max_length=50)
     email = field.String(required=True, unique=True, max_length=50)
     dogs = association.HasMany('tests.support.dog.HasManyDog3', via='human_id')
-
-
-class HasManyHuman3Model(DictModel):
-    """ Model for the HasManyHuman3 Entity"""
-
-    class Meta:
-        """ Meta class for model options"""
-        entity = HasManyHuman3
-        model_name = 'has_many_humans3'