Merge pull request #171 from collerek/fix_docs

fix isnull typo and formatting
This commit is contained in:
collerek
2021-04-22 19:01:36 +02:00
committed by GitHub
38 changed files with 1784 additions and 458 deletions

1
.gitignore vendored
View File

@ -1,6 +1,7 @@
p38venv
alembic
alembic.ini
build
.idea
.pytest_cache
.mypy_cache

View File

@ -20,8 +20,7 @@ to pydantic field types like ConstrainedStr
#### is\_valid\_uni\_relation
```python
| @classmethod
| is_valid_uni_relation(cls) -> bool
| is_valid_uni_relation() -> bool
```
Checks if field is a relation definition but only for ForeignKey relation,
@ -40,8 +39,7 @@ Model columns only.
#### get\_alias
```python
| @classmethod
| get_alias(cls) -> str
| get_alias() -> str
```
Used to translate Model column names to database column names during db queries.
@ -51,75 +49,26 @@ Used to translate Model column names to database column names during db queries.
`(str)`: returns custom database column name if defined by user,
otherwise field name in ormar/pydantic
<a name="fields.base.BaseField.is_valid_field_info_field"></a>
#### is\_valid\_field\_info\_field
<a name="fields.base.BaseField.get_pydantic_default"></a>
#### get\_pydantic\_default
```python
| @classmethod
| is_valid_field_info_field(cls, field_name: str) -> bool
```
Checks if field belongs to pydantic FieldInfo
- used during setting default pydantic values.
Excludes defaults and alias as they are populated separately
(defaults) or not at all (alias)
**Arguments**:
- `field_name (str)`: field name of BaseFIeld
**Returns**:
`(bool)`: True if field is present on pydantic.FieldInfo
<a name="fields.base.BaseField.get_base_pydantic_field_info"></a>
#### get\_base\_pydantic\_field\_info
```python
| @classmethod
| get_base_pydantic_field_info(cls, allow_null: bool) -> FieldInfo
| get_pydantic_default() -> Dict
```
Generates base pydantic.FieldInfo with only default and optionally
required to fix pydantic Json field being set to required=False.
Used in an ormar Model Metaclass.
**Arguments**:
- `allow_null (bool)`: flag if the default value can be None
or if it should be populated by pydantic Undefined
**Returns**:
`(pydantic.FieldInfo)`: instance of base pydantic.FieldInfo
<a name="fields.base.BaseField.convert_to_pydantic_field_info"></a>
#### convert\_to\_pydantic\_field\_info
```python
| @classmethod
| convert_to_pydantic_field_info(cls, allow_null: bool = False) -> FieldInfo
```
Converts a BaseField into pydantic.FieldInfo
that is later easily processed by pydantic.
Used in an ormar Model Metaclass.
**Arguments**:
- `allow_null (bool)`: flag if the default value can be None
or if it should be populated by pydantic Undefined
**Returns**:
`(pydantic.FieldInfo)`: actual instance of pydantic.FieldInfo with all needed fields populated
<a name="fields.base.BaseField.default_value"></a>
#### default\_value
```python
| @classmethod
| default_value(cls, use_server: bool = False) -> Optional[FieldInfo]
| default_value(use_server: bool = False) -> Optional[Dict]
```
Returns a FieldInfo instance with populated default
@ -145,8 +94,7 @@ which is returning a FieldInfo instance
#### get\_default
```python
| @classmethod
| get_default(cls, use_server: bool = False) -> Any
| get_default(use_server: bool = False) -> Any
```
Return default value for a field.
@ -166,8 +114,7 @@ treated as default value, default False
#### has\_default
```python
| @classmethod
| has_default(cls, use_server: bool = True) -> bool
| has_default(use_server: bool = True) -> bool
```
Checks if the field has default value set.
@ -185,8 +132,7 @@ treated as default value, default False
#### is\_auto\_primary\_key
```python
| @classmethod
| is_auto_primary_key(cls) -> bool
| is_auto_primary_key() -> bool
```
Checks if field is first a primary key and if it,
@ -201,8 +147,7 @@ Autoincrement primary_key is nullable/optional.
#### construct\_constraints
```python
| @classmethod
| construct_constraints(cls) -> List
| construct_constraints() -> List
```
Converts list of ormar constraints into sqlalchemy ForeignKeys.
@ -217,8 +162,7 @@ And we need a new ForeignKey for subclasses of current model
#### get\_column
```python
| @classmethod
| get_column(cls, name: str) -> sqlalchemy.Column
| get_column(name: str) -> sqlalchemy.Column
```
Returns definition of sqlalchemy.Column used in creation of sqlalchemy.Table.
@ -233,12 +177,28 @@ primary_key, index, unique, nullable, default and server_default.
`(sqlalchemy.Column)`: actual definition of the database column as sqlalchemy requires.
<a name="fields.base.BaseField._get_encrypted_column"></a>
#### \_get\_encrypted\_column
```python
| _get_encrypted_column(name: str) -> sqlalchemy.Column
```
Returns EncryptedString column type instead of actual column.
**Arguments**:
- `name (str)`: column name
**Returns**:
`(sqlalchemy.Column)`: newly defined column
<a name="fields.base.BaseField.expand_relationship"></a>
#### expand\_relationship
```python
| @classmethod
| expand_relationship(cls, value: Any, child: Union["Model", "NewBaseModel"], to_register: bool = True) -> Any
| expand_relationship(value: Any, child: Union["Model", "NewBaseModel"], to_register: bool = True) -> Any
```
Function overwritten for relations, in basic field the value is returned as is.
@ -261,8 +221,7 @@ dict (from Model) or actual instance/list of a "Model".
#### set\_self\_reference\_flag
```python
| @classmethod
| set_self_reference_flag(cls) -> None
| set_self_reference_flag() -> None
```
Sets `self_reference` to True if field to and owner are same model.
@ -275,8 +234,7 @@ Sets `self_reference` to True if field to and owner are same model.
#### has\_unresolved\_forward\_refs
```python
| @classmethod
| has_unresolved_forward_refs(cls) -> bool
| has_unresolved_forward_refs() -> bool
```
Verifies if the filed has any ForwardRefs that require updating before the
@ -290,8 +248,7 @@ model can be used.
#### evaluate\_forward\_ref
```python
| @classmethod
| evaluate_forward_ref(cls, globalns: Any, localns: Any) -> None
| evaluate_forward_ref(globalns: Any, localns: Any) -> None
```
Evaluates the ForwardRef to actual Field based on global and local namespaces
@ -309,8 +266,7 @@ Evaluates the ForwardRef to actual Field based on global and local namespaces
#### get\_related\_name
```python
| @classmethod
| get_related_name(cls) -> str
| get_related_name() -> str
```
Returns name to use for reverse relation.

View File

@ -5,7 +5,7 @@
#### create\_dummy\_instance
```python
create_dummy_instance(fk: Type["Model"], pk: Any = None) -> "Model"
create_dummy_instance(fk: Type["T"], pk: Any = None) -> "T"
```
Ormar never returns you a raw data.
@ -31,7 +31,7 @@ If the nested related Models are required they are set with -1 as pk value.
#### create\_dummy\_model
```python
create_dummy_model(base_model: Type["Model"], pk_field: Type[Union[BaseField, "ForeignKeyField", "ManyToManyField"]]) -> Type["BaseModel"]
create_dummy_model(base_model: Type["T"], pk_field: Union[BaseField, "ForeignKeyField", "ManyToManyField"]) -> Type["BaseModel"]
```
Used to construct a dummy pydantic model for type hints and pydantic validation.
@ -40,7 +40,7 @@ Populates only pk field and set it to desired type.
**Arguments**:
- `base_model (Model class)`: class of target dummy model
- `pk_field (Type[Union[BaseField, "ForeignKeyField", "ManyToManyField"]])`: ormar Field to be set on pydantic Model
- `pk_field (Union[BaseField, "ForeignKeyField", "ManyToManyField"])`: ormar Field to be set on pydantic Model
**Returns**:
@ -50,7 +50,7 @@ Populates only pk field and set it to desired type.
#### populate\_fk\_params\_based\_on\_to\_model
```python
populate_fk_params_based_on_to_model(to: Type["Model"], nullable: bool, onupdate: str = None, ondelete: str = None) -> Tuple[Any, List, Any]
populate_fk_params_based_on_to_model(to: Type["T"], nullable: bool, onupdate: str = None, ondelete: str = None) -> Tuple[Any, List, Any]
```
Based on target to model to which relation leads to populates the type of the
@ -69,6 +69,25 @@ How to treat child rows on delete of parent (the one where FK is defined) model.
`(Tuple[Any, List, Any])`: tuple with target pydantic type, list of fk constraints and target col type
<a name="fields.foreign_key.validate_not_allowed_fields"></a>
#### validate\_not\_allowed\_fields
```python
validate_not_allowed_fields(kwargs: Dict) -> None
```
Verifies if not allowed parameters are set on relation models.
Usually they are omitted later anyway but this way it's explicitly
notify the user that it's not allowed/ supported.
**Raises**:
- `ModelDefinitionError`: if any forbidden field is set
**Arguments**:
- `kwargs (Dict)`: dict of kwargs to verify passed to relation field
<a name="fields.foreign_key.UniqueColumns"></a>
## UniqueColumns Objects
@ -94,7 +113,7 @@ to produce sqlalchemy.ForeignKeys
#### ForeignKey
```python
ForeignKey(to: "ToType", *, name: str = None, unique: bool = False, nullable: bool = True, related_name: str = None, virtual: bool = False, onupdate: str = None, ondelete: str = None, **kwargs: Any, ,) -> Any
ForeignKey(to: "ToType", *, name: str = None, unique: bool = False, nullable: bool = True, related_name: str = None, virtual: bool = False, onupdate: str = None, ondelete: str = None, **kwargs: Any, ,) -> "T"
```
Despite a name it's a function that returns constructed ForeignKeyField.
@ -134,8 +153,7 @@ Actual class returned from ForeignKey function call and stored in model_fields.
#### get\_source\_related\_name
```python
| @classmethod
| get_source_related_name(cls) -> str
| get_source_related_name() -> str
```
Returns name to use for source relation name.
@ -150,8 +168,7 @@ It's either set as `related_name` or by default it's owner model. get_name + 's'
#### get\_related\_name
```python
| @classmethod
| get_related_name(cls) -> str
| get_related_name() -> str
```
Returns name to use for reverse relation.
@ -161,12 +178,37 @@ It's either set as `related_name` or by default it's owner model. get_name + 's'
`(str)`: name of the related_name or default related name.
<a name="fields.foreign_key.ForeignKeyField.default_target_field_name"></a>
#### default\_target\_field\_name
```python
| default_target_field_name() -> str
```
Returns default target model name on through model.
**Returns**:
`(str)`: name of the field
<a name="fields.foreign_key.ForeignKeyField.default_source_field_name"></a>
#### default\_source\_field\_name
```python
| default_source_field_name() -> str
```
Returns default target model name on through model.
**Returns**:
`(str)`: name of the field
<a name="fields.foreign_key.ForeignKeyField.evaluate_forward_ref"></a>
#### evaluate\_forward\_ref
```python
| @classmethod
| evaluate_forward_ref(cls, globalns: Any, localns: Any) -> None
| evaluate_forward_ref(globalns: Any, localns: Any) -> None
```
Evaluates the ForwardRef to actual Field based on global and local namespaces
@ -184,8 +226,7 @@ Evaluates the ForwardRef to actual Field based on global and local namespaces
#### \_extract\_model\_from\_sequence
```python
| @classmethod
| _extract_model_from_sequence(cls, value: List, child: "Model", to_register: bool) -> List["Model"]
| _extract_model_from_sequence(value: List, child: "Model", to_register: bool) -> List["Model"]
```
Takes a list of Models and registers them on parent.
@ -207,8 +248,7 @@ Used in reverse FK relations.
#### \_register\_existing\_model
```python
| @classmethod
| _register_existing_model(cls, value: "Model", child: "Model", to_register: bool) -> "Model"
| _register_existing_model(value: "Model", child: "Model", to_register: bool) -> "Model"
```
Takes already created instance and registers it for parent.
@ -230,8 +270,7 @@ Used in reverse FK relations and normal FK for single models.
#### \_construct\_model\_from\_dict
```python
| @classmethod
| _construct_model_from_dict(cls, value: dict, child: "Model", to_register: bool) -> "Model"
| _construct_model_from_dict(value: dict, child: "Model", to_register: bool) -> "Model"
```
Takes a dictionary, creates a instance and registers it for parent.
@ -254,8 +293,7 @@ Used in normal FK for dictionaries.
#### \_construct\_model\_from\_pk
```python
| @classmethod
| _construct_model_from_pk(cls, value: Any, child: "Model", to_register: bool) -> "Model"
| _construct_model_from_pk(value: Any, child: "Model", to_register: bool) -> "Model"
```
Takes a pk value, creates a dummy instance and registers it for parent.
@ -277,8 +315,7 @@ Used in normal FK for dictionaries.
#### register\_relation
```python
| @classmethod
| register_relation(cls, model: "Model", child: "Model") -> None
| register_relation(model: "Model", child: "Model") -> None
```
Registers relation between parent and child in relation manager.
@ -296,8 +333,7 @@ Used in Metaclass and sometimes some relations are missing
#### has\_unresolved\_forward\_refs
```python
| @classmethod
| has_unresolved_forward_refs(cls) -> bool
| has_unresolved_forward_refs() -> bool
```
Verifies if the filed has any ForwardRefs that require updating before the
@ -311,8 +347,7 @@ model can be used.
#### expand\_relationship
```python
| @classmethod
| expand_relationship(cls, value: Any, child: Union["Model", "NewBaseModel"], to_register: bool = True) -> Optional[Union["Model", List["Model"]]]
| expand_relationship(value: Any, child: Union["Model", "NewBaseModel"], to_register: bool = True) -> Optional[Union["Model", List["Model"]]]
```
For relations the child model is first constructed (if needed),
@ -336,8 +371,7 @@ Selects the appropriate constructor based on a passed value.
#### get\_relation\_name
```python
| @classmethod
| get_relation_name(cls) -> str
| get_relation_name() -> str
```
Returns name of the relation, which can be a own name or through model
@ -351,8 +385,7 @@ names for m2m models
#### get\_source\_model
```python
| @classmethod
| get_source_model(cls) -> Type["Model"]
| get_source_model() -> Type["Model"]
```
Returns model from which the relation comes -> either owner or through model

View File

@ -1,6 +1,19 @@
<a name="fields.many_to_many"></a>
# fields.many\_to\_many
<a name="fields.many_to_many.forbid_through_relations"></a>
#### forbid\_through\_relations
```python
forbid_through_relations(through: Type["Model"]) -> None
```
Verifies if the through model does not have relations.
**Arguments**:
- `through (Type['Model])`: through Model to be checked
<a name="fields.many_to_many.populate_m2m_params_based_on_to_model"></a>
#### populate\_m2m\_params\_based\_on\_to\_model
@ -24,7 +37,7 @@ pydantic field to use and type of the target column field.
#### ManyToMany
```python
ManyToMany(to: "ToType", through: Optional["ToType"] = None, *, name: str = None, unique: bool = False, virtual: bool = False, **kwargs: Any, ,) -> Any
ManyToMany(to: "ToType", through: Optional["ToType"] = None, *, name: str = None, unique: bool = False, virtual: bool = False, **kwargs: Any, ,) -> "RelationProxy[T]"
```
Despite a name it's a function that returns constructed ManyToManyField.
@ -60,8 +73,7 @@ Actual class returned from ManyToMany function call and stored in model_fields.
#### get\_source\_related\_name
```python
| @classmethod
| get_source_related_name(cls) -> str
| get_source_related_name() -> str
```
Returns name to use for source relation name.
@ -72,40 +84,11 @@ It's either set as `related_name` or by default it's field name.
`(str)`: name of the related_name or default related name.
<a name="fields.many_to_many.ManyToManyField.default_target_field_name"></a>
#### default\_target\_field\_name
```python
| @classmethod
| default_target_field_name(cls) -> str
```
Returns default target model name on through model.
**Returns**:
`(str)`: name of the field
<a name="fields.many_to_many.ManyToManyField.default_source_field_name"></a>
#### default\_source\_field\_name
```python
| @classmethod
| default_source_field_name(cls) -> str
```
Returns default target model name on through model.
**Returns**:
`(str)`: name of the field
<a name="fields.many_to_many.ManyToManyField.has_unresolved_forward_refs"></a>
#### has\_unresolved\_forward\_refs
```python
| @classmethod
| has_unresolved_forward_refs(cls) -> bool
| has_unresolved_forward_refs() -> bool
```
Verifies if the filed has any ForwardRefs that require updating before the
@ -119,8 +102,7 @@ model can be used.
#### evaluate\_forward\_ref
```python
| @classmethod
| evaluate_forward_ref(cls, globalns: Any, localns: Any) -> None
| evaluate_forward_ref(globalns: Any, localns: Any) -> None
```
Evaluates the ForwardRef to actual Field based on global and local namespaces
@ -138,8 +120,7 @@ Evaluates the ForwardRef to actual Field based on global and local namespaces
#### get\_relation\_name
```python
| @classmethod
| get_relation_name(cls) -> str
| get_relation_name() -> str
```
Returns name of the relation, which can be a own name or through model
@ -153,8 +134,7 @@ names for m2m models
#### get\_source\_model
```python
| @classmethod
| get_source_model(cls) -> Type["Model"]
| get_source_model() -> Type["Model"]
```
Returns model from which the relation comes -> either owner or through model
@ -167,8 +147,7 @@ Returns model from which the relation comes -> either owner or through model
#### create\_default\_through\_model
```python
| @classmethod
| create_default_through_model(cls) -> None
| create_default_through_model() -> None
```
Creates default empty through model if no additional fields are required.

View File

@ -5,7 +5,7 @@
#### is\_field\_an\_forward\_ref
```python
is_field_an_forward_ref(field: Type["BaseField"]) -> bool
is_field_an_forward_ref(field: "BaseField") -> bool
```
Checks if field is a relation field and whether any of the referenced models
@ -91,7 +91,7 @@ extraction of ormar model_fields.
#### group\_related\_list
```python
group_related_list(list_: List) -> Dict
group_related_list(list_: List) -> collections.OrderedDict
```
Translates the list of related strings into a dictionary.

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: "ManyToManyField") -> None
```
Registers pydantic field on through model that leads to passed model
@ -38,32 +38,6 @@ field_name. Returns a pydantic field with type of field_name field type.
`(pydantic.ModelField)`: newly created pydantic field
<a name="models.helpers.pydantic.populate_default_pydantic_field_value"></a>
#### populate\_default\_pydantic\_field\_value
```python
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
(so the default_value declared on ormar model if set)
and converts it to pydantic.FieldInfo
that pydantic is able to extract later.
On FieldInfo there are saved all needed params like max_length of the string
and other constraints that pydantic can use to build
it's own field validation used by ormar.
**Arguments**:
- `ormar_field (ormar Field)`: field to convert
- `field_name (str)`: field to convert name
- `attrs (Dict)`: current class namespace
**Returns**:
`(Dict)`: updated namespace dict
<a name="models.helpers.pydantic.populate_pydantic_default_values"></a>
#### populate\_pydantic\_default\_values
@ -76,7 +50,7 @@ dictionary of the class. Fields declared on model are all subclasses of the
BaseField class.
Trigger conversion of ormar field into pydantic FieldInfo, which has all needed
paramaters saved.
parameters saved.
Overwrites the annotations of ormar fields to corresponding types declared on
ormar fields (constructed dynamically for relations).

View File

@ -5,7 +5,7 @@
#### register\_relation\_on\_build
```python
register_relation_on_build(field: Type["ForeignKeyField"]) -> None
register_relation_on_build(field: "ForeignKeyField") -> None
```
Registers ForeignKey relation in alias_manager to set a table_prefix.
@ -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: "ManyToManyField") -> None
```
Registers connection between through model and both sides of the m2m relation.
@ -43,7 +43,7 @@ By default relation name is a model.name.lower().
#### expand\_reverse\_relationship
```python
expand_reverse_relationship(model_field: Type["ForeignKeyField"]) -> None
expand_reverse_relationship(model_field: "ForeignKeyField") -> None
```
If the reverse relation has not been set before it's set here.
@ -76,7 +76,7 @@ If the reverse relation has not been set before it's set here.
#### register\_reverse\_model\_fields
```python
register_reverse_model_fields(model_field: Type["ForeignKeyField"]) -> None
register_reverse_model_fields(model_field: "ForeignKeyField") -> None
```
Registers reverse ForeignKey field on related model.
@ -93,7 +93,7 @@ Autogenerated reverse fields also set related_name to the original field name.
#### register\_through\_shortcut\_fields
```python
register_through_shortcut_fields(model_field: Type["ManyToManyField"]) -> None
register_through_shortcut_fields(model_field: "ManyToManyField") -> None
```
Registers m2m relation through shortcut on both ends of the relation.
@ -106,7 +106,7 @@ Registers m2m relation through shortcut on both ends of the relation.
#### register\_relation\_in\_alias\_manager
```python
register_relation_in_alias_manager(field: Type["ForeignKeyField"]) -> None
register_relation_in_alias_manager(field: "ForeignKeyField") -> None
```
Registers the relation (and reverse relation) in alias manager.
@ -125,7 +125,7 @@ fk - register_relation_on_build
#### verify\_related\_name\_dont\_duplicate
```python
verify_related_name_dont_duplicate(related_name: str, model_field: Type["ForeignKeyField"]) -> None
verify_related_name_dont_duplicate(related_name: str, model_field: "ForeignKeyField") -> None
```
Verifies whether the used related_name (regardless of the fact if user defined or
@ -150,7 +150,7 @@ model
#### reverse\_field\_not\_already\_registered
```python
reverse_field_not_already_registered(model_field: Type["ForeignKeyField"]) -> bool
reverse_field_not_already_registered(model_field: "ForeignKeyField") -> bool
```
Checks if child is already registered in parents pydantic fields.

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: "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: "ManyToManyField", field_name: str) -> None
```
Registers sqlalchemy Column with sqlalchemy.ForeignKey leading to the model.
@ -98,6 +98,72 @@ or pkname validation fails.
`(Tuple[Optional[str], List[sqlalchemy.Column]])`: pkname, list of sqlalchemy columns
<a name="models.helpers.sqlalchemy._process_fields"></a>
#### \_process\_fields
```python
_process_fields(model_fields: Dict, new_model: Type["Model"]) -> Tuple[Optional[str], List[sqlalchemy.Column]]
```
Helper method.
Populates pkname and columns.
Trigger validation of primary_key - only one and required pk can be set,
cannot be pydantic_only.
Append fields to columns if it's not pydantic_only,
virtual ForeignKey or ManyToMany field.
Sets `owner` on each model_field as reference to newly created Model.
**Raises**:
- `ModelDefinitionError`: if validation of related_names fail,
or pkname validation fails.
**Arguments**:
- `model_fields (Dict[str, ormar.Field])`: dictionary of declared ormar model fields
- `new_model (Model class)`:
**Returns**:
`(Tuple[Optional[str], List[sqlalchemy.Column]])`: pkname, list of sqlalchemy columns
<a name="models.helpers.sqlalchemy._is_through_model_not_set"></a>
#### \_is\_through\_model\_not\_set
```python
_is_through_model_not_set(field: "BaseField") -> bool
```
Alias to if check that verifies if through model was created.
**Arguments**:
- `field ("BaseField")`: field to check
**Returns**:
`(bool)`: result of the check
<a name="models.helpers.sqlalchemy._is_db_field"></a>
#### \_is\_db\_field
```python
_is_db_field(field: "BaseField") -> bool
```
Alias to if check that verifies if field should be included in database.
**Arguments**:
- `field ("BaseField")`: field to check
**Returns**:
`(bool)`: result of the check
<a name="models.helpers.sqlalchemy.populate_meta_tablename_columns_and_pk"></a>
#### populate\_meta\_tablename\_columns\_and\_pk
@ -165,7 +231,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: "ForeignKeyField") -> None
```
Updates a column with a new type column based on updated parameters in FK fields.
@ -173,7 +239,7 @@ Updates a column with a new type column based on updated parameters in FK fields
**Arguments**:
- `model (Type["Model"])`: model on which columns needs to be updated
- `field (Type[ForeignKeyField])`: field with column definition that requires update
- `field (ForeignKeyField)`: field with column definition that requires update
**Returns**:

View File

@ -5,7 +5,7 @@
#### check\_if\_field\_has\_choices
```python
check_if_field_has_choices(field: Type[BaseField]) -> bool
check_if_field_has_choices(field: BaseField) -> bool
```
Checks if given field has choices populated.
@ -23,7 +23,7 @@ A if it has one, a validator for this field needs to be attached.
#### convert\_choices\_if\_needed
```python
convert_choices_if_needed(field: Type["BaseField"], value: Any) -> Tuple[Any, List]
convert_choices_if_needed(field: "BaseField", value: Any) -> Tuple[Any, List]
```
Converts dates to isoformat as fastapi can check this condition in routes
@ -37,7 +37,7 @@ Converts decimal to float with given scale.
**Arguments**:
- `field (Type[BaseField])`: ormar field to check with choices
- `field (BaseField)`: ormar field to check with choices
- `values (Dict)`: current values of the model to verify
**Returns**:
@ -48,7 +48,7 @@ Converts decimal to float with given scale.
#### validate\_choices
```python
validate_choices(field: Type["BaseField"], value: Any) -> None
validate_choices(field: "BaseField", value: Any) -> None
```
Validates if given value is in provided choices.
@ -59,7 +59,7 @@ Validates if given value is in provided choices.
**Arguments**:
- `field (Type[BaseField])`: field to validate
- `field (BaseField)`: field to validate
- `value (Any)`: value of the field
<a name="models.helpers.validation.choices_validator"></a>

View File

@ -78,12 +78,12 @@ Primary key field is always added and cannot be excluded (will be added anyway).
`(List[str])`: list of column field names or aliases
<a name="models.mixins.excludable_mixin.ExcludableMixin._update_excluded_with_related_not_required"></a>
#### \_update\_excluded\_with\_related\_not\_required
<a name="models.mixins.excludable_mixin.ExcludableMixin._update_excluded_with_related"></a>
#### \_update\_excluded\_with\_related
```python
| @classmethod
| _update_excluded_with_related_not_required(cls, exclude: Union["AbstractSetIntStr", "MappingIntStrAny", None], nested: bool = False) -> Union[Set, Dict]
| _update_excluded_with_related(cls, exclude: Union[Set, Dict, None]) -> Set
```
Used during generation of the dict().
@ -96,7 +96,6 @@ exclusion, for nested models all related models are excluded.
**Arguments**:
- `exclude (Union[Set, Dict, None])`: set/dict with fields to exclude
- `nested (bool)`: flag setting nested models (child of previous one, not main one)
**Returns**:

View File

@ -19,7 +19,7 @@ in the end all parent (main) models should be unique.
```python
| @classmethod
| merge_instances_list(cls, result_rows: Sequence["Model"]) -> Sequence["Model"]
| merge_instances_list(cls, result_rows: List["Model"]) -> List["Model"]
```
Merges a list of models into list of unique models.
@ -41,7 +41,7 @@ populated, each instance is one row in db and some models can duplicate
```python
| @classmethod
| merge_two_instances(cls, one: "Model", other: "Model") -> "Model"
| merge_two_instances(cls, one: "Model", other: "Model", relation_map: Dict = None) -> "Model"
```
Merges current (other) Model and previous one (one) and returns the current
@ -51,6 +51,7 @@ If needed it's calling itself recurrently and merges also children models.
**Arguments**:
- `relation_map (Dict)`: map of models relations to follow
- `one (Model)`: previous model instance
- `other (Model)`: current model instance
@ -58,3 +59,30 @@ If needed it's calling itself recurrently and merges also children models.
`(Model)`: current Model instance with data merged from previous one.
<a name="models.mixins.merge_mixin.MergeModelMixin._merge_items_lists"></a>
#### \_merge\_items\_lists
```python
| @classmethod
| _merge_items_lists(cls, field_name: str, current_field: List, other_value: List, relation_map: Optional[Dict]) -> List
```
Takes two list of nested models and process them going deeper
according with the map.
If model from one's list is in other -> they are merged with relations
to follow passed from map.
If one's model is not in other it's simply appended to the list.
**Arguments**:
- `field_name (str)`: name of the current relation field
- `current_field (List[Model])`: list of nested models from one model
- `other_value (List[Model])`: list of nested models from other model
- `relation_map (Dict)`: map of relations to follow
**Returns**:
`(List[Model])`: merged list of models

View File

@ -59,7 +59,7 @@ or field name specified by related parameter.
```python
| @classmethod
| get_related_field_name(cls, target_field: Type["ForeignKeyField"]) -> str
| get_related_field_name(cls, target_field: "ForeignKeyField") -> str
```
Returns name of the relation field that should be used in prefetch query.

View File

@ -30,7 +30,7 @@ related fields.
```python
| @classmethod
| extract_related_fields(cls) -> List
| extract_related_fields(cls) -> List["ForeignKeyField"]
```
Returns List of ormar Fields for all relations declared on a model.
@ -45,7 +45,7 @@ List is cached in cls._related_fields for quicker access.
```python
| @classmethod
| extract_through_names(cls) -> Set
| extract_through_names(cls) -> Set[str]
```
Extracts related fields through names which are shortcuts to through models.
@ -84,43 +84,35 @@ related fields that are not stored as foreign keys on given model.
`(Set)`: set of model fields with non fk relation fields excluded
<a name="models.mixins.relation_mixin.RelationMixin._exclude_related_names_not_required"></a>
#### \_exclude\_related\_names\_not\_required
```python
| @classmethod
| _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.
**Arguments**:
- `nested (bool)`: flag setting nested models (child of previous one, not main one)
**Returns**:
`(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]
| _iterate_related_models(cls, node_list: NodeList = None, source_relation: str = None) -> List[str]
```
Iterates related models recursively to extract relation strings of
nested not visited models.
**Returns**:
`(List[str])`: list of relation strings to be passed to select_related
<a name="models.mixins.relation_mixin.RelationMixin._get_final_relations"></a>
#### \_get\_final\_relations
```python
| @staticmethod
| _get_final_relations(processed_relations: List, source_relation: Optional[str]) -> List[str]
```
Helper method to prefix nested relation strings with current source relation
**Arguments**:
- `visited (Set[str])`: set of already visited models
- `processed_relations (List[str])`: list of already processed relation str
- `source_relation (str)`: name of the current relation
- `source_model (Type["Model"])`: model from which relation comes in nested relations
**Returns**:

View File

@ -33,6 +33,25 @@ Translate columns into aliases (db names).
`(Dict[str, str])`: dictionary of model that is about to be saved
<a name="models.mixins.save_mixin.SavePrepareMixin._remove_not_ormar_fields"></a>
#### \_remove\_not\_ormar\_fields
```python
| @classmethod
| _remove_not_ormar_fields(cls, new_kwargs: dict) -> dict
```
Removes primary key for if it's nullable or autoincrement pk field,
and it's set to None.
**Arguments**:
- `new_kwargs (Dict[str, str])`: dictionary of model that is about to be saved
**Returns**:
`(Dict[str, str])`: dictionary of model that is about to be saved
<a name="models.mixins.save_mixin.SavePrepareMixin._remove_pk_from_kwargs"></a>
#### \_remove\_pk\_from\_kwargs
@ -52,6 +71,25 @@ and it's set to None.
`(Dict[str, str])`: dictionary of model that is about to be saved
<a name="models.mixins.save_mixin.SavePrepareMixin.parse_non_db_fields"></a>
#### parse\_non\_db\_fields
```python
| @classmethod
| parse_non_db_fields(cls, model_dict: Dict) -> Dict
```
Receives dictionary of model that is about to be saved and changes uuid fields
to strings in bulk_update.
**Arguments**:
- `model_dict (Dict)`: dictionary of model that is about to be saved
**Returns**:
`(Dict)`: dictionary of model that is about to be saved
<a name="models.mixins.save_mixin.SavePrepareMixin.substitute_models_with_pks"></a>
#### substitute\_models\_with\_pks
@ -110,3 +148,73 @@ fields with choices set to see if the value is allowed.
`(Dict)`: dictionary of model that is about to be saved
<a name="models.mixins.save_mixin.SavePrepareMixin._upsert_model"></a>
#### \_upsert\_model
```python
| @staticmethod
| async _upsert_model(instance: "Model", save_all: bool, previous_model: Optional["Model"], relation_field: Optional["ForeignKeyField"], update_count: int) -> int
```
Method updates given instance if:
* instance is not saved or
* instance have no pk or
* save_all=True flag is set
and instance is not __pk_only__.
If relation leading to instance is a ManyToMany also the through model is saved
**Arguments**:
- `instance (Model)`: current model to upsert
- `save_all (bool)`: flag if all models should be saved or only not saved ones
- `relation_field (Optional[ForeignKeyField])`: field with relation
- `previous_model (Model)`: previous model from which method came
- `update_count (int)`: no of updated models
**Returns**:
`(int)`: no of updated models
<a name="models.mixins.save_mixin.SavePrepareMixin._upsert_through_model"></a>
#### \_upsert\_through\_model
```python
| @staticmethod
| async _upsert_through_model(instance: "Model", previous_model: "Model", relation_field: "ForeignKeyField") -> None
```
Upsert through model for m2m relation.
**Arguments**:
- `instance (Model)`: current model to upsert
- `relation_field (Optional[ForeignKeyField])`: field with relation
- `previous_model (Model)`: previous model from which method came
<a name="models.mixins.save_mixin.SavePrepareMixin._update_relation_list"></a>
#### \_update\_relation\_list
```python
| async _update_relation_list(fields_list: Collection["ForeignKeyField"], follow: bool, save_all: bool, relation_map: Dict, update_count: int) -> int
```
Internal method used in save_related to follow deeper from
related models and update numbers of updated related instances.
**Arguments**:
- `fields_list (Collection["ForeignKeyField"])`: list of ormar fields to follow and save
- `relation_map (Dict)`: map of relations to follow
- `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
- `update_count (int)`: internal parameter for recursive calls -
number of updated instances
**Returns**:
`(int)`: tuple of update count and visited

View File

@ -102,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, base_class: Type["Model"]) -> None
copy_and_replace_m2m_through_model(field: 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
@ -119,7 +119,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 (ManyToManyField)`: field with relations definition
- `field_name (str)`: name of the relation field
- `table_name (str)`: name of the table
- `parent_fields (Dict)`: dictionary of fields to copy to new models from parent
@ -130,9 +130,7 @@ Removes the original sqlalchemy table from metadata if it was not removed.
#### copy\_data\_from\_parent\_model
```python
copy_data_from_parent_model(base_class: Type["Model"], curr_class: type, attrs: Dict, model_fields: Dict[
str, Union[Type[BaseField], Type[ForeignKeyField], Type[ManyToManyField]]
]) -> Tuple[Dict, Dict]
copy_data_from_parent_model(base_class: Type["Model"], curr_class: type, attrs: Dict, model_fields: Dict[str, Union[BaseField, ForeignKeyField, ManyToManyField]]) -> Tuple[Dict, Dict]
```
Copy the key parameters [databse, metadata, property_fields and constraints]
@ -162,9 +160,7 @@ Since relation fields requires different related_name for different children
#### extract\_from\_parents\_definition
```python
extract_from_parents_definition(base_class: type, curr_class: type, attrs: Dict, model_fields: Dict[
str, Union[Type[BaseField], Type[ForeignKeyField], Type[ManyToManyField]]
]) -> Tuple[Dict, Dict]
extract_from_parents_definition(base_class: type, curr_class: type, attrs: Dict, model_fields: Dict[str, Union[BaseField, ForeignKeyField, ManyToManyField]]) -> Tuple[Dict, Dict]
```
Extracts fields from base classes if they have valid oramr fields.

View File

@ -13,7 +13,7 @@ class ModelRow(NewBaseModel)
```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"]
| from_row(cls, row: sqlalchemy.engine.ResultProxy, source_model: Type["Model"], select_related: List = None, related_models: Any = None, related_field: "ForeignKeyField" = None, excludable: ExcludableItems = None, current_relation_str: str = "", proxy_source_model: Optional[Type["Model"]] = None, used_prefixes: List[str] = None) -> Optional["Model"]
```
Model method to convert raw sql row from database into ormar.Model instance.
@ -30,6 +30,7 @@ nested models in result.
**Arguments**:
- `used_prefixes (List[str])`: list of already extracted prefixes
- `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
@ -37,18 +38,37 @@ nested models in result.
- `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
- `related_field (ForeignKeyField)`: field with relation declaration
**Returns**:
`(Optional[Model])`: returns model if model is populated from database
<a name="models.model_row.ModelRow._process_table_prefix"></a>
#### \_process\_table\_prefix
```python
| @classmethod
| _process_table_prefix(cls, source_model: Type["Model"], current_relation_str: str, related_field: "ForeignKeyField", used_prefixes: List[str]) -> str
```
**Arguments**:
- `source_model (Type[Model])`: model on which relation was defined
- `current_relation_str (str)`: current relation string
- `related_field ("ForeignKeyField")`: field with relation declaration
- `used_prefixes (List[str])`: list of already extracted prefixes
**Returns**:
`(str)`: table_prefix to use
<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
| _populate_nested_models_from_row(cls, item: dict, row: sqlalchemy.engine.ResultProxy, source_model: Type["Model"], related_models: Any, excludable: ExcludableItems, table_prefix: str, used_prefixes: List[str], current_relation_str: str = None, proxy_source_model: Type["Model"] = None) -> dict
```
Traverses structure of related models and populates the nested models
@ -75,12 +95,48 @@ instances. In the end those instances are added to the final model dictionary.
`(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
<a name="models.model_row.ModelRow._process_remainder_and_relation_string"></a>
#### \_process\_remainder\_and\_relation\_string
```python
| @staticmethod
| _process_remainder_and_relation_string(related_models: Union[Dict, List], current_relation_str: Optional[str], related: str) -> Tuple[str, Optional[Union[Dict, List]]]
```
Process remainder models and relation string
**Arguments**:
- `related_models (Union[Dict, List])`: list or dict of related models
- `current_relation_str (Optional[str])`: current relation string
- `related (str)`: name of the relation
<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"
| _populate_through_instance(cls, row: sqlalchemy.engine.ResultProxy, item: Dict, related: str, excludable: ExcludableItems, child: "Model", proxy_source_model: Optional[Type["Model"]]) -> None
```
Populates the through model on reverse side of current query.
Normally it's child class, unless the query is from queryset.
**Arguments**:
- `row (sqlalchemy.engine.ResultProxy)`: row from db result
- `item (Dict)`: parent item dict
- `related (str)`: current relation name
- `excludable (ExcludableItems)`: structure of fields to include and exclude
- `child ("Model")`: child item of parent
- `proxy_source_model (Type["Model"])`: source model from which querysetproxy is constructed
<a name="models.model_row.ModelRow._create_through_instance"></a>
#### \_create\_through\_instance
```python
| @classmethod
| _create_through_instance(cls, row: sqlalchemy.engine.ResultProxy, through_name: str, related: str, excludable: ExcludableItems) -> "ModelRow"
```
Initialize the through model from db row.

View File

@ -12,7 +12,7 @@ class Model(ModelRow)
#### upsert
```python
| async upsert(**kwargs: Any) -> "Model"
| async upsert(**kwargs: Any) -> T
```
Performs either a save or an update depending on the presence of the pk.
@ -31,7 +31,7 @@ For save kwargs are ignored, used only in update if provided.
#### save
```python
| async save() -> "Model"
| async save() -> T
```
Performs a save of given Model instance.
@ -60,7 +60,7 @@ Sets model save status to True.
#### save\_related
```python
| async save_related(follow: bool = False, visited: Set = None, update_count: int = 0) -> int
| async save_related(follow: bool = False, save_all: bool = False, relation_map: Dict = None, exclude: Union[Set, Dict] = None, update_count: int = 0, previous_model: "Model" = None, relation_field: Optional["ForeignKeyField"] = None) -> int
```
Triggers a upsert method on all related models
@ -79,10 +79,14 @@ Nested relations of those kind need to be persisted manually.
**Arguments**:
- `relation_field (Optional[ForeignKeyField])`: field with relation leading to this model
- `previous_model (Model)`: previous model from which method came
- `exclude (Union[Set, Dict])`: items to exclude during saving of relations
- `relation_map (Dict)`: map of relations to follow
- `save_all (bool)`: flag if all models should be saved or only not saved ones
- `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
- `visited (Set)`: internal parameter for recursive calls - already visited models
- `update_count (int)`: internal parameter for recursive calls -
number of updated instances
@ -90,36 +94,11 @@ number of updated instances
`(int)`: number of updated/saved models
<a name="models.model.Model._update_and_follow"></a>
#### \_update\_and\_follow
```python
| @staticmethod
| 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
of updated related instances.
**Arguments**:
- `rel (Model)`: Model to follow
- `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
- `visited (Set)`: internal parameter for recursive calls - already visited models
- `update_count (int)`: internal parameter for recursive calls -
number of updated instances
**Returns**:
`(Tuple[int, Set])`: tuple of update count and visited
<a name="models.model.Model.update"></a>
#### update
```python
| async update(**kwargs: Any) -> "Model"
| async update(_columns: List[str] = None, **kwargs: Any) -> T
```
Performs update of Model instance in the database.
@ -129,14 +108,15 @@ Sends pre_update and post_update signals.
Sets model save status to True.
**Arguments**:
- `_columns (List)`: list of columns to update, if None all are updated
- `kwargs (Any)`: list of fields to update as field=value pairs
**Raises**:
- `ModelPersistenceError`: If the pk column is not set
**Arguments**:
- `kwargs (Any)`: list of fields to update as field=value pairs
**Returns**:
`(Model)`: updated Model
@ -166,7 +146,7 @@ or update and the Model will be saved in database again.
#### load
```python
| async load() -> "Model"
| async load() -> T
```
Allow to refresh existing Models fields from database.
@ -185,7 +165,7 @@ Does NOT refresh the related models fields if they were loaded before.
#### load\_all
```python
| async load_all(follow: bool = False, exclude: Union[List, str, Set, Dict] = None) -> "Model"
| async load_all(follow: bool = False, exclude: Union[List, str, Set, Dict] = None, order_by: Union[List, str] = None) -> T
```
Allow to refresh existing Models fields from database.
@ -203,17 +183,18 @@ 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 ()`:
- `order_by (Union[List, str])`: columns by which models should be sorted
- `exclude (Union[List, str, Set, Dict])`: related models to 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
**Raises**:
- `NoMatch`: If given pk is not found in database.
**Returns**:
`(Model)`: reloaded Model

View File

@ -364,7 +364,7 @@ Returns related field names applying on them include and exclude set.
```python
| @staticmethod
| _extract_nested_models_from_list(models: MutableSequence, include: Union[Set, Dict, None], exclude: Union[Set, Dict, None]) -> List
| _extract_nested_models_from_list(relation_map: Dict, models: MutableSequence, include: Union[Set, Dict, None], exclude: Union[Set, Dict, None]) -> List
```
Converts list of models into list of dictionaries.
@ -383,7 +383,7 @@ Converts list of models into list of dictionaries.
#### \_skip\_ellipsis
```python
| _skip_ellipsis(items: Union[Set, Dict, None], key: str) -> Union[Set, Dict, None]
| _skip_ellipsis(items: Union[Set, Dict, None], key: str, default_return: Any = None) -> Union[Set, Dict, None]
```
Helper to traverse the include/exclude dictionaries.
@ -399,11 +399,25 @@ and not the actual set/dict with fields names.
`(Union[Set, Dict, None])`: nested value of the items
<a name="models.newbasemodel.NewBaseModel._convert_all"></a>
#### \_convert\_all
```python
| _convert_all(items: Union[Set, Dict, None]) -> Union[Set, Dict, None]
```
Helper to convert __all__ pydantic special index to ormar which does not
support index based exclusions.
**Arguments**:
- `items (Union[Set, Dict, None])`: current include/exclude value
<a name="models.newbasemodel.NewBaseModel._extract_nested_models"></a>
#### \_extract\_nested\_models
```python
| _extract_nested_models(nested: bool, dict_instance: Dict, include: Optional[Dict], exclude: Optional[Dict]) -> Dict
| _extract_nested_models(relation_map: Dict, dict_instance: Dict, include: Optional[Dict], exclude: Optional[Dict]) -> Dict
```
Traverse nested models and converts them into dictionaries.
@ -424,7 +438,7 @@ Calls itself recursively if needed.
#### dict
```python
| dict(*, include: Union[Set, Dict] = None, exclude: Union[Set, Dict] = None, by_alias: bool = False, skip_defaults: bool = None, exclude_unset: bool = False, exclude_defaults: bool = False, exclude_none: bool = False, nested: bool = False) -> "DictStrAny"
| dict(*, include: Union[Set, Dict] = None, exclude: Union[Set, Dict] = None, by_alias: bool = False, skip_defaults: bool = None, exclude_unset: bool = False, exclude_defaults: bool = False, exclude_none: bool = False, relation_map: Dict = None) -> "DictStrAny"
```
Generate a dictionary representation of the model,
@ -443,7 +457,7 @@ Additionally fields decorated with @property_field are also added.
- `exclude_unset (bool)`: flag to exclude not set values - passed to pydantic
- `exclude_defaults (bool)`: flag to exclude default values - passed to pydantic
- `exclude_none (bool)`: flag to exclude None values - passed to pydantic
- `nested (bool)`: flag if the current model is nested
- `relation_map (Dict)`: map of the relations to follow to avoid circural deps
**Returns**:
@ -536,14 +550,14 @@ That includes own non-relational fields ang foreign key fields.
#### get\_relation\_model\_id
```python
| get_relation_model_id(target_field: Type["BaseField"]) -> Optional[int]
| get_relation_model_id(target_field: "BaseField") -> Optional[int]
```
Returns an id of the relation side model to use in prefetch query.
**Arguments**:
- `target_field (Type["BaseField"])`: field with relation definition
- `target_field ("BaseField")`: field with relation definition
**Returns**:

View File

@ -0,0 +1,78 @@
<a name="models.traversible"></a>
# models.traversible
<a name="models.traversible.NodeList"></a>
## NodeList Objects
```python
class NodeList()
```
Helper class that helps with iterating nested models
<a name="models.traversible.NodeList.add"></a>
#### add
```python
| add(node_class: Type["RelationMixin"], relation_name: str = None, parent_node: "Node" = None) -> "Node"
```
Adds new Node or returns the existing one
**Arguments**:
- `node_class (ormar.models.metaclass.ModelMetaclass)`: Model in current node
- `relation_name (str)`: name of the current relation
- `parent_node (Optional[Node])`: parent node
**Returns**:
`(Node)`: returns new or already existing node
<a name="models.traversible.NodeList.find"></a>
#### find
```python
| find(node_class: Type["RelationMixin"], relation_name: Optional[str] = None, parent_node: "Node" = None) -> Optional["Node"]
```
Searches for existing node with given parameters
**Arguments**:
- `node_class (ormar.models.metaclass.ModelMetaclass)`: Model in current node
- `relation_name (str)`: name of the current relation
- `parent_node (Optional[Node])`: parent node
**Returns**:
`(Optional[Node])`: returns already existing node or None
<a name="models.traversible.Node"></a>
## Node Objects
```python
class Node()
```
<a name="models.traversible.Node.visited"></a>
#### visited
```python
| visited(relation_name: str) -> bool
```
Checks if given relation was already visited.
Relation was visited if it's name is in current node children.
Relation was visited if one of the parent node had the same Model class
**Arguments**:
- `relation_name (str)`: name of relation
**Returns**:
`(bool)`: result of the check

View File

@ -1,6 +1,115 @@
<a name="queryset.clause"></a>
# queryset.clause
<a name="queryset.clause.FilterGroup"></a>
## FilterGroup Objects
```python
class FilterGroup()
```
Filter groups are used in complex queries condition to group and and or
clauses in where condition
<a name="queryset.clause.FilterGroup.resolve"></a>
#### resolve
```python
| resolve(model_cls: Type["Model"], select_related: List = None, filter_clauses: List = None) -> Tuple[List[FilterAction], List[str]]
```
Resolves the FilterGroups actions to use proper target model, replace
complex relation prefixes if needed and nested groups also resolved.
**Arguments**:
- `model_cls (Type["Model"])`: model from which the query is run
- `select_related (List[str])`: list of models to join
- `filter_clauses (List[FilterAction])`: list of filter conditions
**Returns**:
`(Tuple[List[FilterAction], List[str]])`: list of filter conditions and select_related list
<a name="queryset.clause.FilterGroup._iter"></a>
#### \_iter
```python
| _iter() -> Generator
```
Iterates all actions in a tree
**Returns**:
`(Generator)`: generator yielding from own actions and nested groups
<a name="queryset.clause.FilterGroup._get_text_clauses"></a>
#### \_get\_text\_clauses
```python
| _get_text_clauses() -> List[sqlalchemy.sql.expression.TextClause]
```
Helper to return list of text queries from actions and nested groups
**Returns**:
`(List[sqlalchemy.sql.elements.TextClause])`: list of text queries from actions and nested groups
<a name="queryset.clause.FilterGroup.get_text_clause"></a>
#### get\_text\_clause
```python
| get_text_clause() -> sqlalchemy.sql.expression.TextClause
```
Returns all own actions and nested groups conditions compiled and joined
inside parentheses.
Escapes characters if it's required.
Substitutes values of the models if value is a ormar Model with its pk value.
Compiles the clause.
**Returns**:
`(sqlalchemy.sql.elements.TextClause)`: complied and escaped clause
<a name="queryset.clause.or_"></a>
#### or\_
```python
or_(*args: FilterGroup, **kwargs: Any) -> FilterGroup
```
Construct or filter from nested groups and keyword arguments
**Arguments**:
- `args (Tuple[FilterGroup])`: nested filter groups
- `kwargs (Any)`: fields names and proper value types
**Returns**:
`(ormar.queryset.clause.FilterGroup)`: FilterGroup ready to be resolved
<a name="queryset.clause.and_"></a>
#### and\_
```python
and_(*args: FilterGroup, **kwargs: Any) -> FilterGroup
```
Construct and filter from nested groups and keyword arguments
**Arguments**:
- `args (Tuple[FilterGroup])`: nested filter groups
- `kwargs (Any)`: fields names and proper value types
**Returns**:
`(ormar.queryset.clause.FilterGroup)`: FilterGroup ready to be resolved
<a name="queryset.clause.QueryClause"></a>
## QueryClause Objects
@ -14,7 +123,7 @@ Constructs FilterActions from strings passed as arguments
#### prepare\_filter
```python
| prepare_filter(**kwargs: Any) -> Tuple[List[FilterAction], List[str]]
| prepare_filter(_own_only: bool = False, **kwargs: Any) -> Tuple[List[FilterAction], List[str]]
```
Main external access point that processes the clauses into sqlalchemy text
@ -23,6 +132,7 @@ mentioned in select_related strings but not included in select_related.
**Arguments**:
- `_own_only ()`:
- `kwargs (Any)`: key, value pair with column names and values
**Returns**:
@ -33,7 +143,7 @@ mentioned in select_related strings but not included in select_related.
#### \_populate\_filter\_clauses
```python
| _populate_filter_clauses(**kwargs: Any) -> Tuple[List[FilterAction], List[str]]
| _populate_filter_clauses(_own_only: bool, **kwargs: Any) -> Tuple[List[FilterAction], List[str]]
```
Iterates all clauses and extracts used operator and field from related
@ -104,3 +214,16 @@ present in alias_manager.
`(List[FilterAction])`: list of actions with aliases changed if needed
<a name="queryset.clause.QueryClause._verify_prefix_and_switch"></a>
#### \_verify\_prefix\_and\_switch
```python
| _verify_prefix_and_switch(action: "FilterAction") -> None
```
Helper to switch prefix to complex relation one if required
**Arguments**:
- `action (ormar.queryset.actions.filter_action.FilterAction)`: action to switch prefix in

View File

@ -0,0 +1,359 @@
<a name="queryset.field_accessor"></a>
# queryset.field\_accessor
<a name="queryset.field_accessor.FieldAccessor"></a>
## FieldAccessor Objects
```python
class FieldAccessor()
```
Helper to access ormar fields directly from Model class also for nested
models attributes.
<a name="queryset.field_accessor.FieldAccessor.__bool__"></a>
#### \_\_bool\_\_
```python
| __bool__() -> bool
```
Hack to avoid pydantic name check from parent model, returns false
**Returns**:
`(bool)`: False
<a name="queryset.field_accessor.FieldAccessor.__getattr__"></a>
#### \_\_getattr\_\_
```python
| __getattr__(item: str) -> Any
```
Accessor return new accessor for each field and nested models.
Thanks to that operator overload is possible to use in filter.
**Arguments**:
- `item (str)`: attribute name
**Returns**:
`(ormar.queryset.field_accessor.FieldAccessor)`: FieldAccessor for field or nested model
<a name="queryset.field_accessor.FieldAccessor.__eq__"></a>
#### \_\_eq\_\_
```python
| __eq__(other: Any) -> FilterGroup
```
overloaded to work as sql `column = <VALUE>`
**Arguments**:
- `other (str)`: value to check agains operator
**Returns**:
`(ormar.queryset.clause.FilterGroup)`: FilterGroup for operator
<a name="queryset.field_accessor.FieldAccessor.__ge__"></a>
#### \_\_ge\_\_
```python
| __ge__(other: Any) -> FilterGroup
```
overloaded to work as sql `column >= <VALUE>`
**Arguments**:
- `other (str)`: value to check agains operator
**Returns**:
`(ormar.queryset.clause.FilterGroup)`: FilterGroup for operator
<a name="queryset.field_accessor.FieldAccessor.__gt__"></a>
#### \_\_gt\_\_
```python
| __gt__(other: Any) -> FilterGroup
```
overloaded to work as sql `column > <VALUE>`
**Arguments**:
- `other (str)`: value to check agains operator
**Returns**:
`(ormar.queryset.clause.FilterGroup)`: FilterGroup for operator
<a name="queryset.field_accessor.FieldAccessor.__le__"></a>
#### \_\_le\_\_
```python
| __le__(other: Any) -> FilterGroup
```
overloaded to work as sql `column <= <VALUE>`
**Arguments**:
- `other (str)`: value to check agains operator
**Returns**:
`(ormar.queryset.clause.FilterGroup)`: FilterGroup for operator
<a name="queryset.field_accessor.FieldAccessor.__lt__"></a>
#### \_\_lt\_\_
```python
| __lt__(other: Any) -> FilterGroup
```
overloaded to work as sql `column < <VALUE>`
**Arguments**:
- `other (str)`: value to check agains operator
**Returns**:
`(ormar.queryset.clause.FilterGroup)`: FilterGroup for operator
<a name="queryset.field_accessor.FieldAccessor.__mod__"></a>
#### \_\_mod\_\_
```python
| __mod__(other: Any) -> FilterGroup
```
overloaded to work as sql `column LIKE '%<VALUE>%'`
**Arguments**:
- `other (str)`: value to check agains operator
**Returns**:
`(ormar.queryset.clause.FilterGroup)`: FilterGroup for operator
<a name="queryset.field_accessor.FieldAccessor.__lshift__"></a>
#### \_\_lshift\_\_
```python
| __lshift__(other: Any) -> FilterGroup
```
overloaded to work as sql `column IN (<VALUE1>, <VALUE2>,...)`
**Arguments**:
- `other (str)`: value to check agains operator
**Returns**:
`(ormar.queryset.clause.FilterGroup)`: FilterGroup for operator
<a name="queryset.field_accessor.FieldAccessor.__rshift__"></a>
#### \_\_rshift\_\_
```python
| __rshift__(other: Any) -> FilterGroup
```
overloaded to work as sql `column IS NULL`
**Arguments**:
- `other (str)`: value to check agains operator
**Returns**:
`(ormar.queryset.clause.FilterGroup)`: FilterGroup for operator
<a name="queryset.field_accessor.FieldAccessor.in_"></a>
#### in\_
```python
| in_(other: Any) -> FilterGroup
```
works as sql `column IN (<VALUE1>, <VALUE2>,...)`
**Arguments**:
- `other (str)`: value to check agains operator
**Returns**:
`(ormar.queryset.clause.FilterGroup)`: FilterGroup for operator
<a name="queryset.field_accessor.FieldAccessor.iexact"></a>
#### iexact
```python
| iexact(other: Any) -> FilterGroup
```
works as sql `column = <VALUE>` case-insensitive
**Arguments**:
- `other (str)`: value to check agains operator
**Returns**:
`(ormar.queryset.clause.FilterGroup)`: FilterGroup for operator
<a name="queryset.field_accessor.FieldAccessor.contains"></a>
#### contains
```python
| contains(other: Any) -> FilterGroup
```
works as sql `column LIKE '%<VALUE>%'`
**Arguments**:
- `other (str)`: value to check agains operator
**Returns**:
`(ormar.queryset.clause.FilterGroup)`: FilterGroup for operator
<a name="queryset.field_accessor.FieldAccessor.icontains"></a>
#### icontains
```python
| icontains(other: Any) -> FilterGroup
```
works as sql `column LIKE '%<VALUE>%'` case-insensitive
**Arguments**:
- `other (str)`: value to check agains operator
**Returns**:
`(ormar.queryset.clause.FilterGroup)`: FilterGroup for operator
<a name="queryset.field_accessor.FieldAccessor.startswith"></a>
#### startswith
```python
| startswith(other: Any) -> FilterGroup
```
works as sql `column LIKE '<VALUE>%'`
**Arguments**:
- `other (str)`: value to check agains operator
**Returns**:
`(ormar.queryset.clause.FilterGroup)`: FilterGroup for operator
<a name="queryset.field_accessor.FieldAccessor.istartswith"></a>
#### istartswith
```python
| istartswith(other: Any) -> FilterGroup
```
works as sql `column LIKE '%<VALUE>'` case-insensitive
**Arguments**:
- `other (str)`: value to check agains operator
**Returns**:
`(ormar.queryset.clause.FilterGroup)`: FilterGroup for operator
<a name="queryset.field_accessor.FieldAccessor.endswith"></a>
#### endswith
```python
| endswith(other: Any) -> FilterGroup
```
works as sql `column LIKE '%<VALUE>'`
**Arguments**:
- `other (str)`: value to check agains operator
**Returns**:
`(ormar.queryset.clause.FilterGroup)`: FilterGroup for operator
<a name="queryset.field_accessor.FieldAccessor.iendswith"></a>
#### iendswith
```python
| iendswith(other: Any) -> FilterGroup
```
works as sql `column LIKE '%<VALUE>'` case-insensitive
**Arguments**:
- `other (str)`: value to check agains operator
**Returns**:
`(ormar.queryset.clause.FilterGroup)`: FilterGroup for operator
<a name="queryset.field_accessor.FieldAccessor.isnull"></a>
#### isnull
```python
| isnull(other: Any) -> FilterGroup
```
works as sql `column IS NULL` or `IS NOT NULL`
**Arguments**:
- `other (str)`: value to check agains operator
**Returns**:
`(ormar.queryset.clause.FilterGroup)`: FilterGroup for operator
<a name="queryset.field_accessor.FieldAccessor.asc"></a>
#### asc
```python
| asc() -> OrderAction
```
works as sql `column asc`
**Returns**:
`(ormar.queryset.actions.OrderGroup)`: OrderGroup for operator
<a name="queryset.field_accessor.FieldAccessor.desc"></a>
#### desc
```python
| desc() -> OrderAction
```
works as sql `column desc`
**Returns**:
`(ormar.queryset.actions.OrderGroup)`: OrderGroup for operator

View File

@ -27,7 +27,7 @@ Shortcut for ormar's model AliasManager stored on Meta.
```python
| @property
| to_table() -> str
| to_table() -> sqlalchemy.Table
```
Shortcut to table name of the next model
@ -172,6 +172,36 @@ Updates the used aliases list directly.
Process order_by causes for non m2m relations.
<a name="queryset.join.SqlJoin._verify_allowed_order_field"></a>
#### \_verify\_allowed\_order\_field
```python
| _verify_allowed_order_field(order_by: str) -> None
```
Verifies if proper field string is used.
**Arguments**:
- `order_by (str)`: string with order by definition
<a name="queryset.join.SqlJoin._get_alias_and_model"></a>
#### \_get\_alias\_and\_model
```python
| _get_alias_and_model(order_by: str) -> Tuple[str, Type["Model"]]
```
Returns proper model and alias to be applied in the clause.
**Arguments**:
- `order_by (str)`: string with order by definition
**Returns**:
`(Tuple[str, Type["Model"]])`: alias and model to be used in clause
<a name="queryset.join.SqlJoin._get_order_bys"></a>
#### \_get\_order\_bys

View File

@ -241,7 +241,7 @@ Calls itself recurrently to extract deeper nested relations of related model.
#### \_run\_prefetch\_query
```python
| async _run_prefetch_query(target_field: Type["BaseField"], excludable: "ExcludableItems", filter_clauses: List, related_field_name: str) -> Tuple[str, str, List]
| async _run_prefetch_query(target_field: "BaseField", excludable: "ExcludableItems", filter_clauses: List, related_field_name: str) -> Tuple[str, str, List]
```
Actually runs the queries against the database and populates the raw response
@ -252,7 +252,7 @@ models.
**Arguments**:
- `target_field (Type["BaseField"])`: ormar field with relation definition
- `target_field ("BaseField")`: ormar field with relation definition
- `filter_clauses (List[sqlalchemy.sql.elements.TextClause])`: list of clauses, actually one clause with ids of relation
**Returns**:
@ -283,14 +283,14 @@ deeper on related model and already loaded in select related query.
#### \_update\_already\_loaded\_rows
```python
| _update_already_loaded_rows(target_field: Type["BaseField"], prefetch_dict: Dict, orders_by: Dict) -> None
| _update_already_loaded_rows(target_field: "BaseField", prefetch_dict: Dict, orders_by: Dict) -> None
```
Updates models that are already loaded, usually children of children.
**Arguments**:
- `target_field (Type["BaseField"])`: ormar field with relation definition
- `target_field ("BaseField")`: ormar field with relation definition
- `prefetch_dict (Dict)`: dictionaries of related models to prefetch
- `orders_by (Dict)`: dictionary of order by clauses by model
@ -298,7 +298,7 @@ Updates models that are already loaded, usually children of children.
#### \_populate\_rows
```python
| _populate_rows(rows: List, target_field: Type["ForeignKeyField"], parent_model: Type["Model"], table_prefix: str, exclude_prefix: str, excludable: "ExcludableItems", prefetch_dict: Dict, orders_by: Dict) -> None
| _populate_rows(rows: List, target_field: "ForeignKeyField", parent_model: Type["Model"], table_prefix: str, exclude_prefix: str, excludable: "ExcludableItems", prefetch_dict: Dict, orders_by: Dict) -> None
```
Instantiates children models extracted from given relation.
@ -314,7 +314,7 @@ and set on the parent model after sorting if needed.
- `excludable (ExcludableItems)`: structure of fields to include and exclude
- `rows (List[sqlalchemy.engine.result.RowProxy])`: raw sql response from the prefetch query
- `target_field (Type["BaseField"])`: field with relation definition from parent model
- `target_field ("BaseField")`: field with relation definition from parent model
- `parent_model (Type[Model])`: model with relation definition
- `table_prefix (str)`: prefix of the target table from current relation
- `prefetch_dict (Dict)`: dictionaries of related models to prefetch

View File

@ -5,7 +5,7 @@
## QuerySet Objects
```python
class QuerySet()
class QuerySet(Generic[T])
```
Main class to perform database queries, exposed on each model as objects attribute.
@ -29,7 +29,7 @@ Shortcut to model class Meta set on QuerySet model.
```python
| @property
| model() -> Type["Model"]
| model() -> Type["T"]
```
Shortcut to model class set on QuerySet.
@ -52,7 +52,7 @@ all not passed params are taken from current values.
#### \_prefetch\_related\_models
```python
| async _prefetch_related_models(models: Sequence[Optional["Model"]], rows: List) -> Sequence[Optional["Model"]]
| async _prefetch_related_models(models: List[Optional["T"]], rows: List) -> List[Optional["T"]]
```
Performs prefetch query for selected models names.
@ -70,7 +70,7 @@ Performs prefetch query for selected models names.
#### \_process\_query\_result\_rows
```python
| _process_query_result_rows(rows: List) -> Sequence[Optional["Model"]]
| _process_query_result_rows(rows: List) -> List[Optional["T"]]
```
Process database rows and initialize ormar Model from each of the rows.
@ -83,12 +83,29 @@ Process database rows and initialize ormar Model from each of the rows.
`(List[Model])`: list of models
<a name="queryset.queryset.QuerySet._resolve_filter_groups"></a>
#### \_resolve\_filter\_groups
```python
| _resolve_filter_groups(groups: Any) -> Tuple[List[FilterGroup], List[str]]
```
Resolves filter groups to populate FilterAction params in group tree.
**Arguments**:
- `groups (Any)`: tuple of FilterGroups
**Returns**:
`(Tuple[List[FilterGroup], List[str]])`: list of resolver groups
<a name="queryset.queryset.QuerySet.check_single_result_rows_count"></a>
#### check\_single\_result\_rows\_count
```python
| @staticmethod
| check_single_result_rows_count(rows: Sequence[Optional["Model"]]) -> None
| check_single_result_rows_count(rows: Sequence[Optional["T"]]) -> None
```
Verifies if the result has one and only one row.
@ -149,7 +166,7 @@ If any of the params is not passed the QuerySet own value is used.
#### filter
```python
| filter(_exclude: bool = False, **kwargs: Any) -> "QuerySet"
| filter(*args: Any, *, _exclude: bool = False, **kwargs: Any) -> "QuerySet[T]"
```
Allows you to filter by any `Model` attribute/field
@ -162,6 +179,8 @@ You can use special filter suffix to change the filter operands:
* contains - like `album__name__contains='Mal'` (sql like)
* icontains - like `album__name__icontains='mal'` (sql like case insensitive)
* in - like `album__name__in=['Malibu', 'Barclay']` (sql in)
* isnull - like `album__name__isnull=True` (sql is null)
(isnotnull `album__name__isnull=False` (sql is not null))
* gt - like `position__gt=3` (sql >)
* gte - like `position__gte=3` (sql >=)
* lt - like `position__lt=3` (sql <)
@ -184,7 +203,7 @@ You can use special filter suffix to change the filter operands:
#### exclude
```python
| exclude(**kwargs: Any) -> "QuerySet"
| exclude(*args: Any, **kwargs: Any) -> "QuerySet[T]"
```
Works exactly the same as filter and all modifiers (suffixes) are the same,
@ -211,7 +230,7 @@ becomes a union of conditions.
#### select\_related
```python
| select_related(related: Union[List, str]) -> "QuerySet"
| select_related(related: Union[List, str]) -> "QuerySet[T]"
```
Allows to prefetch related models during the same query.
@ -232,11 +251,40 @@ To chain related `Models` relation use double underscores between names.
`(QuerySet)`: QuerySet
<a name="queryset.queryset.QuerySet.select_all"></a>
#### select\_all
```python
| select_all(follow: bool = False) -> "QuerySet[T]"
```
By default adds only directly related models.
If follow=True is set it adds 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.
**Arguments**:
- `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
<a name="queryset.queryset.QuerySet.prefetch_related"></a>
#### prefetch\_related
```python
| prefetch_related(related: Union[List, str]) -> "QuerySet"
| prefetch_related(related: Union[List, str]) -> "QuerySet[T]"
```
Allows to prefetch related models during query - but opposite to
@ -262,7 +310,7 @@ To chain related `Models` relation use double underscores between names.
#### fields
```python
| fields(columns: Union[List, str, Set, Dict], _is_exclude: bool = False) -> "QuerySet"
| fields(columns: Union[List, str, Set, Dict], _is_exclude: bool = False) -> "QuerySet[T]"
```
With `fields()` you can select subset of model columns to limit the data load.
@ -314,7 +362,7 @@ To include whole nested model specify model related field name and ellipsis.
#### exclude\_fields
```python
| exclude_fields(columns: Union[List, str, Set, Dict]) -> "QuerySet"
| exclude_fields(columns: Union[List, str, Set, Dict]) -> "QuerySet[T]"
```
With `exclude_fields()` you can select subset of model columns that will
@ -349,7 +397,7 @@ if explicitly excluded.
#### order\_by
```python
| order_by(columns: Union[List, str]) -> "QuerySet"
| order_by(columns: Union[List, str, OrderAction]) -> "QuerySet[T]"
```
With `order_by()` you can order the results from database based on your
@ -413,6 +461,62 @@ Returns number of rows matching the given criteria
`(int)`: number of rows
<a name="queryset.queryset.QuerySet.max"></a>
#### max
```python
| async max(columns: Union[str, List[str]]) -> Any
```
Returns max value of columns for rows matching the given criteria
(applied with `filter` and `exclude` if set before).
**Returns**:
`(Any)`: max value of column(s)
<a name="queryset.queryset.QuerySet.min"></a>
#### min
```python
| async min(columns: Union[str, List[str]]) -> Any
```
Returns min value of columns for rows matching the given criteria
(applied with `filter` and `exclude` if set before).
**Returns**:
`(Any)`: min value of column(s)
<a name="queryset.queryset.QuerySet.sum"></a>
#### sum
```python
| async sum(columns: Union[str, List[str]]) -> Any
```
Returns sum value of columns for rows matching the given criteria
(applied with `filter` and `exclude` if set before).
**Returns**:
`(int)`: sum value of columns
<a name="queryset.queryset.QuerySet.avg"></a>
#### avg
```python
| async avg(columns: Union[str, List[str]]) -> Any
```
Returns avg value of columns for rows matching the given criteria
(applied with `filter` and `exclude` if set before).
**Returns**:
`(Union[int, float, List])`: avg value of columns
<a name="queryset.queryset.QuerySet.update"></a>
#### update
@ -438,7 +542,7 @@ each=True flag to affect whole table.
#### delete
```python
| async delete(each: bool = False, **kwargs: Any) -> int
| async delete(*args: Any, *, each: bool = False, **kwargs: Any) -> int
```
Deletes from the model table after applying the filters from kwargs.
@ -459,7 +563,7 @@ each=True flag to affect whole table.
#### paginate
```python
| paginate(page: int, page_size: int = 20) -> "QuerySet"
| paginate(page: int, page_size: int = 20) -> "QuerySet[T]"
```
You can paginate the result which is a combination of offset and limit clauses.
@ -478,7 +582,7 @@ Limit is set to page size and offset is set to (page-1) * page_size.
#### limit
```python
| limit(limit_count: int, limit_raw_sql: bool = None) -> "QuerySet"
| limit(limit_count: int, limit_raw_sql: bool = None) -> "QuerySet[T]"
```
You can limit the results to desired number of parent models.
@ -499,7 +603,7 @@ models use the `limit_raw_sql` parameter flag, and set it to `True`.
#### offset
```python
| offset(offset: int, limit_raw_sql: bool = None) -> "QuerySet"
| offset(offset: int, limit_raw_sql: bool = None) -> "QuerySet[T]"
```
You can also offset the results by desired number of main models.
@ -520,7 +624,7 @@ models use the `limit_raw_sql` parameter flag, and set it to `True`.
#### first
```python
| async first(**kwargs: Any) -> "Model"
| async first(*args: Any, **kwargs: Any) -> "T"
```
Gets the first row from the db ordered by primary key column ascending.
@ -538,11 +642,34 @@ Gets the first row from the db ordered by primary key column ascending.
`(Model)`: returned model
<a name="queryset.queryset.QuerySet.get_or_none"></a>
#### get\_or\_none
```python
| async get_or_none(*args: Any, **kwargs: Any) -> Optional["T"]
```
Get's the first row from the db meeting the criteria set by kwargs.
If no criteria set it will return the last row in db sorted by pk.
Passing a criteria is actually calling filter(**kwargs) method described below.
If not match is found None will be returned.
**Arguments**:
- `kwargs (Any)`: fields names and proper value types
**Returns**:
`(Model)`: returned model
<a name="queryset.queryset.QuerySet.get"></a>
#### get
```python
| async get(**kwargs: Any) -> "Model"
| async get(*args: Any, **kwargs: Any) -> "T"
```
Get's the first row from the db meeting the criteria set by kwargs.
@ -568,7 +695,7 @@ Passing a criteria is actually calling filter(**kwargs) method described below.
#### get\_or\_create
```python
| async get_or_create(**kwargs: Any) -> "Model"
| async get_or_create(*args: Any, **kwargs: Any) -> "T"
```
Combination of create and get methods.
@ -589,7 +716,7 @@ it creates a new one with given kwargs.
#### update\_or\_create
```python
| async update_or_create(**kwargs: Any) -> "Model"
| async update_or_create(**kwargs: Any) -> "T"
```
Updates the model, or in case there is no match in database creates a new one.
@ -606,7 +733,7 @@ Updates the model, or in case there is no match in database creates a new one.
#### all
```python
| async all(**kwargs: Any) -> Sequence[Optional["Model"]]
| async all(*args: Any, **kwargs: Any) -> List[Optional["T"]]
```
Returns all rows from a database for given model for set filter options.
@ -627,7 +754,7 @@ If there are no rows meeting the criteria an empty list is returned.
#### create
```python
| async create(**kwargs: Any) -> "Model"
| async create(**kwargs: Any) -> "T"
```
Creates the model instance, saves it in a database and returns the updates model
@ -647,7 +774,7 @@ The allowed kwargs are `Model` fields names and proper value types.
#### bulk\_create
```python
| async bulk_create(objects: List["Model"]) -> None
| async bulk_create(objects: List["T"]) -> None
```
Performs a bulk update in one database session to speed up the process.
@ -666,7 +793,7 @@ Bulk operations do not send signals.
#### bulk\_update
```python
| async bulk_update(objects: List["Model"], columns: List[str] = None) -> None
| async bulk_update(objects: List["T"], columns: List[str] = None) -> None
```
Performs bulk update in one database session to speed up the process.

View File

@ -28,6 +28,16 @@ Applies order_by queries on main model when it's used as a subquery.
That way the subquery with limit and offset only on main model has proper
sorting applied and correct models are fetched.
<a name="queryset.query.Query._apply_default_model_sorting"></a>
#### \_apply\_default\_model\_sorting
```python
| _apply_default_model_sorting() -> None
```
Applies orders_by from model Meta class (if provided), if it was not provided
it was filled by metaclass so it's always there and falls back to pk column
<a name="queryset.query.Query._pagination_query_required"></a>
#### \_pagination\_query\_required
@ -62,11 +72,13 @@ Returns ready to run query with all joins and clauses.
`(sqlalchemy.sql.selectable.Select)`: ready to run query with all joins and clauses.
<a name="queryset.query.Query._build_pagination_subquery"></a>
#### \_build\_pagination\_subquery
<a name="queryset.query.Query._build_pagination_condition"></a>
#### \_build\_pagination\_condition
```python
| _build_pagination_subquery() -> sqlalchemy.sql.select
| _build_pagination_condition() -> Tuple[
| sqlalchemy.sql.expression.TextClause, sqlalchemy.sql.expression.TextClause
| ]
```
In order to apply limit and offset on main table in join only
@ -78,9 +90,8 @@ Needed only if limit or offset are set, the flag limit_sql_raw is not set
and query has select_related applied. Otherwise we can limit/offset normally
at the end of whole query.
**Returns**:
`(sqlalchemy.sql.select)`: constructed subquery on main table with limit, offset and order applied
The condition is added to filters to filter out desired number of main model
primary key values. Whole query is used to determine the values.
<a name="queryset.query.Query._apply_expression_modifiers"></a>
#### \_apply\_expression\_modifiers

View File

@ -5,7 +5,7 @@
#### check\_node\_not\_dict\_or\_not\_last\_node
```python
check_node_not_dict_or_not_last_node(part: str, parts: List, current_level: Any) -> bool
check_node_not_dict_or_not_last_node(part: str, is_last: bool, current_level: Any) -> bool
```
Checks if given name is not present in the current level of the structure.
@ -86,6 +86,27 @@ only other values are overwritten.
`(Dict)`: combination of both dicts
<a name="queryset.utils.subtract_dict"></a>
#### subtract\_dict
```python
subtract_dict(current_dict: Any, updating_dict: Any) -> Dict
```
Update one dict with another but with regard for nested keys.
That way nested sets are unionised, dicts updated and
only other values are overwritten.
**Arguments**:
- `current_dict (Dict[str, ellipsis])`: dict to update
- `updating_dict (Dict)`: dict with values to update
**Returns**:
`(Dict)`: combination of both dicts
<a name="queryset.utils.update_dict_from_list"></a>
#### update\_dict\_from\_list
@ -169,3 +190,24 @@ constructed, extracts alias based on last relation leading to target model.
`(Tuple[str, Type["Model"], str])`: table prefix, target model and relation string
<a name="queryset.utils._process_through_field"></a>
#### \_process\_through\_field
```python
_process_through_field(related_parts: List, relation: Optional[str], related_field: "BaseField", previous_model: Type["Model"], previous_models: List[Type["Model"]]) -> Tuple[Type["Model"], Optional[str], bool]
```
Helper processing through models as they need to be treated differently.
**Arguments**:
- `related_parts (List[str])`: split relation string
- `relation (str)`: relation name
- `related_field ("ForeignKeyField")`: field with relation declaration
- `previous_model (Type["Model"])`: model from which relation is coming
- `previous_models (List[Type["Model"]])`: list of already visited models in relation chain
**Returns**:
`(Tuple[Type["Model"], str, bool])`: previous_model, relation, is_through

View File

@ -56,7 +56,7 @@ List has to have sqlalchemy names of columns (ormar aliases) not the ormar ones.
```python
| @staticmethod
| prefixed_table_name(alias: str, name: str) -> text
| prefixed_table_name(alias: str, table: sqlalchemy.Table) -> text
```
Creates text clause with table name with aliased name.
@ -64,7 +64,7 @@ Creates text clause with table name with aliased name.
**Arguments**:
- `alias (str)`: alias of given table
- `name (str)`: table name
- `table (sqlalchemy.Table)`: table
**Returns**:
@ -138,7 +138,7 @@ Given model and relation name returns the alias for this relation.
#### resolve\_relation\_alias\_after\_complex
```python
| resolve_relation_alias_after_complex(source_model: Union[Type["Model"], Type["ModelRow"]], relation_str: str, relation_field: Type["ForeignKeyField"]) -> str
| resolve_relation_alias_after_complex(source_model: Union[Type["Model"], Type["ModelRow"]], relation_str: str, relation_field: "ForeignKeyField") -> str
```
Given source model and relation string returns the alias for this complex
@ -147,7 +147,7 @@ field definition.
**Arguments**:
- `relation_field (Type["ForeignKeyField"])`: field with direct relation definition
- `relation_field ("ForeignKeyField")`: field with direct relation definition
- `source_model (source Model)`: model with query starts
- `relation_str (str)`: string with relation joins defined

View File

@ -5,7 +5,7 @@
## QuerysetProxy Objects
```python
class QuerysetProxy()
class QuerysetProxy(Generic[T])
```
Exposes QuerySet methods on relations, but also handles creating and removing
@ -16,7 +16,7 @@ of through Models for m2m relations.
```python
| @property
| queryset() -> "QuerySet"
| queryset() -> "QuerySet[T]"
```
Returns queryset if it's set, AttributeError otherwise.
@ -43,7 +43,7 @@ Set's the queryset. Initialized in RelationProxy.
#### \_assign\_child\_to\_parent
```python
| _assign_child_to_parent(child: Optional["Model"]) -> None
| _assign_child_to_parent(child: Optional["T"]) -> None
```
Registers child in parents RelationManager.
@ -56,7 +56,7 @@ Registers child in parents RelationManager.
#### \_register\_related
```python
| _register_related(child: Union["Model", Sequence[Optional["Model"]]]) -> None
| _register_related(child: Union["T", Sequence[Optional["T"]]]) -> None
```
Registers child/ children in parents RelationManager.
@ -78,7 +78,7 @@ Cleans the current list of the related models.
#### create\_through\_instance
```python
| async create_through_instance(child: "Model", **kwargs: Any) -> None
| async create_through_instance(child: "T", **kwargs: Any) -> None
```
Crete a through model instance in the database for m2m relations.
@ -92,7 +92,7 @@ Crete a through model instance in the database for m2m relations.
#### update\_through\_instance
```python
| async update_through_instance(child: "Model", **kwargs: Any) -> None
| async update_through_instance(child: "T", **kwargs: Any) -> None
```
Updates a through model instance in the database for m2m relations.
@ -102,11 +102,26 @@ Updates a through model instance in the database for m2m relations.
- `kwargs (Any)`: dict of additional keyword arguments for through instance
- `child (Model)`: child model instance
<a name="relations.querysetproxy.QuerysetProxy.upsert_through_instance"></a>
#### upsert\_through\_instance
```python
| async upsert_through_instance(child: "T", **kwargs: Any) -> None
```
Updates a through model instance in the database for m2m relations if
it already exists, else creates one.
**Arguments**:
- `kwargs (Any)`: dict of additional keyword arguments for through instance
- `child (Model)`: child model instance
<a name="relations.querysetproxy.QuerysetProxy.delete_through_instance"></a>
#### delete\_through\_instance
```python
| async delete_through_instance(child: "Model") -> None
| async delete_through_instance(child: "T") -> None
```
Removes through model instance from the database for m2m relations.
@ -147,6 +162,62 @@ Actual call delegated to QuerySet.
`(int)`: number of rows
<a name="relations.querysetproxy.QuerysetProxy.max"></a>
#### max
```python
| async max(columns: Union[str, List[str]]) -> Any
```
Returns max value of columns for rows matching the given criteria
(applied with `filter` and `exclude` if set before).
**Returns**:
`(Any)`: max value of column(s)
<a name="relations.querysetproxy.QuerysetProxy.min"></a>
#### min
```python
| async min(columns: Union[str, List[str]]) -> Any
```
Returns min value of columns for rows matching the given criteria
(applied with `filter` and `exclude` if set before).
**Returns**:
`(Any)`: min value of column(s)
<a name="relations.querysetproxy.QuerysetProxy.sum"></a>
#### sum
```python
| async sum(columns: Union[str, List[str]]) -> Any
```
Returns sum value of columns for rows matching the given criteria
(applied with `filter` and `exclude` if set before).
**Returns**:
`(int)`: sum value of columns
<a name="relations.querysetproxy.QuerysetProxy.avg"></a>
#### avg
```python
| async avg(columns: Union[str, List[str]]) -> Any
```
Returns avg value of columns for rows matching the given criteria
(applied with `filter` and `exclude` if set before).
**Returns**:
`(Union[int, float, List])`: avg value of columns
<a name="relations.querysetproxy.QuerysetProxy.clear"></a>
#### clear
@ -175,7 +246,7 @@ or not, keep_reversed=False deletes them from database.
#### first
```python
| async first(**kwargs: Any) -> "Model"
| async first(*args: Any, **kwargs: Any) -> "T"
```
Gets the first row from the db ordered by primary key column ascending.
@ -192,11 +263,34 @@ List of related models is cleared before the call.
`(_asyncio.Future)`:
<a name="relations.querysetproxy.QuerysetProxy.get_or_none"></a>
#### get\_or\_none
```python
| async get_or_none(*args: Any, **kwargs: Any) -> Optional["T"]
```
Get's the first row from the db meeting the criteria set by kwargs.
If no criteria set it will return the last row in db sorted by pk.
Passing a criteria is actually calling filter(**kwargs) method described below.
If not match is found None will be returned.
**Arguments**:
- `kwargs (Any)`: fields names and proper value types
**Returns**:
`(Model)`: returned model
<a name="relations.querysetproxy.QuerysetProxy.get"></a>
#### get
```python
| async get(**kwargs: Any) -> "Model"
| async get(*args: Any, **kwargs: Any) -> "T"
```
Get's the first row from the db meeting the criteria set by kwargs.
@ -226,7 +320,7 @@ List of related models is cleared before the call.
#### all
```python
| async all(**kwargs: Any) -> Sequence[Optional["Model"]]
| async all(*args: Any, **kwargs: Any) -> List[Optional["T"]]
```
Returns all rows from a database for given model for set filter options.
@ -251,7 +345,7 @@ List of related models is cleared before the call.
#### create
```python
| async create(**kwargs: Any) -> "Model"
| async create(**kwargs: Any) -> "T"
```
Creates the model instance, saves it in a database and returns the updates model
@ -296,7 +390,7 @@ each=True flag to affect whole table.
#### get\_or\_create
```python
| async get_or_create(**kwargs: Any) -> "Model"
| async get_or_create(*args: Any, **kwargs: Any) -> "T"
```
Combination of create and get methods.
@ -317,7 +411,7 @@ it creates a new one with given kwargs.
#### update\_or\_create
```python
| async update_or_create(**kwargs: Any) -> "Model"
| async update_or_create(**kwargs: Any) -> "T"
```
Updates the model, or in case there is no match in database creates a new one.
@ -336,7 +430,7 @@ Actual call delegated to QuerySet.
#### filter
```python
| filter(**kwargs: Any) -> "QuerysetProxy"
| filter(*args: Any, **kwargs: Any) -> "QuerysetProxy[T]"
```
Allows you to filter by any `Model` attribute/field
@ -349,6 +443,8 @@ You can use special filter suffix to change the filter operands:
* contains - like `album__name__contains='Mal'` (sql like)
* icontains - like `album__name__icontains='mal'` (sql like case insensitive)
* in - like `album__name__in=['Malibu', 'Barclay']` (sql in)
* isnull - like `album__name__isnull=True` (sql is null)
(isnotnull `album__name__isnull=False` (sql is not null))
* gt - like `position__gt=3` (sql >)
* gte - like `position__gte=3` (sql >=)
* lt - like `position__lt=3` (sql <)
@ -372,7 +468,7 @@ Actual call delegated to QuerySet.
#### exclude
```python
| exclude(**kwargs: Any) -> "QuerysetProxy"
| exclude(*args: Any, **kwargs: Any) -> "QuerysetProxy[T]"
```
Works exactly the same as filter and all modifiers (suffixes) are the same,
@ -397,11 +493,40 @@ Actual call delegated to QuerySet.
`(QuerysetProxy)`: filtered QuerysetProxy
<a name="relations.querysetproxy.QuerysetProxy.select_all"></a>
#### select\_all
```python
| select_all(follow: bool = False) -> "QuerysetProxy[T]"
```
By default adds only directly related models.
If follow=True is set it adds 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.
**Arguments**:
- `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
<a name="relations.querysetproxy.QuerysetProxy.select_related"></a>
#### select\_related
```python
| select_related(related: Union[List, str]) -> "QuerysetProxy"
| select_related(related: Union[List, str]) -> "QuerysetProxy[T]"
```
Allows to prefetch related models during the same query.
@ -428,7 +553,7 @@ Actual call delegated to QuerySet.
#### prefetch\_related
```python
| prefetch_related(related: Union[List, str]) -> "QuerysetProxy"
| prefetch_related(related: Union[List, str]) -> "QuerysetProxy[T]"
```
Allows to prefetch related models during query - but opposite to
@ -456,7 +581,7 @@ Actual call delegated to QuerySet.
#### paginate
```python
| paginate(page: int, page_size: int = 20) -> "QuerysetProxy"
| paginate(page: int, page_size: int = 20) -> "QuerysetProxy[T]"
```
You can paginate the result which is a combination of offset and limit clauses.
@ -477,7 +602,7 @@ Actual call delegated to QuerySet.
#### limit
```python
| limit(limit_count: int) -> "QuerysetProxy"
| limit(limit_count: int) -> "QuerysetProxy[T]"
```
You can limit the results to desired number of parent models.
@ -496,7 +621,7 @@ Actual call delegated to QuerySet.
#### offset
```python
| offset(offset: int) -> "QuerysetProxy"
| offset(offset: int) -> "QuerysetProxy[T]"
```
You can also offset the results by desired number of main models.
@ -515,7 +640,7 @@ Actual call delegated to QuerySet.
#### fields
```python
| fields(columns: Union[List, str, Set, Dict]) -> "QuerysetProxy"
| fields(columns: Union[List, str, Set, Dict]) -> "QuerysetProxy[T]"
```
With `fields()` you can select subset of model columns to limit the data load.
@ -568,7 +693,7 @@ Actual call delegated to QuerySet.
#### exclude\_fields
```python
| exclude_fields(columns: Union[List, str, Set, Dict]) -> "QuerysetProxy"
| exclude_fields(columns: Union[List, str, Set, Dict]) -> "QuerysetProxy[T]"
```
With `exclude_fields()` you can select subset of model columns that will
@ -605,7 +730,7 @@ Actual call delegated to QuerySet.
#### order\_by
```python
| order_by(columns: Union[List, str]) -> "QuerysetProxy"
| order_by(columns: Union[List, str, "OrderAction"]) -> "QuerysetProxy[T]"
```
With `order_by()` you can order the results from database based on your

View File

@ -50,7 +50,7 @@ Actual call is delegated to Relation instance registered under relation name.
```python
| @staticmethod
| add(parent: "Model", child: "Model", field: Type["ForeignKeyField"]) -> None
| add(parent: "Model", child: "Model", field: "ForeignKeyField") -> None
```
Adds relation on both sides -> meaning on both child and parent models.
@ -121,14 +121,14 @@ Returns the actual relation and not the related model(s).
#### \_get\_relation\_type
```python
| _get_relation_type(field: Type["BaseField"]) -> RelationType
| _get_relation_type(field: "BaseField") -> RelationType
```
Returns type of the relation declared on a field.
**Arguments**:
- `field (Type[BaseField])`: field with relation declaration
- `field (BaseField)`: field with relation declaration
**Returns**:
@ -138,7 +138,7 @@ Returns type of the relation declared on a field.
#### \_add\_relation
```python
| _add_relation(field: Type["BaseField"]) -> None
| _add_relation(field: "BaseField") -> None
```
Registers relation in the manager.
@ -146,5 +146,5 @@ Adds Relation instance under field.name.
**Arguments**:
- `field (Type[BaseField])`: field with relation declaration
- `field (BaseField)`: field with relation declaration

View File

@ -5,7 +5,7 @@
## RelationProxy Objects
```python
class RelationProxy(list)
class RelationProxy(Generic[T], list)
```
Proxy of the Relation that is a list with special methods.
@ -96,7 +96,7 @@ Otherwise QuerySetProxy cannot filter by parent primary key.
#### \_set\_queryset
```python
| _set_queryset() -> "QuerySet"
| _set_queryset() -> "QuerySet[T]"
```
Creates new QuerySet with relation model and pre filters it with currents
@ -111,7 +111,7 @@ to the parent model only, without need for user to filter them.
#### remove
```python
| async remove(item: "Model", keep_reversed: bool = True) -> None
| async remove(item: "T", keep_reversed: bool = True) -> None
```
Removes the related from relation with parent.
@ -131,7 +131,7 @@ will be deleted, and not only removed from relation).
#### add
```python
| async add(item: "Model", **kwargs: Any) -> None
| async add(item: "T", **kwargs: Any) -> None
```
Adds child model to relation.

View File

@ -18,7 +18,7 @@ Different types of relations supported by ormar:
## Relation Objects
```python
class Relation()
class Relation(Generic[T])
```
Keeps related Models and handles adding/removing of the children.
@ -27,7 +27,7 @@ Keeps related Models and handles adding/removing of the children.
#### \_\_init\_\_
```python
| __init__(manager: "RelationsManager", type_: RelationType, field_name: str, to: Type["Model"], through: Type["Model"] = None) -> None
| __init__(manager: "RelationsManager", type_: RelationType, field_name: str, to: Type["T"], through: Type["Model"] = None) -> None
```
Initialize the Relation and keep the related models either as instances of

View File

@ -5,7 +5,7 @@
#### get\_relations\_sides\_and\_names
```python
get_relations_sides_and_names(to_field: Type[ForeignKeyField], parent: "Model", child: "Model") -> Tuple["Model", "Model", str, str]
get_relations_sides_and_names(to_field: ForeignKeyField, parent: "Model", child: "Model") -> Tuple["Model", "Model", str, str]
```
Determines the names of child and parent relations names, as well as

View File

@ -128,3 +128,75 @@ that should have the signal receiver registered
`(Callable)`: returns the original function untouched
<a name="decorators.signals.pre_relation_add"></a>
#### pre\_relation\_add
```python
pre_relation_add(senders: Union[Type["Model"], List[Type["Model"]]]) -> Callable
```
Connect given function to all senders for pre_relation_add signal.
**Arguments**:
- `senders (Union[Type["Model"], List[Type["Model"]]])`: one or a list of "Model" classes
that should have the signal receiver registered
**Returns**:
`(Callable)`: returns the original function untouched
<a name="decorators.signals.post_relation_add"></a>
#### post\_relation\_add
```python
post_relation_add(senders: Union[Type["Model"], List[Type["Model"]]]) -> Callable
```
Connect given function to all senders for post_relation_add signal.
**Arguments**:
- `senders (Union[Type["Model"], List[Type["Model"]]])`: one or a list of "Model" classes
that should have the signal receiver registered
**Returns**:
`(Callable)`: returns the original function untouched
<a name="decorators.signals.pre_relation_remove"></a>
#### pre\_relation\_remove
```python
pre_relation_remove(senders: Union[Type["Model"], List[Type["Model"]]]) -> Callable
```
Connect given function to all senders for pre_relation_remove signal.
**Arguments**:
- `senders (Union[Type["Model"], List[Type["Model"]]])`: one or a list of "Model" classes
that should have the signal receiver registered
**Returns**:
`(Callable)`: returns the original function untouched
<a name="decorators.signals.post_relation_remove"></a>
#### post\_relation\_remove
```python
post_relation_remove(senders: Union[Type["Model"], List[Type["Model"]]]) -> Callable
```
Connect given function to all senders for post_relation_remove signal.
**Arguments**:
- `senders (Union[Type["Model"], List[Type["Model"]]])`: one or a list of "Model" classes
that should have the signal receiver registered
**Returns**:
`(Callable)`: returns the original function untouched

View File

@ -69,34 +69,34 @@ tracks = Track.objects.filter(album__name="Fantasies").all()
You can use special filter suffix to change the filter operands:
* exact - exact match to value, sql `column = <VALUE>`
* can be written as`album__name__exact='Malibu'`
* iexact - exact match sql `column = <VALUE>` (case insensitive)
* can be written as`album__name__iexact='malibu'`
* contains - sql `column LIKE '%<VALUE>%'`
* can be written as`album__name__contains='Mal'`
* icontains - sql `column LIKE '%<VALUE>%'` (case insensitive)
* can be written as`album__name__icontains='mal'`
* in - sql ` column IN (<VALUE1>, <VALUE2>, ...)`
* can be written as`album__name__in=['Malibu', 'Barclay']`
* isnull - sql `column IS NULL` (and sql `column IS NOT NULL`)
* can be written as`album__name__isnull=True` (isnotnull `album__name__isnull=False`)
* gt - sql `column > <VALUE>` (greater than)
* can be written as`position__gt=3`
* gte - sql `column >= <VALUE>` (greater or equal than)
* can be written as`position__gte=3`
* lt - sql `column < <VALUE>` (lower than)
* can be written as`position__lt=3`
* lte - sql `column <= <VALUE>` (lower equal than)
* can be written as`position__lte=3`
* startswith - sql `column LIKE '<VALUE>%'` (exact start match)
* can be written as`album__name__startswith='Mal'`
* istartswith - sql `column LIKE '<VALUE>%'` (case insensitive)
* can be written as`album__name__istartswith='mal'`
* endswith - sql `column LIKE '%<VALUE>'` (exact end match)
* can be written as`album__name__endswith='ibu'`
* iendswith - sql `column LIKE '%<VALUE>'` (case insensitive)
* can be written as`album__name__iendswith='IBU'`
* **exact** - exact match to value, sql `column = <VALUE>`
* can be written as`album__name__exact='Malibu'`
* **iexact** - exact match sql `column = <VALUE>` (case insensitive)
* can be written as`album__name__iexact='malibu'`
* **contains** - sql `column LIKE '%<VALUE>%'`
* can be written as`album__name__contains='Mal'`
* **icontains** - sql `column LIKE '%<VALUE>%'` (case insensitive)
* can be written as`album__name__icontains='mal'`
* **in** - sql ` column IN (<VALUE1>, <VALUE2>, ...)`
* can be written as`album__name__in=['Malibu', 'Barclay']`
* **isnull** - sql `column IS NULL` (and sql `column IS NOT NULL`)
* can be written as`album__name__isnull=True` (isnotnull `album__name__isnull=False`)
* **gt** - sql `column > <VALUE>` (greater than)
* can be written as`position__gt=3`
* **gte** - sql `column >= <VALUE>` (greater or equal than)
* can be written as`position__gte=3`
* **lt** - sql `column < <VALUE>` (lower than)
* can be written as`position__lt=3`
* **lte** - sql `column <= <VALUE>` (lower equal than)
* can be written as`position__lte=3`
* **startswith** - sql `column LIKE '<VALUE>%'` (exact start match)
* can be written as`album__name__startswith='Mal'`
* **istartswith** - sql `column LIKE '<VALUE>%'` (case insensitive)
* can be written as`album__name__istartswith='mal'`
* **endswith** - sql `column LIKE '%<VALUE>'` (exact end match)
* can be written as`album__name__endswith='ibu'`
* **iendswith** - sql `column LIKE '%<VALUE>'` (case insensitive)
* can be written as`album__name__iendswith='IBU'`
Some samples:
@ -116,40 +116,40 @@ Product.objects.filter(
### Python style filters
* exact - exact match to value, sql `column = <VALUE>`
* can be written as `Track.album.name == 'Malibu`
* iexact - exact match sql `column = <VALUE>` (case insensitive)
* can be written as `Track.album.name.iexact('malibu')`
* contains - sql `column LIKE '%<VALUE>%'`
* can be written as `Track.album.name % 'Mal')`
* can be written as `Track.album.name.contains('Mal')`
* icontains - sql `column LIKE '%<VALUE>%'` (case insensitive)
* can be written as `Track.album.name.icontains('mal')`
* in - sql ` column IN (<VALUE1>, <VALUE2>, ...)`
* can be written as `Track.album.name << ['Malibu', 'Barclay']`
* can be written as `Track.album.name.in_(['Malibu', 'Barclay'])`
* isnull - sql `column IS NULL` (and sql `column IS NOT NULL`)
* can be written as `Track.album.name >> None`
* can be written as `Track.album.name.is_null(True)`
* not null can be written as `Track.album.name.is_null(False)`
* not null can be written as `~(Track.album.name >> None)`
* not null can be written as `~(Track.album.name.is_null(True))`
* gt - sql `column > <VALUE>` (greater than)
* can be written as `Track.album.name > 3`
* gte - sql `column >= <VALUE>` (greater or equal than)
* can be written as `Track.album.name >= 3`
* lt - sql `column < <VALUE>` (lower than)
* can be written as `Track.album.name < 3`
* lte - sql `column <= <VALUE>` (lower equal than)
* can be written as `Track.album.name <= 3`
* startswith - sql `column LIKE '<VALUE>%'` (exact start match)
* can be written as `Track.album.name.startswith('Mal')`
* istartswith - sql `column LIKE '<VALUE>%'` (case insensitive)
* can be written as `Track.album.name.istartswith('mal')`
* endswith - sql `column LIKE '%<VALUE>'` (exact end match)
* can be written as `Track.album.name.endswith('ibu')`
* iendswith - sql `column LIKE '%<VALUE>'` (case insensitive)
* can be written as `Track.album.name.iendswith('IBU')`
* **exact** - exact match to value, sql `column = <VALUE>`
* can be written as `Track.album.name == 'Malibu`
* **iexact** - exact match sql `column = <VALUE>` (case insensitive)
* can be written as `Track.album.name.iexact('malibu')`
* **contains** - sql `column LIKE '%<VALUE>%'`
* can be written as `Track.album.name % 'Mal')`
* can be written as `Track.album.name.contains('Mal')`
* **icontains** - sql `column LIKE '%<VALUE>%'` (case insensitive)
* can be written as `Track.album.name.icontains('mal')`
* **in** - sql ` column IN (<VALUE1>, <VALUE2>, ...)`
* can be written as `Track.album.name << ['Malibu', 'Barclay']`
* can be written as `Track.album.name.in_(['Malibu', 'Barclay'])`
* **isnull** - sql `column IS NULL` (and sql `column IS NOT NULL`)
* can be written as `Track.album.name >> None`
* can be written as `Track.album.name.isnull(True)`
* not null can be written as `Track.album.name.isnull(False)`
* not null can be written as `~(Track.album.name >> None)`
* not null can be written as `~(Track.album.name.isnull(True))`
* **gt** - sql `column > <VALUE>` (greater than)
* can be written as `Track.album.name > 3`
* **gte** - sql `column >= <VALUE>` (greater or equal than)
* can be written as `Track.album.name >= 3`
* **lt** - sql `column < <VALUE>` (lower than)
* can be written as `Track.album.name < 3`
* **lte** - sql `column <= <VALUE>` (lower equal than)
* can be written as `Track.album.name <= 3`
* **startswith** - sql `column LIKE '<VALUE>%'` (exact start match)
* can be written as `Track.album.name.startswith('Mal')`
* **istartswith** - sql `column LIKE '<VALUE>%'` (case insensitive)
* can be written as `Track.album.name.istartswith('mal')`
* **endswith** - sql `column LIKE '%<VALUE>'` (exact end match)
* can be written as `Track.album.name.endswith('ibu')`
* **iendswith** - sql `column LIKE '%<VALUE>'` (case insensitive)
* can be written as `Track.album.name.iendswith('IBU')`
Some samples:
@ -291,7 +291,7 @@ Let's select books of Tolkien **OR** books written after 1970
sql:
`WHERE ( authors.name = 'J.R.R. Tolkien' OR books.year > 1970 )`
### Django style
#### Django style
```python
books = (
await Book.objects.select_related("author")
@ -301,7 +301,7 @@ books = (
assert len(books) == 5
```
### Python style
#### Python style
```python
books = (
await Book.objects.select_related("author")
@ -316,7 +316,7 @@ Now let's select books written after 1960 or before 1940 which were written by T
sql:
`WHERE ( books.year > 1960 OR books.year < 1940 ) AND authors.name = 'J.R.R. Tolkien'`
### Django style
#### Django style
```python
# OPTION 1 - split and into separate call
books = (
@ -344,7 +344,7 @@ assert books[0].title == "The Hobbit"
assert books[1].title == "The Silmarillion"
```
### Python style
#### Python style
```python
books = (
await Book.objects.select_related("author")
@ -375,7 +375,7 @@ Books of Sapkowski from before 2000 or books of Tolkien written after 1960
sql:
`WHERE ( ( books.year > 1960 AND authors.name = 'J.R.R. Tolkien' ) OR ( books.year < 2000 AND authors.name = 'Andrzej Sapkowski' ) ) `
### Django style
#### Django style
```python
books = (
await Book.objects.select_related("author")
@ -390,7 +390,7 @@ books = (
assert len(books) == 2
```
### Python style
#### Python style
```python
books = (
await Book.objects.select_related("author")
@ -411,7 +411,7 @@ sql:
( books.year < 2000 AND os0cec_authors.name = 'Andrzej Sapkowski' ) OR
books.title LIKE '%hobbit%' )`
### Django style
#### Django style
```python
books = (
await Book.objects.select_related("author")
@ -426,7 +426,7 @@ books = (
)
```
### Python style
#### Python style
```python
books = (
await Book.objects.select_related("author")
@ -451,7 +451,7 @@ AND authors.name = 'J.R.R. Tolkien' ) OR
You can construct a query as follows:
### Django style
#### Django style
```python
books = (
await Book.objects.select_related("author")
@ -472,16 +472,21 @@ assert books[1].title == "The Silmarillion"
assert books[2].title == "The Witcher"
```
#### Python style
```python
books = (
await Book.objects.select_related("author")
.filter(
ormar.or_(
ormar.and_(
ormar.or_(year__gt=1960, year__lt=1940),
author__name="J.R.R. Tolkien",
),
ormar.and_(year__lt=2000, author__name="Andrzej Sapkowski"),
(
(
(Book.year > 1960) |
(Book.year < 1940)
) &
(Book.author.name == "J.R.R. Tolkien")
) |
(
(Book.year < 2000) &
(Book.author.name == "Andrzej Sapkowski")
)
)
.all()
@ -512,7 +517,7 @@ assert books[0].title == "The Witcher"
Same applies to python style chaining and nesting.
### Django style
#### Django style
Note that with django style you cannot provide the same keyword argument several times so queries like `filter(ormar.or_(name='Jack', name='John'))` are not allowed. If you want to check the same
column for several values simply use `in` operator: `filter(name__in=['Jack','John'])`.
@ -559,7 +564,7 @@ books = (
assert len(books) == 5
```
### Python style
#### Python style
Note that with python style you can perfectly use the same fields as many times as you want.
@ -721,7 +726,7 @@ Given sample Models like following:
To order by main model field just provide a field name
### Django style
#### Django style
```python
toys = await Toy.objects.select_related("owner").order_by("name").all()
assert [x.name.replace("Toy ", "") for x in toys] == [
@ -731,7 +736,7 @@ assert toys[0].owner == zeus
assert toys[1].owner == aphrodite
```
### Python style
#### Python style
```python
toys = await Toy.objects.select_related("owner").order_by(Toy.name.asc()).all()
assert [x.name.replace("Toy ", "") for x in toys] == [
@ -747,7 +752,7 @@ To sort on nested models separate field names with dunder '__'.
You can sort this way across all relation types -> `ForeignKey`, reverse virtual FK
and `ManyToMany` fields.
### Django style
#### Django style
```python
toys = await Toy.objects.select_related("owner").order_by("owner__name").all()
assert toys[0].owner.name == toys[1].owner.name == "Aphrodite"
@ -755,7 +760,7 @@ assert toys[2].owner.name == toys[3].owner.name == "Hermes"
assert toys[4].owner.name == toys[5].owner.name == "Zeus"
```
### Python style
#### Python style
```python
toys = await Toy.objects.select_related("owner").order_by(Toy.owner.name.asc()).all()
assert toys[0].owner.name == toys[1].owner.name == "Aphrodite"
@ -765,7 +770,7 @@ assert toys[4].owner.name == toys[5].owner.name == "Zeus"
To sort in descending order provide a hyphen in front of the field name
### Django style
#### Django style
```python
owner = (
await Owner.objects.select_related("toys")
@ -777,7 +782,7 @@ assert owner.toys[0].name == "Toy 4"
assert owner.toys[1].name == "Toy 1"
```
### Python style
#### Python style
```python
owner = (
await Owner.objects.select_related("toys")

View File

@ -25,10 +25,10 @@
* isnull - sql `column IS NULL` (and sql `column IS NOT NULL`)
* OLD: `album__name__isnull=True` (isnotnull `album__name__isnull=False`)
* NEW: can be also written as `Track.album.name >> None`
* NEW: can be also written as `Track.album.name.is_null(True)`
* NEW: not null can be also written as `Track.album.name.is_null(False)`
* NEW: can be also written as `Track.album.name.isnull(True)`
* NEW: not null can be also written as `Track.album.name.isnull(False)`
* NEW: not null can be also written as `~(Track.album.name >> None)`
* NEW: not null can be also written as `~(Track.album.name.is_null(True))`
* NEW: not null can be also written as `~(Track.album.name.isnull(True))`
* gt - sql `column > <VALUE>` (greater than)
* OLD: `position__gt=3`
* NEW: can be also written as `Track.album.name > 3`

View File

@ -77,6 +77,7 @@ nav:
- Order Query: api/query-set/order-query.md
- Limit Query: api/query-set/limit-query.md
- Offset Query: api/query-set/offset-query.md
- Field Accessor: api/query-set/field-accessor.md
- api/query-set/utils.md
- Relations:
- Relation Manager: api/relations/relation-manager.md

View File

@ -9,6 +9,11 @@ if TYPE_CHECKING: # pragma: no cover
class FieldAccessor:
"""
Helper to access ormar fields directly from Model class also for nested
models attributes.
"""
def __init__(
self,
source_model: Type["Model"],
@ -22,10 +27,24 @@ class FieldAccessor:
self._access_chain = access_chain
def __bool__(self) -> bool:
# hack to avoid pydantic name check from parent model
"""
Hack to avoid pydantic name check from parent model, returns false
:return: False
:rtype: bool
"""
return False
def __getattr__(self, item: str) -> Any:
"""
Accessor return new accessor for each field and nested models.
Thanks to that operator overload is possible to use in filter.
:param item: attribute name
:type item: str
:return: FieldAccessor for field or nested model
:rtype: ormar.queryset.field_accessor.FieldAccessor
"""
if self._field and item == self._field.name:
return self._field
@ -57,60 +76,208 @@ class FieldAccessor:
return FilterGroup(**filter_kwg)
def __eq__(self, other: Any) -> FilterGroup: # type: ignore
"""
overloaded to work as sql `column = <VALUE>`
:param other: value to check agains operator
:type other: str
:return: FilterGroup for operator
:rtype: ormar.queryset.clause.FilterGroup
"""
return self._select_operator(op="__eq__", other=other)
def __ge__(self, other: Any) -> FilterGroup:
"""
overloaded to work as sql `column >= <VALUE>`
:param other: value to check agains operator
:type other: str
:return: FilterGroup for operator
:rtype: ormar.queryset.clause.FilterGroup
"""
return self._select_operator(op="__ge__", other=other)
def __gt__(self, other: Any) -> FilterGroup:
"""
overloaded to work as sql `column > <VALUE>`
:param other: value to check agains operator
:type other: str
:return: FilterGroup for operator
:rtype: ormar.queryset.clause.FilterGroup
"""
return self._select_operator(op="__gt__", other=other)
def __le__(self, other: Any) -> FilterGroup:
"""
overloaded to work as sql `column <= <VALUE>`
:param other: value to check agains operator
:type other: str
:return: FilterGroup for operator
:rtype: ormar.queryset.clause.FilterGroup
"""
return self._select_operator(op="__le__", other=other)
def __lt__(self, other: Any) -> FilterGroup:
"""
overloaded to work as sql `column < <VALUE>`
:param other: value to check agains operator
:type other: str
:return: FilterGroup for operator
:rtype: ormar.queryset.clause.FilterGroup
"""
return self._select_operator(op="__lt__", other=other)
def __mod__(self, other: Any) -> FilterGroup:
"""
overloaded to work as sql `column LIKE '%<VALUE>%'`
:param other: value to check agains operator
:type other: str
:return: FilterGroup for operator
:rtype: ormar.queryset.clause.FilterGroup
"""
return self._select_operator(op="__mod__", other=other)
def __lshift__(self, other: Any) -> FilterGroup:
"""
overloaded to work as sql `column IN (<VALUE1>, <VALUE2>,...)`
:param other: value to check agains operator
:type other: str
:return: FilterGroup for operator
:rtype: ormar.queryset.clause.FilterGroup
"""
return self._select_operator(op="in", other=other)
def __rshift__(self, other: Any) -> FilterGroup:
"""
overloaded to work as sql `column IS NULL`
:param other: value to check agains operator
:type other: str
:return: FilterGroup for operator
:rtype: ormar.queryset.clause.FilterGroup
"""
return self._select_operator(op="isnull", other=True)
def in_(self, other: Any) -> FilterGroup:
"""
works as sql `column IN (<VALUE1>, <VALUE2>,...)`
:param other: value to check agains operator
:type other: str
:return: FilterGroup for operator
:rtype: ormar.queryset.clause.FilterGroup
"""
return self._select_operator(op="in", other=other)
def iexact(self, other: Any) -> FilterGroup:
"""
works as sql `column = <VALUE>` case-insensitive
:param other: value to check agains operator
:type other: str
:return: FilterGroup for operator
:rtype: ormar.queryset.clause.FilterGroup
"""
return self._select_operator(op="iexact", other=other)
def contains(self, other: Any) -> FilterGroup:
"""
works as sql `column LIKE '%<VALUE>%'`
:param other: value to check agains operator
:type other: str
:return: FilterGroup for operator
:rtype: ormar.queryset.clause.FilterGroup
"""
return self._select_operator(op="contains", other=other)
def icontains(self, other: Any) -> FilterGroup:
"""
works as sql `column LIKE '%<VALUE>%'` case-insensitive
:param other: value to check agains operator
:type other: str
:return: FilterGroup for operator
:rtype: ormar.queryset.clause.FilterGroup
"""
return self._select_operator(op="icontains", other=other)
def startswith(self, other: Any) -> FilterGroup:
"""
works as sql `column LIKE '<VALUE>%'`
:param other: value to check agains operator
:type other: str
:return: FilterGroup for operator
:rtype: ormar.queryset.clause.FilterGroup
"""
return self._select_operator(op="startswith", other=other)
def istartswith(self, other: Any) -> FilterGroup:
"""
works as sql `column LIKE '%<VALUE>'` case-insensitive
:param other: value to check agains operator
:type other: str
:return: FilterGroup for operator
:rtype: ormar.queryset.clause.FilterGroup
"""
return self._select_operator(op="istartswith", other=other)
def endswith(self, other: Any) -> FilterGroup:
"""
works as sql `column LIKE '%<VALUE>'`
:param other: value to check agains operator
:type other: str
:return: FilterGroup for operator
:rtype: ormar.queryset.clause.FilterGroup
"""
return self._select_operator(op="endswith", other=other)
def iendswith(self, other: Any) -> FilterGroup:
"""
works as sql `column LIKE '%<VALUE>'` case-insensitive
:param other: value to check agains operator
:type other: str
:return: FilterGroup for operator
:rtype: ormar.queryset.clause.FilterGroup
"""
return self._select_operator(op="iendswith", other=other)
def isnull(self, other: Any) -> FilterGroup:
"""
works as sql `column IS NULL` or `IS NOT NULL`
:param other: value to check agains operator
:type other: str
:return: FilterGroup for operator
:rtype: ormar.queryset.clause.FilterGroup
"""
return self._select_operator(op="isnull", other=other)
def asc(self) -> OrderAction:
"""
works as sql `column asc`
:return: OrderGroup for operator
:rtype: ormar.queryset.actions.OrderGroup
"""
return OrderAction(order_str=self._access_chain, model_cls=self._source_model)
def desc(self) -> OrderAction:
"""
works as sql `column desc`
:return: OrderGroup for operator
:rtype: ormar.queryset.actions.OrderGroup
"""
return OrderAction(
order_str="-" + self._access_chain, model_cls=self._source_model
)

View File

@ -122,6 +122,9 @@ renderer:
- title: Offset Query
contents:
- queryset.offset_query.*
- title: Field accessor
contents:
- queryset.field_accessor.*
- title: Utils
contents:
- queryset.utils.*