update docs, add load_all(), tests for load_all, make through field optional

This commit is contained in:
collerek
2021-03-03 19:48:40 +01:00
parent 9ad1528cc0
commit a8ae50276e
56 changed files with 1653 additions and 653 deletions

View File

@ -0,0 +1,188 @@
<a name="models.excludable"></a>
# models.excludable
<a name="models.excludable.Excludable"></a>
## Excludable Objects
```python
@dataclass
class Excludable()
```
Class that keeps sets of fields to exclude and include
<a name="models.excludable.Excludable.get_copy"></a>
#### get\_copy
```python
| get_copy() -> "Excludable"
```
Return copy of self to avoid in place modifications
**Returns**:
`(ormar.models.excludable.Excludable)`: copy of self with copied sets
<a name="models.excludable.Excludable.set_values"></a>
#### set\_values
```python
| set_values(value: Set, is_exclude: bool) -> None
```
Appends the data to include/exclude sets.
**Arguments**:
- `value (set)`: set of values to add
- `is_exclude (bool)`: flag if values are to be excluded or included
<a name="models.excludable.Excludable.is_included"></a>
#### is\_included
```python
| is_included(key: str) -> bool
```
Check if field in included (in set or set is {...})
**Arguments**:
- `key (str)`: key to check
**Returns**:
`(bool)`: result of the check
<a name="models.excludable.Excludable.is_excluded"></a>
#### is\_excluded
```python
| is_excluded(key: str) -> bool
```
Check if field in excluded (in set or set is {...})
**Arguments**:
- `key (str)`: key to check
**Returns**:
`(bool)`: result of the check
<a name="models.excludable.ExcludableItems"></a>
## ExcludableItems Objects
```python
class ExcludableItems()
```
Keeps a dictionary of Excludables by alias + model_name keys
to allow quick lookup by nested models without need to travers
deeply nested dictionaries and passing include/exclude around
<a name="models.excludable.ExcludableItems.from_excludable"></a>
#### from\_excludable
```python
| @classmethod
| from_excludable(cls, other: "ExcludableItems") -> "ExcludableItems"
```
Copy passed ExcludableItems to avoid inplace modifications.
**Arguments**:
- `other (ormar.models.excludable.ExcludableItems)`: other excludable items to be copied
**Returns**:
`(ormar.models.excludable.ExcludableItems)`: copy of other
<a name="models.excludable.ExcludableItems.get"></a>
#### get
```python
| get(model_cls: Type["Model"], alias: str = "") -> Excludable
```
Return Excludable for given model and alias.
**Arguments**:
- `model_cls (ormar.models.metaclass.ModelMetaclass)`: target model to check
- `alias (str)`: table alias from relation manager
**Returns**:
`(ormar.models.excludable.Excludable)`: Excludable for given model and alias
<a name="models.excludable.ExcludableItems.build"></a>
#### build
```python
| build(items: Union[List[str], str, Tuple[str], Set[str], Dict], model_cls: Type["Model"], is_exclude: bool = False) -> None
```
Receives the one of the types of items and parses them as to achieve
a end situation with one excludable per alias/model in relation.
Each excludable has two sets of values - one to include, one to exclude.
**Arguments**:
- `items (Union[List[str], str, Tuple[str], Set[str], Dict])`: values to be included or excluded
- `model_cls (ormar.models.metaclass.ModelMetaclass)`: source model from which relations are constructed
- `is_exclude (bool)`: flag if items should be included or excluded
<a name="models.excludable.ExcludableItems._set_excludes"></a>
#### \_set\_excludes
```python
| _set_excludes(items: Set, model_name: str, is_exclude: bool, alias: str = "") -> None
```
Sets set of values to be included or excluded for given key and model.
**Arguments**:
- `items (set)`: items to include/exclude
- `model_name (str)`: name of model to construct key
- `is_exclude (bool)`: flag if values should be included or excluded
- `alias (str)`:
<a name="models.excludable.ExcludableItems._traverse_dict"></a>
#### \_traverse\_dict
```python
| _traverse_dict(values: Dict, source_model: Type["Model"], model_cls: Type["Model"], is_exclude: bool, related_items: List = None, alias: str = "") -> None
```
Goes through dict of nested values and construct/update Excludables.
**Arguments**:
- `values (Dict)`: items to include/exclude
- `source_model (ormar.models.metaclass.ModelMetaclass)`: source model from which relations are constructed
- `model_cls (ormar.models.metaclass.ModelMetaclass)`: model from which current relation is constructed
- `is_exclude (bool)`: flag if values should be included or excluded
- `related_items (List)`: list of names of related fields chain
- `alias (str)`: alias of relation
<a name="models.excludable.ExcludableItems._traverse_list"></a>
#### \_traverse\_list
```python
| _traverse_list(values: Set[str], model_cls: Type["Model"], is_exclude: bool) -> None
```
Goes through list of values and construct/update Excludables.
**Arguments**:
- `values (set)`: items to include/exclude
- `model_cls (ormar.models.metaclass.ModelMetaclass)`: model from which current relation is constructed
- `is_exclude (bool)`: flag if values should be included or excluded

View File

@ -87,28 +87,6 @@ extraction of ormar model_fields.
`(Tuple[Dict, Dict])`: namespace of the class updated, dict of extracted model_fields
<a name="models.helpers.models.validate_related_names_in_relations"></a>
#### validate\_related\_names\_in\_relations
```python
validate_related_names_in_relations(model_fields: Dict, new_model: Type["Model"]) -> None
```
Performs a validation of relation_names in relation fields.
If multiple fields are leading to the same related model
only one can have empty related_name param
(populated by default as model.name.lower()+'s').
Also related_names have to be unique for given related model.
**Raises**:
- `ModelDefinitionError`: if validation of related_names fail
**Arguments**:
- `model_fields (Dict[str, ormar.Field])`: dictionary of declared ormar model fields
- `new_model (Model class)`:
<a name="models.helpers.models.group_related_list"></a>
#### group\_related\_list
@ -134,3 +112,23 @@ Result dictionary is sorted by length of the values and by key
`(Dict[str, List])`: list converted to dictionary to avoid repetition and group nested models
<a name="models.helpers.models.meta_field_not_set"></a>
#### meta\_field\_not\_set
```python
meta_field_not_set(model: Type["Model"], field_name: str) -> bool
```
Checks if field with given name is already present in model.Meta.
Then check if it's set to something truthful
(in practice meaning not None, as it's non or ormar Field only).
**Arguments**:
- `model (Model class)`: newly constructed model
- `field_name (str)`: name of the ormar field
**Returns**:
`(bool)`: result of the check

View File

@ -5,7 +5,7 @@
#### create\_pydantic\_field
```python
create_pydantic_field(field_name: str, model: Type["Model"], model_field: Type[ManyToManyField]) -> None
create_pydantic_field(field_name: str, model: Type["Model"], model_field: Type["ManyToManyField"]) -> None
```
Registers pydantic field on through model that leads to passed model
@ -42,7 +42,7 @@ field_name. Returns a pydantic field with type of field_name field type.
#### populate\_default\_pydantic\_field\_value
```python
populate_default_pydantic_field_value(ormar_field: Type[BaseField], field_name: str, attrs: dict) -> dict
populate_default_pydantic_field_value(ormar_field: Type["BaseField"], field_name: str, attrs: dict) -> dict
```
Grabs current value of the ormar Field in class namespace
@ -94,7 +94,7 @@ Those annotations are later used by pydantic to construct it's own fields.
#### get\_pydantic\_base\_orm\_config
```python
get_pydantic_base_orm_config() -> Type[BaseConfig]
get_pydantic_base_orm_config() -> Type[pydantic.BaseConfig]
```
Returns empty pydantic Config with orm_mode set to True.

View File

@ -0,0 +1,25 @@
<a name="models.helpers.related_names_validation"></a>
# models.helpers.related\_names\_validation
<a name="models.helpers.related_names_validation.validate_related_names_in_relations"></a>
#### validate\_related\_names\_in\_relations
```python
validate_related_names_in_relations(model_fields: Dict, new_model: Type["Model"]) -> None
```
Performs a validation of relation_names in relation fields.
If multiple fields are leading to the same related model
only one can have empty related_name param
(populated by default as model.name.lower()+'s').
Also related_names have to be unique for given related model.
**Raises**:
- `ModelDefinitionError`: if validation of related_names fail
**Arguments**:
- `model_fields (Dict[str, ormar.Field])`: dictionary of declared ormar model fields
- `new_model (Model class)`:

View File

@ -23,7 +23,7 @@ aliases for proper sql joins.
#### register\_many\_to\_many\_relation\_on\_build
```python
register_many_to_many_relation_on_build(field: Type[ManyToManyField]) -> None
register_many_to_many_relation_on_build(field: Type["ManyToManyField"]) -> None
```
Registers connection between through model and both sides of the m2m relation.
@ -89,11 +89,24 @@ Autogenerated reverse fields also set related_name to the original field name.
- `model_field (relation Field)`: original relation ForeignKey field
<a name="models.helpers.relations.register_through_shortcut_fields"></a>
#### register\_through\_shortcut\_fields
```python
register_through_shortcut_fields(model_field: Type["ManyToManyField"]) -> None
```
Registers m2m relation through shortcut on both ends of the relation.
**Arguments**:
- `model_field (ManyToManyField)`: relation field defined in parent model
<a name="models.helpers.relations.register_relation_in_alias_manager"></a>
#### register\_relation\_in\_alias\_manager
```python
register_relation_in_alias_manager(field: Type[ForeignKeyField]) -> None
register_relation_in_alias_manager(field: Type["ForeignKeyField"]) -> None
```
Registers the relation (and reverse relation) in alias manager.

View File

@ -5,7 +5,7 @@
#### adjust\_through\_many\_to\_many\_model
```python
adjust_through_many_to_many_model(model_field: Type[ManyToManyField]) -> None
adjust_through_many_to_many_model(model_field: Type["ManyToManyField"]) -> None
```
Registers m2m relation on through model.
@ -21,7 +21,7 @@ Sets pydantic fields with child and parent model types.
#### create\_and\_append\_m2m\_fk
```python
create_and_append_m2m_fk(model: Type["Model"], model_field: Type[ManyToManyField], field_name: str) -> None
create_and_append_m2m_fk(model: Type["Model"], model_field: Type["ManyToManyField"], field_name: str) -> None
```
Registers sqlalchemy Column with sqlalchemy.ForeignKey leading to the model.
@ -38,7 +38,7 @@ Newly created field is added to m2m relation through model Meta columns and tabl
#### check\_pk\_column\_validity
```python
check_pk_column_validity(field_name: str, field: BaseField, pkname: Optional[str]) -> Optional[str]
check_pk_column_validity(field_name: str, field: "BaseField", pkname: Optional[str]) -> Optional[str]
```
Receives the field marked as primary key and verifies if the pkname
@ -165,7 +165,7 @@ It populates name, metadata, columns and constraints.
#### update\_column\_definition
```python
update_column_definition(model: Union[Type["Model"], Type["NewBaseModel"]], field: Type[ForeignKeyField]) -> None
update_column_definition(model: Union[Type["Model"], Type["NewBaseModel"]], field: Type["ForeignKeyField"]) -> None
```
Updates a column with a new type column based on updated parameters in FK fields.

View File

@ -0,0 +1,120 @@
<a name="models.helpers.validation"></a>
# models.helpers.validation
<a name="models.helpers.validation.check_if_field_has_choices"></a>
#### check\_if\_field\_has\_choices
```python
check_if_field_has_choices(field: Type[BaseField]) -> bool
```
Checks if given field has choices populated.
A if it has one, a validator for this field needs to be attached.
**Arguments**:
- `field (BaseField)`: ormar field to check
**Returns**:
`(bool)`: result of the check
<a name="models.helpers.validation.convert_choices_if_needed"></a>
#### convert\_choices\_if\_needed
```python
convert_choices_if_needed(field: Type["BaseField"], value: Any) -> Tuple[Any, List]
```
Converts dates to isoformat as fastapi can check this condition in routes
and the fields are not yet parsed.
Converts enums to list of it's values.
Converts uuids to strings.
Converts decimal to float with given scale.
**Arguments**:
- `field (Type[BaseField])`: ormar field to check with choices
- `values (Dict)`: current values of the model to verify
**Returns**:
`(Tuple[Any, List])`: value, choices list
<a name="models.helpers.validation.validate_choices"></a>
#### validate\_choices
```python
validate_choices(field: Type["BaseField"], value: Any) -> None
```
Validates if given value is in provided choices.
**Raises**:
- `ValueError`: If value is not in choices.
**Arguments**:
- `field (Type[BaseField])`: field to validate
- `value (Any)`: value of the field
<a name="models.helpers.validation.choices_validator"></a>
#### choices\_validator
```python
choices_validator(cls: Type["Model"], values: Dict[str, Any]) -> Dict[str, Any]
```
Validator that is attached to pydantic model pre root validators.
Validator checks if field value is in field.choices list.
**Raises**:
- `ValueError`: if field value is outside of allowed choices.
**Arguments**:
- `cls (Model class)`: constructed class
- `values (Dict[str, Any])`: dictionary of field values (pydantic side)
**Returns**:
`(Dict[str, Any])`: values if pass validation, otherwise exception is raised
<a name="models.helpers.validation.construct_modify_schema_function"></a>
#### construct\_modify\_schema\_function
```python
construct_modify_schema_function(fields_with_choices: List) -> SchemaExtraCallable
```
Modifies the schema to include fields with choices validator.
Those fields will be displayed in schema as Enum types with available choices
values listed next to them.
**Arguments**:
- `fields_with_choices (List)`: list of fields with choices validation
**Returns**:
`(Callable)`: callable that will be run by pydantic to modify the schema
<a name="models.helpers.validation.populate_choices_validators"></a>
#### populate\_choices\_validators
```python
populate_choices_validators(model: Type["Model"]) -> None
```
Checks if Model has any fields with choices set.
If yes it adds choices validation into pre root validators.
**Arguments**:
- `model (Model class)`: newly constructed Model

View File

@ -30,88 +30,12 @@ passed items.
`(Union[Set, Dict, None])`: child extracted from items if exists
<a name="models.mixins.excludable_mixin.ExcludableMixin.get_excluded"></a>
#### get\_excluded
```python
| @staticmethod
| get_excluded(exclude: Union[Set, Dict, None], key: str = None) -> Union[Set, Dict, None]
```
Proxy to ExcludableMixin.get_child for exclusions.
**Arguments**:
- `exclude (Union[Set, Dict, None])`: bag of items to exclude
- `key (str)`: name of the child to extract
**Returns**:
`(Union[Set, Dict, None])`: child extracted from items if exists
<a name="models.mixins.excludable_mixin.ExcludableMixin.get_included"></a>
#### get\_included
```python
| @staticmethod
| get_included(include: Union[Set, Dict, None], key: str = None) -> Union[Set, Dict, None]
```
Proxy to ExcludableMixin.get_child for inclusions.
**Arguments**:
- `include (Union[Set, Dict, None])`: bag of items to include
- `key (str)`: name of the child to extract
**Returns**:
`(Union[Set, Dict, None])`: child extracted from items if exists
<a name="models.mixins.excludable_mixin.ExcludableMixin.is_excluded"></a>
#### is\_excluded
```python
| @staticmethod
| is_excluded(exclude: Union[Set, Dict, None], key: str = None) -> bool
```
Checks if given key should be excluded on model/ dict.
**Arguments**:
- `exclude (Union[Set, Dict, None])`: bag of items to exclude
- `key (str)`: name of the child to extract
**Returns**:
`(Union[Set, Dict, None])`: child extracted from items if exists
<a name="models.mixins.excludable_mixin.ExcludableMixin.is_included"></a>
#### is\_included
```python
| @staticmethod
| is_included(include: Union[Set, Dict, None], key: str = None) -> bool
```
Checks if given key should be included on model/ dict.
**Arguments**:
- `include (Union[Set, Dict, None])`: bag of items to include
- `key (str)`: name of the child to extract
**Returns**:
`(Union[Set, Dict, None])`: child extracted from items if exists
<a name="models.mixins.excludable_mixin.ExcludableMixin._populate_pk_column"></a>
#### \_populate\_pk\_column
```python
| @staticmethod
| _populate_pk_column(model: Type["Model"], columns: List[str], use_alias: bool = False) -> List[str]
| _populate_pk_column(model: Union[Type["Model"], Type["ModelRow"]], columns: List[str], use_alias: bool = False) -> List[str]
```
Adds primary key column/alias (depends on use_alias flag) to list of
@ -132,7 +56,7 @@ column names that are selected.
```python
| @classmethod
| own_table_columns(cls, model: Type["Model"], fields: Optional[Union[Set, Dict]], exclude_fields: Optional[Union[Set, Dict]], use_alias: bool = False) -> List[str]
| own_table_columns(cls, model: Union[Type["Model"], Type["ModelRow"]], excludable: ExcludableItems, alias: str = "", use_alias: bool = False) -> List[str]
```
Returns list of aliases or field names for given model.
@ -145,9 +69,9 @@ Primary key field is always added and cannot be excluded (will be added anyway).
**Arguments**:
- `alias (str)`: relation prefix
- `excludable (ExcludableItems)`: structure of fields to include and exclude
- `model (Type["Model"])`: model on columns are selected
- `fields (Optional[Union[Set, Dict]])`: set/dict of fields to include
- `exclude_fields (Optional[Union[Set, Dict]])`: set/dict of fields to exclude
- `use_alias (bool)`: flag if aliases or field names should be used
**Returns**:
@ -183,7 +107,7 @@ exclusion, for nested models all related models are excluded.
```python
| @classmethod
| get_names_to_exclude(cls, fields: Optional[Union[Dict, Set]] = None, exclude_fields: Optional[Union[Dict, Set]] = None) -> Set
| get_names_to_exclude(cls, excludable: ExcludableItems, alias: str) -> Set
```
Returns a set of models field names that should be explicitly excluded
@ -197,8 +121,8 @@ them with dicts constructed from those db rows.
**Arguments**:
- `fields (Optional[Union[Set, Dict]])`: set/dict of fields to include
- `exclude_fields (Optional[Union[Set, Dict]])`: set/dict of fields to exclude
- `alias (str)`: alias of current relation
- `excludable (ExcludableItems)`: structure of fields to include and exclude
**Returns**:

View File

@ -40,12 +40,26 @@ List is cached in cls._related_fields for quicker access.
`(List)`: list of related fields
<a name="models.mixins.relation_mixin.RelationMixin.extract_through_names"></a>
#### extract\_through\_names
```python
| @classmethod
| extract_through_names(cls) -> Set
```
Extracts related fields through names which are shortcuts to through models.
**Returns**:
`(Set)`: set of related through fields names
<a name="models.mixins.relation_mixin.RelationMixin.extract_related_names"></a>
#### extract\_related\_names
```python
| @classmethod
| extract_related_names(cls) -> Set
| extract_related_names(cls) -> Set[str]
```
Returns List of fields names for all relations declared on a model.
@ -53,7 +67,7 @@ List is cached in cls._related_names for quicker access.
**Returns**:
`(List)`: list of related fields names
`(Set)`: set of related fields names
<a name="models.mixins.relation_mixin.RelationMixin._extract_db_related_names"></a>
#### \_extract\_db\_related\_names
@ -91,3 +105,24 @@ for nested models all related models are returned.
`(Set)`: set of non mandatory related fields
<a name="models.mixins.relation_mixin.RelationMixin._iterate_related_models"></a>
#### \_iterate\_related\_models
```python
| @classmethod
| _iterate_related_models(cls, visited: Set[Union[Type["Model"], Type["RelationMixin"]]] = None, source_relation: str = None, source_model: Union[Type["Model"], Type["RelationMixin"]] = None) -> List[str]
```
Iterates related models recursively to extract relation strings of
nested not visited models.
**Arguments**:
- `visited (Set[str])`: set of already visited models
- `source_relation (str)`: name of the current relation
- `source_model (Type["Model"])`: model from which relation comes in nested relations
**Returns**:
`(List[str])`: list of relation strings to be passed to select_related

View File

@ -91,3 +91,22 @@ passed by the user.
`(Dict)`: dictionary of model that is about to be saved
<a name="models.mixins.save_mixin.SavePrepareMixin.validate_choices"></a>
#### validate\_choices
```python
| @classmethod
| validate_choices(cls, new_kwargs: Dict) -> Dict
```
Receives dictionary of model that is about to be saved and validates the
fields with choices set to see if the value is allowed.
**Arguments**:
- `new_kwargs (Dict)`: dictionary of model that is about to be saved
**Returns**:
`(Dict)`: dictionary of model that is about to be saved

View File

@ -12,61 +12,6 @@ Class used for type hinting.
Users can subclass this one for convenience but it's not required.
The only requirement is that ormar.Model has to have inner class with name Meta.
<a name="models.metaclass.check_if_field_has_choices"></a>
#### check\_if\_field\_has\_choices
```python
check_if_field_has_choices(field: Type[BaseField]) -> bool
```
Checks if given field has choices populated.
A if it has one, a validator for this field needs to be attached.
**Arguments**:
- `field (BaseField)`: ormar field to check
**Returns**:
`(bool)`: result of the check
<a name="models.metaclass.choices_validator"></a>
#### choices\_validator
```python
choices_validator(cls: Type["Model"], values: Dict[str, Any]) -> Dict[str, Any]
```
Validator that is attached to pydantic model pre root validators.
Validator checks if field value is in field.choices list.
**Raises**:
- `ValueError`: if field value is outside of allowed choices.
**Arguments**:
- `cls (Model class)`: constructed class
- `values (Dict[str, Any])`: dictionary of field values (pydantic side)
**Returns**:
`(Dict[str, Any])`: values if pass validation, otherwise exception is raised
<a name="models.metaclass.populate_choices_validators"></a>
#### populate\_choices\_validators
```python
populate_choices_validators(model: Type["Model"]) -> None
```
Checks if Model has any fields with choices set.
If yes it adds choices validation into pre root validators.
**Arguments**:
- `model (Model class)`: newly constructed Model
<a name="models.metaclass.add_cached_properties"></a>
#### add\_cached\_properties
@ -87,26 +32,6 @@ All properties here are used as "cache" to not recalculate them constantly.
- `new_model (Model class)`: newly constructed Model
<a name="models.metaclass.meta_field_not_set"></a>
#### meta\_field\_not\_set
```python
meta_field_not_set(model: Type["Model"], field_name: str) -> bool
```
Checks if field with given name is already present in model.Meta.
Then check if it's set to something truthful
(in practice meaning not None, as it's non or ormar Field only).
**Arguments**:
- `model (Model class)`: newly constructed model
- `field_name (str)`: name of the ormar field
**Returns**:
`(bool)`: result of the check
<a name="models.metaclass.add_property_fields"></a>
#### add\_property\_fields
@ -141,24 +66,6 @@ Signals are emitted in both model own methods and in selected queryset ones.
- `new_model (Model class)`: newly constructed model
<a name="models.metaclass.update_attrs_and_fields"></a>
#### update\_attrs\_and\_fields
```python
update_attrs_and_fields(attrs: Dict, new_attrs: Dict, model_fields: Dict, new_model_fields: Dict, new_fields: Set) -> Dict
```
Updates __annotations__, values of model fields (so pydantic FieldInfos)
as well as model.Meta.model_fields definitions from parents.
**Arguments**:
- `attrs (Dict)`: new namespace for class being constructed
- `new_attrs (Dict)`: related of the namespace extracted from parent class
- `model_fields (Dict[str, BaseField])`: ormar fields in defined in current class
- `new_model_fields (Dict[str, BaseField])`: ormar fields defined in parent classes
- `new_fields (Set[str])`: set of new fields names
<a name="models.metaclass.verify_constraint_names"></a>
#### verify\_constraint\_names
@ -195,7 +102,7 @@ Updates Meta parameters in child from parent if needed.
#### copy\_and\_replace\_m2m\_through\_model
```python
copy_and_replace_m2m_through_model(field: Type[ManyToManyField], field_name: str, table_name: str, parent_fields: Dict, attrs: Dict, meta: ModelMeta) -> None
copy_and_replace_m2m_through_model(field: Type[ManyToManyField], field_name: str, table_name: str, parent_fields: Dict, attrs: Dict, meta: ModelMeta, base_class: Type["Model"]) -> None
```
Clones class with Through model for m2m relations, appends child name to the name
@ -211,6 +118,7 @@ Removes the original sqlalchemy table from metadata if it was not removed.
**Arguments**:
- `base_class (Type["Model"])`: base class model
- `field (Type[ManyToManyField])`: field with relations definition
- `field_name (str)`: name of the relation field
- `table_name (str)`: name of the table
@ -281,6 +189,24 @@ If the class is a ormar.Model it is skipped.
`(Tuple[Dict, Dict])`: updated attrs and model_fields
<a name="models.metaclass.update_attrs_and_fields"></a>
#### update\_attrs\_and\_fields
```python
update_attrs_and_fields(attrs: Dict, new_attrs: Dict, model_fields: Dict, new_model_fields: Dict, new_fields: Set) -> Dict
```
Updates __annotations__, values of model fields (so pydantic FieldInfos)
as well as model.Meta.model_fields definitions from parents.
**Arguments**:
- `attrs (Dict)`: new namespace for class being constructed
- `new_attrs (Dict)`: related of the namespace extracted from parent class
- `model_fields (Dict[str, BaseField])`: ormar fields in defined in current class
- `new_model_fields (Dict[str, BaseField])`: ormar fields defined in parent classes
- `new_fields (Set[str])`: set of new fields names
<a name="models.metaclass.ModelMetaclass"></a>
## ModelMetaclass Objects

View File

@ -0,0 +1,132 @@
<a name="models.model_row"></a>
# models.model\_row
<a name="models.model_row.ModelRow"></a>
## ModelRow Objects
```python
class ModelRow(NewBaseModel)
```
<a name="models.model_row.ModelRow.from_row"></a>
#### from\_row
```python
| @classmethod
| from_row(cls, row: sqlalchemy.engine.ResultProxy, source_model: Type["Model"], select_related: List = None, related_models: Any = None, related_field: Type["ForeignKeyField"] = None, excludable: ExcludableItems = None, current_relation_str: str = "", proxy_source_model: Optional[Type["Model"]] = None) -> Optional["Model"]
```
Model method to convert raw sql row from database into ormar.Model instance.
Traverses nested models if they were specified in select_related for query.
Called recurrently and returns model instance if it's present in the row.
Note that it's processing one row at a time, so if there are duplicates of
parent row that needs to be joined/combined
(like parent row in sql join with 2+ child rows)
instances populated in this method are later combined in the QuerySet.
Other method working directly on raw database results is in prefetch_query,
where rows are populated in a different way as they do not have
nested models in result.
**Arguments**:
- `proxy_source_model (Optional[Type["ModelRow"]])`: source model from which querysetproxy is constructed
- `excludable (ExcludableItems)`: structure of fields to include and exclude
- `current_relation_str (str)`: name of the relation field
- `source_model (Type[Model])`: model on which relation was defined
- `row (sqlalchemy.engine.result.ResultProxy)`: raw result row from the database
- `select_related (List)`: list of names of related models fetched from database
- `related_models (Union[List, Dict])`: list or dict of related models
- `related_field (Type[ForeignKeyField])`: field with relation declaration
**Returns**:
`(Optional[Model])`: returns model if model is populated from database
<a name="models.model_row.ModelRow._populate_nested_models_from_row"></a>
#### \_populate\_nested\_models\_from\_row
```python
| @classmethod
| _populate_nested_models_from_row(cls, item: dict, row: sqlalchemy.engine.ResultProxy, source_model: Type["Model"], related_models: Any, excludable: ExcludableItems, table_prefix: str, current_relation_str: str = None, proxy_source_model: Type["Model"] = None) -> dict
```
Traverses structure of related models and populates the nested models
from the database row.
Related models can be a list if only directly related models are to be
populated, converted to dict if related models also have their own related
models to be populated.
Recurrently calls from_row method on nested instances and create nested
instances. In the end those instances are added to the final model dictionary.
**Arguments**:
- `proxy_source_model (Optional[Type["ModelRow"]])`: source model from which querysetproxy is constructed
- `excludable (ExcludableItems)`: structure of fields to include and exclude
- `source_model (Type[Model])`: source model from which relation started
- `current_relation_str (str)`: joined related parts into one string
- `item (Dict)`: dictionary of already populated nested models, otherwise empty dict
- `row (sqlalchemy.engine.result.ResultProxy)`: raw result row from the database
- `related_models (Union[Dict, List])`: list or dict of related models
**Returns**:
`(Dict)`: dictionary with keys corresponding to model fields names
and values are database values
<a name="models.model_row.ModelRow.populate_through_instance"></a>
#### populate\_through\_instance
```python
| @classmethod
| populate_through_instance(cls, row: sqlalchemy.engine.ResultProxy, through_name: str, related: str, excludable: ExcludableItems) -> "ModelRow"
```
Initialize the through model from db row.
Excluded all relation fields and other exclude/include set in excludable.
**Arguments**:
- `row (sqlalchemy.engine.ResultProxy)`: loaded row from database
- `through_name (str)`: name of the through field
- `related (str)`: name of the relation
- `excludable (ExcludableItems)`: structure of fields to include and exclude
**Returns**:
`("ModelRow")`: initialized through model without relation
<a name="models.model_row.ModelRow.extract_prefixed_table_columns"></a>
#### extract\_prefixed\_table\_columns
```python
| @classmethod
| extract_prefixed_table_columns(cls, item: dict, row: sqlalchemy.engine.result.ResultProxy, table_prefix: str, excludable: ExcludableItems) -> Dict
```
Extracts own fields from raw sql result, using a given prefix.
Prefix changes depending on the table's position in a join.
If the table is a main table, there is no prefix.
All joined tables have prefixes to allow duplicate column names,
as well as duplicated joins to the same table from multiple different tables.
Extracted fields populates the related dict later used to construct a Model.
Used in Model.from_row and PrefetchQuery._populate_rows methods.
**Arguments**:
- `excludable (ExcludableItems)`: structure of fields to include and exclude
- `item (Dict)`: dictionary of already populated nested models, otherwise empty dict
- `row (sqlalchemy.engine.result.ResultProxy)`: raw result row from the database
- `table_prefix (str)`: prefix of the table from AliasManager
each pair of tables have own prefix (two of them depending on direction) -
used in joins to allow multiple joins to the same table.
**Returns**:
`(Dict)`: dictionary with keys corresponding to model fields names
and values are database values

View File

@ -5,122 +5,14 @@
## Model Objects
```python
class Model(NewBaseModel)
class Model(ModelRow)
```
<a name="models.model.Model.from_row"></a>
#### from\_row
```python
| @classmethod
| from_row(cls: Type[T], row: sqlalchemy.engine.ResultProxy, select_related: List = None, related_models: Any = None, previous_model: Type[T] = None, source_model: Type[T] = None, related_name: str = None, fields: Optional[Union[Dict, Set]] = None, exclude_fields: Optional[Union[Dict, Set]] = None, current_relation_str: str = None) -> Optional[T]
```
Model method to convert raw sql row from database into ormar.Model instance.
Traverses nested models if they were specified in select_related for query.
Called recurrently and returns model instance if it's present in the row.
Note that it's processing one row at a time, so if there are duplicates of
parent row that needs to be joined/combined
(like parent row in sql join with 2+ child rows)
instances populated in this method are later combined in the QuerySet.
Other method working directly on raw database results is in prefetch_query,
where rows are populated in a different way as they do not have
nested models in result.
**Arguments**:
- `current_relation_str (str)`: name of the relation field
- `source_model (Type[Model])`: model on which relation was defined
- `row (sqlalchemy.engine.result.ResultProxy)`: raw result row from the database
- `select_related (List)`: list of names of related models fetched from database
- `related_models (Union[List, Dict])`: list or dict of related models
- `previous_model (Model class)`: internal param for nested models to specify table_prefix
- `related_name (str)`: internal parameter - name of current nested model
- `fields (Optional[Union[Dict, Set]])`: fields and related model fields to include
if provided only those are included
- `exclude_fields (Optional[Union[Dict, Set]])`: fields and related model fields to exclude
excludes the fields even if they are provided in fields
**Returns**:
`(Optional[Model])`: returns model if model is populated from database
<a name="models.model.Model.populate_nested_models_from_row"></a>
#### populate\_nested\_models\_from\_row
```python
| @classmethod
| populate_nested_models_from_row(cls, item: dict, row: sqlalchemy.engine.ResultProxy, related_models: Any, fields: Optional[Union[Dict, Set]] = None, exclude_fields: Optional[Union[Dict, Set]] = None, current_relation_str: str = None, source_model: Type[T] = None) -> dict
```
Traverses structure of related models and populates the nested models
from the database row.
Related models can be a list if only directly related models are to be
populated, converted to dict if related models also have their own related
models to be populated.
Recurrently calls from_row method on nested instances and create nested
instances. In the end those instances are added to the final model dictionary.
**Arguments**:
- `source_model (Type[Model])`: source model from which relation started
- `current_relation_str (str)`: joined related parts into one string
- `item (Dict)`: dictionary of already populated nested models, otherwise empty dict
- `row (sqlalchemy.engine.result.ResultProxy)`: raw result row from the database
- `related_models (Union[Dict, List])`: list or dict of related models
- `fields (Optional[Union[Dict, Set]])`: fields and related model fields to include -
if provided only those are included
- `exclude_fields (Optional[Union[Dict, Set]])`: fields and related model fields to exclude
excludes the fields even if they are provided in fields
**Returns**:
`(Dict)`: dictionary with keys corresponding to model fields names
and values are database values
<a name="models.model.Model.extract_prefixed_table_columns"></a>
#### extract\_prefixed\_table\_columns
```python
| @classmethod
| extract_prefixed_table_columns(cls, item: dict, row: sqlalchemy.engine.result.ResultProxy, table_prefix: str, fields: Optional[Union[Dict, Set]] = None, exclude_fields: Optional[Union[Dict, Set]] = None) -> dict
```
Extracts own fields from raw sql result, using a given prefix.
Prefix changes depending on the table's position in a join.
If the table is a main table, there is no prefix.
All joined tables have prefixes to allow duplicate column names,
as well as duplicated joins to the same table from multiple different tables.
Extracted fields populates the related dict later used to construct a Model.
Used in Model.from_row and PrefetchQuery._populate_rows methods.
**Arguments**:
- `item (Dict)`: dictionary of already populated nested models, otherwise empty dict
- `row (sqlalchemy.engine.result.ResultProxy)`: raw result row from the database
- `table_prefix (str)`: prefix of the table from AliasManager
each pair of tables have own prefix (two of them depending on direction) -
used in joins to allow multiple joins to the same table.
- `fields (Optional[Union[Dict, Set]])`: fields and related model fields to include -
if provided only those are included
- `exclude_fields (Optional[Union[Dict, Set]])`: fields and related model fields to exclude
excludes the fields even if they are provided in fields
**Returns**:
`(Dict)`: dictionary with keys corresponding to model fields names
and values are database values
<a name="models.model.Model.upsert"></a>
#### upsert
```python
| async upsert(**kwargs: Any) -> T
| async upsert(**kwargs: Any) -> "Model"
```
Performs either a save or an update depending on the presence of the pk.
@ -139,7 +31,7 @@ For save kwargs are ignored, used only in update if provided.
#### save
```python
| async save() -> T
| async save() -> "Model"
```
Performs a save of given Model instance.
@ -203,7 +95,7 @@ number of updated instances
```python
| @staticmethod
| async _update_and_follow(rel: T, follow: bool, visited: Set, update_count: int) -> Tuple[int, Set]
| async _update_and_follow(rel: "Model", follow: bool, visited: Set, update_count: int) -> Tuple[int, Set]
```
Internal method used in save_related to follow related models and update numbers
@ -227,7 +119,7 @@ number of updated instances
#### update
```python
| async update(**kwargs: Any) -> T
| async update(**kwargs: Any) -> "Model"
```
Performs update of Model instance in the database.
@ -274,7 +166,7 @@ or update and the Model will be saved in database again.
#### load
```python
| async load() -> T
| async load() -> "Model"
```
Allow to refresh existing Models fields from database.
@ -289,3 +181,40 @@ Does NOT refresh the related models fields if they were loaded before.
`(Model)`: reloaded Model
<a name="models.model.Model.load_all"></a>
#### load\_all
```python
| async load_all(follow: bool = False, exclude: Union[List, str, Set, Dict] = None) -> "Model"
```
Allow to refresh existing Models fields from database.
Performs refresh of the related models fields.
By default loads only self and the directly related ones.
If follow=True is set it loads also related models of related models.
To not get stuck in an infinite loop as related models also keep a relation
to parent model visited models set is kept.
That way already visited models that are nested are loaded, but the load do not
follow them inside. So Model A -> Model B -> Model C -> Model A -> Model X
will load second Model A but will never follow into Model X.
Nested relations of those kind need to be loaded manually.
**Raises**:
- `NoMatch`: If given pk is not found in database.
**Arguments**:
- `exclude ()`:
- `follow (bool)`: flag to trigger deep save -
by default only directly related models are saved
with follow=True also related models of related models are saved
**Returns**:
`(Model)`: reloaded Model

View File

@ -146,7 +146,7 @@ Raises exception if model is abstract or has ForwardRefs in relation fields.
#### \_extract\_related\_model\_instead\_of\_field
```python
| _extract_related_model_instead_of_field(item: str) -> Optional[Union["T", Sequence["T"]]]
| _extract_related_model_instead_of_field(item: str) -> Optional[Union["Model", Sequence["Model"]]]
```
Retrieves the related model/models from RelationshipManager.
@ -276,7 +276,7 @@ cause some dialect require different treatment
#### remove
```python
| remove(parent: "T", name: str) -> None
| remove(parent: "Model", name: str) -> None
```
Removes child from relation with given name in RelationshipManager