fill docstrings on mixins
This commit is contained in:
@ -2,6 +2,10 @@ from typing import Dict, TYPE_CHECKING
|
||||
|
||||
|
||||
class AliasMixin:
|
||||
"""
|
||||
Used to translate field names into database column names.
|
||||
"""
|
||||
|
||||
if TYPE_CHECKING: # pragma: no cover
|
||||
from ormar import ModelMeta
|
||||
|
||||
|
||||
@ -25,6 +25,10 @@ if TYPE_CHECKING: # pragma no cover
|
||||
|
||||
|
||||
class ExcludableMixin(RelationMixin):
|
||||
"""
|
||||
Used to include/exclude given set of fields on models during load and dict() calls.
|
||||
"""
|
||||
|
||||
if TYPE_CHECKING: # pragma: no cover
|
||||
from ormar import Model
|
||||
|
||||
@ -32,6 +36,16 @@ class ExcludableMixin(RelationMixin):
|
||||
def get_child(
|
||||
items: Union[Set, Dict, None], key: str = None
|
||||
) -> Union[Set, Dict, None]:
|
||||
"""
|
||||
Used to get nested dictionaries keys if they exists otherwise returns
|
||||
passed items.
|
||||
:param items: bag of items to include or exclude
|
||||
:type items: Union[Set, Dict, None]
|
||||
:param key: name of the child to extract
|
||||
:type key: str
|
||||
:return: child extracted from items if exists
|
||||
:rtype: Union[Set, Dict, None]
|
||||
"""
|
||||
if isinstance(items, dict):
|
||||
return items.get(key, {})
|
||||
return items
|
||||
@ -40,16 +54,46 @@ class ExcludableMixin(RelationMixin):
|
||||
def get_excluded(
|
||||
exclude: Union[Set, Dict, None], key: str = None
|
||||
) -> Union[Set, Dict, None]:
|
||||
"""
|
||||
Proxy to ExcludableMixin.get_child for exclusions.
|
||||
|
||||
:param exclude: bag of items to exclude
|
||||
:type exclude: Union[Set, Dict, None]
|
||||
:param key: name of the child to extract
|
||||
:type key: str
|
||||
:return: child extracted from items if exists
|
||||
:rtype: Union[Set, Dict, None]
|
||||
"""
|
||||
return ExcludableMixin.get_child(items=exclude, key=key)
|
||||
|
||||
@staticmethod
|
||||
def get_included(
|
||||
include: Union[Set, Dict, None], key: str = None
|
||||
) -> Union[Set, Dict, None]:
|
||||
"""
|
||||
Proxy to ExcludableMixin.get_child for inclusions.
|
||||
|
||||
:param include: bag of items to include
|
||||
:type include: Union[Set, Dict, None]
|
||||
:param key: name of the child to extract
|
||||
:type key: str
|
||||
:return: child extracted from items if exists
|
||||
:rtype: Union[Set, Dict, None]
|
||||
"""
|
||||
return ExcludableMixin.get_child(items=include, key=key)
|
||||
|
||||
@staticmethod
|
||||
def is_excluded(exclude: Union[Set, Dict, None], key: str = None) -> bool:
|
||||
"""
|
||||
Checks if given key should be excluded on model/ dict.
|
||||
|
||||
:param exclude: bag of items to exclude
|
||||
:type exclude: Union[Set, Dict, None]
|
||||
:param key: name of the child to extract
|
||||
:type key: str
|
||||
:return: child extracted from items if exists
|
||||
:rtype: Union[Set, Dict, None]
|
||||
"""
|
||||
if exclude is None:
|
||||
return False
|
||||
if exclude is Ellipsis: # pragma: nocover
|
||||
@ -63,6 +107,16 @@ class ExcludableMixin(RelationMixin):
|
||||
|
||||
@staticmethod
|
||||
def is_included(include: Union[Set, Dict, None], key: str = None) -> bool:
|
||||
"""
|
||||
Checks if given key should be included on model/ dict.
|
||||
|
||||
:param include: bag of items to include
|
||||
:type include: Union[Set, Dict, None]
|
||||
:param key: name of the child to extract
|
||||
:type key: str
|
||||
:return: child extracted from items if exists
|
||||
:rtype: Union[Set, Dict, None]
|
||||
"""
|
||||
if include is None:
|
||||
return True
|
||||
if include is Ellipsis:
|
||||
@ -78,6 +132,19 @@ class ExcludableMixin(RelationMixin):
|
||||
def _populate_pk_column(
|
||||
model: Type["Model"], columns: List[str], use_alias: bool = False,
|
||||
) -> List[str]:
|
||||
"""
|
||||
Adds primary key column/alias (depends on use_alias flag) to list of
|
||||
column names that are selected.
|
||||
|
||||
:param model: model on columns are selected
|
||||
:type model: Type["Model"]
|
||||
:param columns: list of columns names
|
||||
:type columns: List[str]
|
||||
:param use_alias: flag to set if aliases or field names should be used
|
||||
:type use_alias: bool
|
||||
:return: list of columns names with pk column in it
|
||||
:rtype: List[str]
|
||||
"""
|
||||
pk_alias = (
|
||||
model.get_column_alias(model.Meta.pkname)
|
||||
if use_alias
|
||||
@ -95,6 +162,26 @@ class ExcludableMixin(RelationMixin):
|
||||
exclude_fields: Optional[Union[Set, Dict]],
|
||||
use_alias: bool = False,
|
||||
) -> List[str]:
|
||||
"""
|
||||
Returns list of aliases or field names for given model.
|
||||
Aliases/names switch is use_alias flag.
|
||||
|
||||
If provided only fields included in fields will be returned.
|
||||
If provided fields in exclude_fields will be excluded in return.
|
||||
|
||||
Primary key field is always added and cannot be excluded (will be added anyway).
|
||||
|
||||
:param model: model on columns are selected
|
||||
:type model: Type["Model"]
|
||||
:param fields: set/dict of fields to include
|
||||
:type fields: Optional[Union[Set, Dict]]
|
||||
:param exclude_fields: set/dict of fields to exclude
|
||||
:type exclude_fields: Optional[Union[Set, Dict]]
|
||||
:param use_alias: flag if aliases or field names should be used
|
||||
:type use_alias: bool
|
||||
:return: list of column field names or aliases
|
||||
:rtype: List[str]
|
||||
"""
|
||||
columns = [
|
||||
model.get_column_name_from_alias(col.name) if not use_alias else col.name
|
||||
for col in model.Meta.table.columns
|
||||
@ -129,6 +216,21 @@ class ExcludableMixin(RelationMixin):
|
||||
exclude: Union["AbstractSetIntStr", "MappingIntStrAny", None],
|
||||
nested: bool = False,
|
||||
) -> Union[Set, Dict]:
|
||||
"""
|
||||
Used during generation of the dict().
|
||||
To avoid cyclical references and max recurrence limit nested models have to
|
||||
exclude related models that are not mandatory.
|
||||
|
||||
For a main model (not nested) only nullable related field names are added to
|
||||
exclusion, for nested models all related models are excluded.
|
||||
|
||||
:param exclude: set/dict with fields to exclude
|
||||
:type exclude: Union[Set, Dict, None]
|
||||
:param nested: flag setting nested models (child of previous one, not main one)
|
||||
:type nested: bool
|
||||
:return: set or dict with excluded fields added.
|
||||
:rtype: Union[Set, Dict]
|
||||
"""
|
||||
exclude = exclude or {}
|
||||
related_set = cls._exclude_related_names_not_required(nested=nested)
|
||||
if isinstance(exclude, set):
|
||||
@ -144,6 +246,23 @@ class ExcludableMixin(RelationMixin):
|
||||
fields: Optional[Union[Dict, Set]] = None,
|
||||
exclude_fields: Optional[Union[Dict, Set]] = None,
|
||||
) -> Set:
|
||||
"""
|
||||
Returns a set of models field names that should be explicitly excluded
|
||||
during model initialization.
|
||||
|
||||
Those fields will be set to None to avoid ormar/pydantic setting default
|
||||
values on them. They should be returned as None in any case.
|
||||
|
||||
Used in parsing data from database rows that construct Models by initializing
|
||||
them with dicts constructed from those db rows.
|
||||
|
||||
:param fields: set/dict of fields to include
|
||||
:type fields: Optional[Union[Set, Dict]]
|
||||
:param exclude_fields: set/dict of fields to exclude
|
||||
:type exclude_fields: Optional[Union[Set, Dict]]
|
||||
:return: set of field names that should be excluded
|
||||
:rtype: Set
|
||||
"""
|
||||
fields_names = cls.extract_db_own_fields()
|
||||
if fields and fields is not Ellipsis:
|
||||
fields_to_keep = {name for name in fields if name in fields_names}
|
||||
|
||||
@ -8,8 +8,28 @@ if TYPE_CHECKING: # pragma no cover
|
||||
|
||||
|
||||
class MergeModelMixin:
|
||||
"""
|
||||
Used to merge models instances returned by database,
|
||||
but already initialized to ormar Models.keys
|
||||
|
||||
Models can duplicate during joins when parent model has multiple child rows,
|
||||
in the end all parent (main) models should be unique.
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def merge_instances_list(cls, result_rows: Sequence["Model"]) -> Sequence["Model"]:
|
||||
"""
|
||||
Merges a list of models into list of unique models.
|
||||
|
||||
Models can duplicate during joins when parent model has multiple child rows,
|
||||
in the end all parent (main) models should be unique.
|
||||
|
||||
:param result_rows: list of already initialized Models with child models
|
||||
populated, each instance is one row in db and some models can duplicate
|
||||
:type result_rows: List["Model"]
|
||||
:return: list of merged models where each main model is unique
|
||||
:rtype: List["Model"]
|
||||
"""
|
||||
merged_rows: List["Model"] = []
|
||||
grouped_instances: OrderedDict = OrderedDict()
|
||||
|
||||
@ -27,6 +47,19 @@ class MergeModelMixin:
|
||||
|
||||
@classmethod
|
||||
def merge_two_instances(cls, one: "Model", other: "Model") -> "Model":
|
||||
"""
|
||||
Merges current (other) Model and previous one (one) and returns the current
|
||||
Model instance with data merged from previous one.
|
||||
|
||||
If needed it's calling itself recurrently and merges also children models.
|
||||
|
||||
:param one: previous model instance
|
||||
:type one: Model
|
||||
:param other: current model instance
|
||||
:type other: Model
|
||||
:return: current Model instance with data merged from previous one.
|
||||
:rtype: Model
|
||||
"""
|
||||
for field in one.Meta.model_fields.keys():
|
||||
current_field = getattr(one, field)
|
||||
if isinstance(current_field, list) and not isinstance(
|
||||
|
||||
@ -6,6 +6,10 @@ from ormar.models.mixins.relation_mixin import RelationMixin
|
||||
|
||||
|
||||
class PrefetchQueryMixin(RelationMixin):
|
||||
"""
|
||||
Used in PrefetchQuery to extract ids and names of models to prefetch.
|
||||
"""
|
||||
|
||||
if TYPE_CHECKING: # pragma no cover
|
||||
from ormar import Model
|
||||
|
||||
@ -18,6 +22,20 @@ class PrefetchQueryMixin(RelationMixin):
|
||||
reverse: bool,
|
||||
related: str,
|
||||
) -> Tuple[Type["Model"], str]:
|
||||
"""
|
||||
Returns Model on which query clause should be performed and name of the column.
|
||||
|
||||
:param parent_model: related model that the relation lead to
|
||||
:type parent_model: Type[Model]
|
||||
:param target_model: model on which query should be perfomed
|
||||
:type target_model: Type[Model]
|
||||
:param reverse: flag if the relation is reverse
|
||||
:type reverse: bool
|
||||
:param related: name of the relation field
|
||||
:type related: str
|
||||
:return: Model on which query clause should be performed and name of the column
|
||||
:rtype: Tuple[Type[Model], str]
|
||||
"""
|
||||
if reverse:
|
||||
field_name = (
|
||||
parent_model.Meta.model_fields[related].related_name
|
||||
@ -36,6 +54,22 @@ class PrefetchQueryMixin(RelationMixin):
|
||||
def get_column_name_for_id_extraction(
|
||||
parent_model: Type["Model"], reverse: bool, related: str, use_raw: bool,
|
||||
) -> str:
|
||||
"""
|
||||
Returns name of the column that should be used to extract ids from model.
|
||||
Depending on the relation side it's either primary key column of parent model
|
||||
or field name specified by related parameter.
|
||||
|
||||
:param parent_model: model from which id column should be extracted
|
||||
:type parent_model: Type[Model]
|
||||
:param reverse: flag if the relation is reverse
|
||||
:type reverse: bool
|
||||
:param related: name of the relation field
|
||||
:type related: str
|
||||
:param use_raw: flag if aliases or field names should be used
|
||||
:type use_raw: bool
|
||||
:return:
|
||||
:rtype:
|
||||
"""
|
||||
if reverse:
|
||||
column_name = parent_model.Meta.pkname
|
||||
return (
|
||||
@ -46,6 +80,16 @@ class PrefetchQueryMixin(RelationMixin):
|
||||
|
||||
@classmethod
|
||||
def get_related_field_name(cls, target_field: Type["BaseField"]) -> str:
|
||||
"""
|
||||
Returns name of the relation field that should be used in prefetch query.
|
||||
This field is later used to register relation in prefetch query,
|
||||
populate relations dict, and populate nested model in prefetch query.
|
||||
|
||||
:param target_field: relation field that should be used in prefetch
|
||||
:type target_field: Type[BaseField]
|
||||
:return: name of the field
|
||||
:rtype: str
|
||||
"""
|
||||
if issubclass(target_field, ormar.fields.ManyToManyField):
|
||||
return cls.get_name()
|
||||
if target_field.virtual:
|
||||
@ -54,6 +98,20 @@ class PrefetchQueryMixin(RelationMixin):
|
||||
|
||||
@classmethod
|
||||
def get_filtered_names_to_extract(cls, prefetch_dict: Dict) -> List:
|
||||
"""
|
||||
Returns list of related fields names that should be followed to prefetch related
|
||||
models from.
|
||||
|
||||
List of models is translated into dict to assure each model is extracted only
|
||||
once in one query, that's why this function accepts prefetch_dict not list.
|
||||
|
||||
Only relations from current model are returned.
|
||||
|
||||
:param prefetch_dict: dictionary of fields to extract
|
||||
:type prefetch_dict: Dict
|
||||
:return: list of fields names to extract
|
||||
:rtype: List
|
||||
"""
|
||||
related_to_extract = []
|
||||
if prefetch_dict and prefetch_dict is not Ellipsis:
|
||||
related_to_extract = [
|
||||
|
||||
@ -5,6 +5,10 @@ from ormar.fields.foreign_key import ForeignKeyField
|
||||
|
||||
|
||||
class RelationMixin:
|
||||
"""
|
||||
Used to return relation fields/names etc. from given model
|
||||
"""
|
||||
|
||||
if TYPE_CHECKING: # pragma no cover
|
||||
from ormar import ModelMeta
|
||||
|
||||
@ -14,6 +18,12 @@ class RelationMixin:
|
||||
|
||||
@classmethod
|
||||
def extract_db_own_fields(cls) -> Set:
|
||||
"""
|
||||
Returns only fields that are stored in the own database table, exclude all
|
||||
related fields.
|
||||
:return: set of model fields with relation fields excluded
|
||||
:rtype: Set
|
||||
"""
|
||||
related_names = cls.extract_related_names()
|
||||
self_fields = {
|
||||
name for name in cls.Meta.model_fields.keys() if name not in related_names
|
||||
@ -22,7 +32,13 @@ class RelationMixin:
|
||||
|
||||
@classmethod
|
||||
def extract_related_fields(cls) -> List:
|
||||
"""
|
||||
Returns List of ormar Fields for all relations declared on a model.
|
||||
List is cached in cls._related_fields for quicker access.
|
||||
|
||||
:return: list of related fields
|
||||
:rtype: List
|
||||
"""
|
||||
if isinstance(cls._related_fields, List):
|
||||
return cls._related_fields
|
||||
|
||||
@ -35,7 +51,13 @@ class RelationMixin:
|
||||
|
||||
@classmethod
|
||||
def extract_related_names(cls) -> Set:
|
||||
"""
|
||||
Returns List of fields names for all relations declared on a model.
|
||||
List is cached in cls._related_names for quicker access.
|
||||
|
||||
:return: list of related fields names
|
||||
:rtype: List
|
||||
"""
|
||||
if isinstance(cls._related_names, Set):
|
||||
return cls._related_names
|
||||
|
||||
@ -49,6 +71,12 @@ class RelationMixin:
|
||||
|
||||
@classmethod
|
||||
def _extract_db_related_names(cls) -> Set:
|
||||
"""
|
||||
Returns only fields that are stored in the own database table, exclude
|
||||
related fields that are not stored as foreign keys on given model.
|
||||
:return: set of model fields with non fk relation fields excluded
|
||||
:rtype: Set
|
||||
"""
|
||||
related_names = cls.extract_related_names()
|
||||
related_names = {
|
||||
name
|
||||
@ -59,6 +87,17 @@ class RelationMixin:
|
||||
|
||||
@classmethod
|
||||
def _exclude_related_names_not_required(cls, nested: bool = False) -> Set:
|
||||
"""
|
||||
Returns a set of non mandatory related models field names.
|
||||
|
||||
For a main model (not nested) only nullable related field names are returned,
|
||||
for nested models all related models are returned.
|
||||
|
||||
:param nested: flag setting nested models (child of previous one, not main one)
|
||||
:type nested: bool
|
||||
:return: set of non mandatory related fields
|
||||
:rtype: Set
|
||||
"""
|
||||
if nested:
|
||||
return cls.extract_related_names()
|
||||
related_names = cls.extract_related_names()
|
||||
|
||||
@ -6,8 +6,21 @@ from ormar.models.mixins.relation_mixin import RelationMixin
|
||||
|
||||
|
||||
class SavePrepareMixin(RelationMixin):
|
||||
"""
|
||||
Used to prepare models to be saved in database
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def substitute_models_with_pks(cls, model_dict: Dict) -> Dict: # noqa CCR001
|
||||
"""
|
||||
Receives dictionary of model that is about to be saved and changes all related
|
||||
models that are stored as foreign keys to their fk value.
|
||||
|
||||
:param model_dict: dictionary of model that is about to be saved
|
||||
:type model_dict: Dict
|
||||
:return: dictionary of model that is about to be saved
|
||||
:rtype: Dict
|
||||
"""
|
||||
for field in cls.extract_related_names():
|
||||
field_value = model_dict.get(field, None)
|
||||
if field_value is not None:
|
||||
@ -34,6 +47,16 @@ class SavePrepareMixin(RelationMixin):
|
||||
|
||||
@classmethod
|
||||
def populate_default_values(cls, new_kwargs: Dict) -> Dict:
|
||||
"""
|
||||
Receives dictionary of model that is about to be saved and populates the default
|
||||
value on the fields that have the default value set, but no actual value was
|
||||
passed by the user.
|
||||
|
||||
:param new_kwargs: dictionary of model that is about to be saved
|
||||
:type new_kwargs: Dict
|
||||
:return: dictionary of model that is about to be saved
|
||||
:rtype: Dict
|
||||
"""
|
||||
for field_name, field in cls.Meta.model_fields.items():
|
||||
if (
|
||||
field_name not in new_kwargs
|
||||
|
||||
Reference in New Issue
Block a user