add tests for cross model forward references, add docs for processing forwardrefs, wip on refactoring queries into separate pages based on functionality

This commit is contained in:
collerek
2021-01-26 17:29:40 +01:00
parent a2834666fc
commit b710ed9780
39 changed files with 2054 additions and 1004 deletions

View File

@ -8,13 +8,13 @@
class QueryClause()
```
Constructs where clauses from strings passed as arguments
Constructs FilterActions from strings passed as arguments
<a name="queryset.clause.QueryClause.filter"></a>
#### filter
<a name="queryset.clause.QueryClause.prepare_filter"></a>
#### prepare\_filter
```python
| filter(**kwargs: Any) -> Tuple[List[sqlalchemy.sql.expression.TextClause], List[str]]
| prepare_filter(**kwargs: Any) -> Tuple[List[FilterAction], List[str]]
```
Main external access point that processes the clauses into sqlalchemy text
@ -33,7 +33,7 @@ mentioned in select_related strings but not included in select_related.
#### \_populate\_filter\_clauses
```python
| _populate_filter_clauses(**kwargs: Any) -> Tuple[List[sqlalchemy.sql.expression.TextClause], List[str]]
| _populate_filter_clauses(**kwargs: Any) -> Tuple[List[FilterAction], List[str]]
```
Iterates all clauses and extracts used operator and field from related
@ -48,114 +48,59 @@ is determined and the final clause is escaped if needed and compiled.
`(Tuple[List[sqlalchemy.sql.elements.TextClause], List[str]])`: Tuple with list of where clauses and updated select_related list
<a name="queryset.clause.QueryClause._process_column_clause_for_operator_and_value"></a>
#### \_process\_column\_clause\_for\_operator\_and\_value
<a name="queryset.clause.QueryClause._register_complex_duplicates"></a>
#### \_register\_complex\_duplicates
```python
| _process_column_clause_for_operator_and_value(value: Any, op: str, column: sqlalchemy.Column, table: sqlalchemy.Table, table_prefix: str) -> sqlalchemy.sql.expression.TextClause
| _register_complex_duplicates(select_related: List[str]) -> None
```
Escapes characters if it's required.
Substitutes values of the models if value is a ormar Model with its pk value.
Compiles the clause.
Checks if duplicate aliases are presented which can happen in self relation
or when two joins end with the same pair of models.
If there are duplicates, the all duplicated joins are registered as source
model and whole relation key (not just last relation name).
**Arguments**:
- `value (Any)`: value of the filter
- `op (str)`: filter operator
- `column (sqlalchemy.sql.schema.Column)`: column on which filter should be applied
- `table (sqlalchemy.sql.schema.Table)`: table on which filter should be applied
- `table_prefix (str)`: prefix from AliasManager
- `select_related (List[str])`: list of relation strings
**Returns**:
`(sqlalchemy.sql.elements.TextClause)`: complied and escaped clause
`(None)`: None
<a name="queryset.clause.QueryClause._determine_filter_target_table"></a>
#### \_determine\_filter\_target\_table
<a name="queryset.clause.QueryClause._parse_related_prefixes"></a>
#### \_parse\_related\_prefixes
```python
| _determine_filter_target_table(related_parts: List[str], select_related: List[str]) -> Tuple[List[str], str, Type["Model"]]
| _parse_related_prefixes(select_related: List[str]) -> List[Prefix]
```
Adds related strings to select_related list otherwise the clause would fail as
the required columns would not be present. That means that select_related
list is filled with missing values present in filters.
Walks the relation to retrieve the actual model on which the clause should be
constructed, extracts alias based on last relation leading to target model.
Walks all relation strings and parses the target models and prefixes.
**Arguments**:
- `related_parts (List[str])`: list of split parts of related string
- `select_related (List[str])`: list of related models
- `select_related (List[str])`: list of relation strings
**Returns**:
`(Tuple[List[str], str, Type[Model]])`: list of related models, table_prefix, final model class
`(List[Prefix])`: list of parsed prefixes
<a name="queryset.clause.QueryClause._compile_clause"></a>
#### \_compile\_clause
<a name="queryset.clause.QueryClause._switch_filter_action_prefixes"></a>
#### \_switch\_filter\_action\_prefixes
```python
| _compile_clause(clause: sqlalchemy.sql.expression.BinaryExpression, column: sqlalchemy.Column, table: sqlalchemy.Table, table_prefix: str, modifiers: Dict) -> sqlalchemy.sql.expression.TextClause
| _switch_filter_action_prefixes(filter_clauses: List[FilterAction]) -> List[FilterAction]
```
Compiles the clause to str using appropriate database dialect, replace columns
names with aliased names and converts it back to TextClause.
Substitutes aliases for filter action if the complex key (whole relation str) is
present in alias_manager.
**Arguments**:
- `clause (sqlalchemy.sql.elements.BinaryExpression)`: original not compiled clause
- `column (sqlalchemy.sql.schema.Column)`: column on which filter should be applied
- `table (sqlalchemy.sql.schema.Table)`: table on which filter should be applied
- `table_prefix (str)`: prefix from AliasManager
- `modifiers (Dict[str, NoneType])`: sqlalchemy modifiers - used only to escape chars here
- `filter_clauses (List[FilterAction])`: raw list of actions
**Returns**:
`(sqlalchemy.sql.elements.TextClause)`: compiled and escaped clause
<a name="queryset.clause.QueryClause._escape_characters_in_clause"></a>
#### \_escape\_characters\_in\_clause
```python
| @staticmethod
| _escape_characters_in_clause(op: str, value: Any) -> Tuple[Any, bool]
```
Escapes the special characters ["%", "_"] if needed.
Adds `%` for `like` queries.
**Raises**:
- `QueryDefinitionError`: if contains or icontains is used with
ormar model instance
**Arguments**:
- `op (str)`: operator used in query
- `value (Any)`: value of the filter
**Returns**:
`(Tuple[Any, bool])`: escaped value and flag if escaping is needed
<a name="queryset.clause.QueryClause._extract_operator_field_and_related"></a>
#### \_extract\_operator\_field\_and\_related
```python
| @staticmethod
| _extract_operator_field_and_related(parts: List[str]) -> Tuple[str, str, Optional[List]]
```
Splits filter query key and extracts required parts.
**Arguments**:
- `parts (List[str])`: split filter query key
**Returns**:
`(Tuple[str, str, Optional[List]])`: operator, field_name, list of related parts
`(List[FilterAction])`: list of actions with aliases changed if needed