wip with m2m fields

This commit is contained in:
collerek
2021-02-28 08:19:02 +01:00
parent ad9d065c6d
commit fd38ae2a40
7 changed files with 231 additions and 304 deletions

View File

@ -12,6 +12,20 @@ class Excludable:
include: Set = field(default_factory=set)
exclude: Set = field(default_factory=set)
@property
def include_all(self):
return ... in self.include
@property
def exclude_all(self):
return ... in self.exclude
def get_copy(self) -> "Excludable":
_copy = self.__class__()
_copy.include = {x for x in self.include}
_copy.exclude = {x for x in self.exclude}
return _copy
def set_values(self, value: Set, is_exclude: bool) -> None:
prop = "exclude" if is_exclude else "include"
if ... in getattr(self, prop) or ... in value:
@ -38,6 +52,13 @@ class ExcludableItems:
def __init__(self) -> None:
self.items: Dict[str, Excludable] = dict()
@classmethod
def from_excludable(cls, other: "ExcludableItems") -> "ExcludableItems":
new_excludable = cls()
for key, value in other.items.items():
new_excludable.items[key] = value.get_copy()
return new_excludable
def get(self, model_cls: Type["Model"], alias: str = "") -> Excludable:
key = f"{alias + '_' if alias else ''}{model_cls.get_name(lower=True)}"
return self.items.get(key, Excludable())

View File

@ -9,9 +9,10 @@ from typing import (
TYPE_CHECKING,
Type,
TypeVar,
Union,
Union, cast,
)
from ormar.models.excludable import ExcludableItems
from ormar.models.mixins.relation_mixin import RelationMixin
from ormar.queryset.utils import translate_list_to_dict, update
@ -161,10 +162,11 @@ class ExcludableMixin(RelationMixin):
def own_table_columns(
cls,
model: Union[Type["Model"], Type["ModelRow"]],
fields: Optional[Union[Set, Dict]],
exclude_fields: Optional[Union[Set, Dict]],
excludable: ExcludableItems,
alias: str = '',
use_alias: bool = False,
) -> List[str]:
# TODO update docstring
"""
Returns list of aliases or field names for given model.
Aliases/names switch is use_alias flag.
@ -176,15 +178,12 @@ class ExcludableMixin(RelationMixin):
:param model: model on columns are selected
:type model: Type["Model"]
:param fields: set/dict of fields to include
:type fields: Optional[Union[Set, Dict]]
:param exclude_fields: set/dict of fields to exclude
:type exclude_fields: Optional[Union[Set, Dict]]
:param use_alias: flag if aliases or field names should be used
:type use_alias: bool
:return: list of column field names or aliases
:rtype: List[str]
"""
model_excludable = excludable.get(model_cls=model, alias=alias)
columns = [
model.get_column_name_from_alias(col.name) if not use_alias else col.name
for col in model.Meta.table.columns
@ -193,17 +192,17 @@ class ExcludableMixin(RelationMixin):
model.get_column_name_from_alias(col.name)
for col in model.Meta.table.columns
]
if fields:
if model_excludable.include:
columns = [
col
for col, name in zip(columns, field_names)
if model.is_included(fields, name)
if model_excludable.is_included(name)
]
if exclude_fields:
if model_excludable.exclude:
columns = [
col
for col, name in zip(columns, field_names)
if not model.is_excluded(exclude_fields, name)
if not model_excludable.is_excluded(name)
]
# always has to return pk column for ormar to work
@ -246,8 +245,8 @@ class ExcludableMixin(RelationMixin):
@classmethod
def get_names_to_exclude(
cls,
fields: Optional[Union[Dict, Set]] = None,
exclude_fields: Optional[Union[Dict, Set]] = None,
excludable: ExcludableItems,
alias: str
) -> Set:
"""
Returns a set of models field names that should be explicitly excluded
@ -259,33 +258,27 @@ class ExcludableMixin(RelationMixin):
Used in parsing data from database rows that construct Models by initializing
them with dicts constructed from those db rows.
:param fields: set/dict of fields to include
:type fields: Optional[Union[Set, Dict]]
:param exclude_fields: set/dict of fields to exclude
:type exclude_fields: Optional[Union[Set, Dict]]
:param alias: alias of current relation
:type alias: str
:param excludable: structure of fields to include and exclude
:type excludable: ExcludableItems
:return: set of field names that should be excluded
:rtype: Set
"""
model = cast(Type["Model"], cls)
model_excludable = excludable.get(model_cls=model, alias=alias)
fields_names = cls.extract_db_own_fields()
if fields and fields is not Ellipsis:
fields_to_keep = {name for name in fields if name in fields_names}
if model_excludable.include and model_excludable.include_all:
fields_to_keep = model_excludable.include.intersection(fields_names)
else:
fields_to_keep = fields_names
fields_to_exclude = fields_names - fields_to_keep
if isinstance(exclude_fields, Set):
if model_excludable.exclude:
fields_to_exclude = fields_to_exclude.union(
{name for name in exclude_fields if name in fields_names}
model_excludable.exclude.intersection(fields_names)
)
elif isinstance(exclude_fields, Dict):
new_to_exclude = {
name
for name in exclude_fields
if name in fields_names and exclude_fields[name] is Ellipsis
}
fields_to_exclude = fields_to_exclude.union(new_to_exclude)
fields_to_exclude = fields_to_exclude - {cls.Meta.pkname}
return fields_to_exclude

View File

@ -14,6 +14,7 @@ from typing import (
import sqlalchemy
from ormar.models import NewBaseModel # noqa: I202
from ormar.models.excludable import ExcludableItems
from ormar.models.helpers.models import group_related_list
@ -33,8 +34,7 @@ class ModelRow(NewBaseModel):
select_related: List = None,
related_models: Any = None,
related_field: Type["ForeignKeyField"] = None,
fields: Optional[Union[Dict, Set]] = None,
exclude_fields: Optional[Union[Dict, Set]] = None,
excludable: ExcludableItems = None,
current_relation_str: str = "",
) -> Optional[T]:
"""
@ -50,6 +50,8 @@ class ModelRow(NewBaseModel):
where rows are populated in a different way as they do not have
nested models in result.
:param excludable: structure of fields to include and exclude
:type excludable: ExcludableItems
:param current_relation_str: name of the relation field
:type current_relation_str: str
:param source_model: model on which relation was defined
@ -62,12 +64,6 @@ class ModelRow(NewBaseModel):
:type related_models: Union[List, Dict]
:param related_field: field with relation declaration
:type related_field: Type[ForeignKeyField]
:param fields: fields and related model fields to include
if provided only those are included
:type fields: Optional[Union[Dict, Set]]
:param exclude_fields: fields and related model fields to exclude
excludes the fields even if they are provided in fields
:type exclude_fields: Optional[Union[Dict, Set]]
:return: returns model if model is populated from database
:rtype: Optional[Model]
"""
@ -75,6 +71,7 @@ class ModelRow(NewBaseModel):
select_related = select_related or []
related_models = related_models or []
table_prefix = ""
excludable = excludable or ExcludableItems()
if select_related:
source_model = cast(Type[T], cls)
@ -87,12 +84,11 @@ class ModelRow(NewBaseModel):
relation_field=related_field,
)
item = cls.populate_nested_models_from_row(
item = cls._populate_nested_models_from_row(
item=item,
row=row,
related_models=related_models,
fields=fields,
exclude_fields=exclude_fields,
excludable=excludable,
current_relation_str=current_relation_str,
source_model=source_model,
)
@ -100,28 +96,26 @@ class ModelRow(NewBaseModel):
item=item,
row=row,
table_prefix=table_prefix,
fields=fields,
exclude_fields=exclude_fields,
excludable=excludable
)
instance: Optional[T] = None
if item.get(cls.Meta.pkname, None) is not None:
item["__excluded__"] = cls.get_names_to_exclude(
fields=fields, exclude_fields=exclude_fields
excludable=excludable, alias=table_prefix
)
instance = cast(T, cls(**item))
instance.set_save_status(True)
return instance
@classmethod
def populate_nested_models_from_row( # noqa: CFQ002
def _populate_nested_models_from_row( # noqa: CFQ002
cls,
item: dict,
row: sqlalchemy.engine.ResultProxy,
source_model: Type[T],
related_models: Any,
fields: Optional[Union[Dict, Set]] = None,
exclude_fields: Optional[Union[Dict, Set]] = None,
excludable: ExcludableItems,
current_relation_str: str = None,
) -> dict:
"""
@ -134,6 +128,8 @@ class ModelRow(NewBaseModel):
Recurrently calls from_row method on nested instances and create nested
instances. In the end those instances are added to the final model dictionary.
:param excludable: structure of fields to include and exclude
:type excludable: ExcludableItems
:param source_model: source model from which relation started
:type source_model: Type[Model]
:param current_relation_str: joined related parts into one string
@ -144,12 +140,6 @@ class ModelRow(NewBaseModel):
:type row: sqlalchemy.engine.result.ResultProxy
:param related_models: list or dict of related models
:type related_models: Union[Dict, List]
:param fields: fields and related model fields to include -
if provided only those are included
:type fields: Optional[Union[Dict, Set]]
:param exclude_fields: fields and related model fields to exclude
excludes the fields even if they are provided in fields
:type exclude_fields: Optional[Union[Dict, Set]]
:return: dictionary with keys corresponding to model fields names
and values are database values
:rtype: Dict
@ -163,8 +153,6 @@ class ModelRow(NewBaseModel):
)
field = cls.Meta.model_fields[related]
field = cast(Type["ForeignKeyField"], field)
fields = cls.get_included(fields, related)
exclude_fields = cls.get_excluded(exclude_fields, related)
model_cls = field.to
remainder = None
@ -174,8 +162,7 @@ class ModelRow(NewBaseModel):
row,
related_models=remainder,
related_field=field,
fields=fields,
exclude_fields=exclude_fields,
excludable=excludable,
current_relation_str=relation_str,
source_model=source_model,
)
@ -188,8 +175,7 @@ class ModelRow(NewBaseModel):
row=row,
related=related,
through_name=through_name,
fields=fields,
exclude_fields=exclude_fields,
excludable=excludable
)
item[through_name] = through_child
setattr(child, through_name, through_child)
@ -203,35 +189,29 @@ class ModelRow(NewBaseModel):
row: sqlalchemy.engine.ResultProxy,
through_name: str,
related: str,
fields: Optional[Union[Dict, Set]] = None,
exclude_fields: Optional[Union[Dict, Set]] = None,
excludable: ExcludableItems
) -> Dict:
# TODO: fix excludes and includes
fields = cls.get_included(fields, through_name)
# exclude_fields = cls.get_excluded(exclude_fields, through_name)
# TODO: fix excludes and includes and docstring
model_cls = cls.Meta.model_fields[through_name].to
exclude_fields = model_cls.extract_related_names()
table_prefix = cls.Meta.alias_manager.resolve_relation_alias(
from_model=cls, relation_name=related
)
child = model_cls.extract_prefixed_table_columns(
item={},
row=row,
table_prefix=table_prefix,
fields=fields,
exclude_fields=exclude_fields,
excludable=excludable,
table_prefix=table_prefix
)
return child
@classmethod
def extract_prefixed_table_columns( # noqa CCR001
def extract_prefixed_table_columns(
cls,
item: dict,
row: sqlalchemy.engine.result.ResultProxy,
table_prefix: str,
fields: Optional[Union[Dict, Set]] = None,
exclude_fields: Optional[Union[Dict, Set]] = None,
) -> dict:
excludable: ExcludableItems
) -> Dict:
"""
Extracts own fields from raw sql result, using a given prefix.
Prefix changes depending on the table's position in a join.
@ -244,6 +224,8 @@ class ModelRow(NewBaseModel):
Used in Model.from_row and PrefetchQuery._populate_rows methods.
:param excludable: structure of fields to include and exclude
:type excludable: ExcludableItems
:param item: dictionary of already populated nested models, otherwise empty dict
:type item: Dict
:param row: raw result row from the database
@ -252,12 +234,6 @@ class ModelRow(NewBaseModel):
each pair of tables have own prefix (two of them depending on direction) -
used in joins to allow multiple joins to the same table.
:type table_prefix: str
:param fields: fields and related model fields to include -
if provided only those are included
:type fields: Optional[Union[Dict, Set]]
:param exclude_fields: fields and related model fields to exclude
excludes the fields even if they are provided in fields
:type exclude_fields: Optional[Union[Dict, Set]]
:return: dictionary with keys corresponding to model fields names
and values are database values
:rtype: Dict
@ -267,8 +243,8 @@ class ModelRow(NewBaseModel):
selected_columns = cls.own_table_columns(
model=cls,
fields=fields or {},
exclude_fields=exclude_fields or {},
excludable=excludable,
alias=table_prefix,
use_alias=False,
)

View File

@ -16,6 +16,7 @@ from sqlalchemy import text
import ormar # noqa I100
from ormar.exceptions import RelationshipInstanceError
from ormar.models.excludable import ExcludableItems
from ormar.relations import AliasManager
if TYPE_CHECKING: # pragma no cover
@ -29,8 +30,7 @@ class SqlJoin:
used_aliases: List,
select_from: sqlalchemy.sql.select,
columns: List[sqlalchemy.Column],
fields: Optional[Union[Set, Dict]],
exclude_fields: Optional[Union[Set, Dict]],
excludable: ExcludableItems,
order_columns: Optional[List["OrderAction"]],
sorted_orders: OrderedDict,
main_model: Type["Model"],
@ -44,8 +44,7 @@ class SqlJoin:
self.related_models = related_models or []
self.select_from = select_from
self.columns = columns
self.fields = fields
self.exclude_fields = exclude_fields
self.excludable=excludable
self.order_columns = order_columns
self.sorted_orders = sorted_orders
self.main_model = main_model
@ -200,10 +199,7 @@ class SqlJoin:
used_aliases=self.used_aliases,
select_from=self.select_from,
columns=self.columns,
fields=self.main_model.get_excluded(self.fields, related_name),
exclude_fields=self.main_model.get_excluded(
self.exclude_fields, related_name
),
excludable=self.excludable,
order_columns=self.order_columns,
sorted_orders=self.sorted_orders,
main_model=self.next_model,
@ -303,8 +299,8 @@ class SqlJoin:
# TODO: fix fields and exclusions for through model?
self_related_fields = self.next_model.own_table_columns(
model=self.next_model,
fields=self.fields,
exclude_fields=self.exclude_fields,
excludable=self.excludable,
alias=self.next_alias,
use_alias=True,
)
self.columns.extend(

View File

@ -13,6 +13,7 @@ from typing import (
)
import ormar
from ormar.models.excludable import ExcludableItems
from ormar.queryset.clause import QueryClause
from ormar.queryset.query import Query
from ormar.queryset.utils import extract_models_to_dict_of_lists, translate_list_to_dict
@ -125,8 +126,7 @@ class PrefetchQuery:
def __init__( # noqa: CFQ002
self,
model_cls: Type["Model"],
fields: Optional[Union[Dict, Set]],
exclude_fields: Optional[Union[Dict, Set]],
excludable: ExcludableItems,
prefetch_related: List,
select_related: List,
orders_by: List["OrderAction"],
@ -136,8 +136,7 @@ class PrefetchQuery:
self.database = self.model.Meta.database
self._prefetch_related = prefetch_related
self._select_related = select_related
self._exclude_columns = exclude_fields
self._columns = fields
self.excludable = excludable
self.already_extracted: Dict = dict()
self.models: Dict = {}
self.select_dict = translate_list_to_dict(self._select_related)
@ -366,8 +365,6 @@ class PrefetchQuery:
select_dict = translate_list_to_dict(self._select_related)
prefetch_dict = translate_list_to_dict(self._prefetch_related)
target_model = self.model
fields = self._columns
exclude_fields = self._exclude_columns
orders_by = self.order_dict
for related in prefetch_dict.keys():
await self._extract_related_models(
@ -375,8 +372,7 @@ class PrefetchQuery:
target_model=target_model,
prefetch_dict=prefetch_dict.get(related, {}),
select_dict=select_dict.get(related, {}),
fields=fields,
exclude_fields=exclude_fields,
excludable=self.excludable,
orders_by=orders_by.get(related, {}),
)
final_models = []
@ -394,8 +390,7 @@ class PrefetchQuery:
target_model: Type["Model"],
prefetch_dict: Dict,
select_dict: Dict,
fields: Union[Set[Any], Dict[Any, Any], None],
exclude_fields: Union[Set[Any], Dict[Any, Any], None],
excludable: ExcludableItems,
orders_by: Dict,
) -> None:
"""
@ -424,8 +419,6 @@ class PrefetchQuery:
:return: None
:rtype: None
"""
fields = target_model.get_included(fields, related)
exclude_fields = target_model.get_excluded(exclude_fields, related)
target_field = target_model.Meta.model_fields[related]
target_field = cast(Type["ForeignKeyField"], target_field)
reverse = False
@ -450,14 +443,11 @@ class PrefetchQuery:
related_field_name = parent_model.get_related_field_name(
target_field=target_field
)
fields = add_relation_field_to_fields(
fields=fields, related_field_name=related_field_name
)
table_prefix, rows = await self._run_prefetch_query(
target_field=target_field,
fields=fields,
exclude_fields=exclude_fields,
excludable=excludable,
filter_clauses=filter_clauses,
related_field_name=related_field_name
)
else:
rows = []
@ -472,8 +462,7 @@ class PrefetchQuery:
select_dict=self._get_select_related_if_apply(
subrelated, select_dict
),
fields=fields,
exclude_fields=exclude_fields,
excludable=excludable,
orders_by=self._get_select_related_if_apply(subrelated, orders_by),
)
@ -483,8 +472,7 @@ class PrefetchQuery:
parent_model=parent_model,
target_field=target_field,
table_prefix=table_prefix,
fields=fields,
exclude_fields=exclude_fields,
excludable=excludable,
prefetch_dict=prefetch_dict,
orders_by=orders_by,
)
@ -498,9 +486,9 @@ class PrefetchQuery:
async def _run_prefetch_query(
self,
target_field: Type["BaseField"],
fields: Union[Set[Any], Dict[Any, Any], None],
exclude_fields: Union[Set[Any], Dict[Any, Any], None],
excludable: ExcludableItems,
filter_clauses: List,
related_field_name: str
) -> Tuple[str, List]:
"""
Actually runs the queries against the database and populates the raw response
@ -511,10 +499,6 @@ class PrefetchQuery:
:param target_field: ormar field with relation definition
:type target_field: Type["BaseField"]
:param fields: fields to include
:type fields: Union[Set[Any], Dict[Any, Any], None]
:param exclude_fields: fields to exclude
:type exclude_fields: Union[Set[Any], Dict[Any, Any], None]
:param filter_clauses: list of clauses, actually one clause with ids of relation
:type filter_clauses: List[sqlalchemy.sql.elements.TextClause]
:return: table prefix and raw rows from sql response
@ -533,6 +517,11 @@ class PrefetchQuery:
)
self.already_extracted.setdefault(target_name, {})["prefix"] = table_prefix
model_excludable = excludable.get(model_cls=target_model, alias=table_prefix)
if model_excludable.include and not model_excludable.is_included(
related_field_name):
model_excludable.set_values({related_field_name}, is_exclude=False)
qry = Query(
model_cls=query_target,
select_related=select_related,
@ -540,8 +529,7 @@ class PrefetchQuery:
exclude_clauses=[],
offset=None,
limit_count=None,
fields=fields,
exclude_fields=exclude_fields,
excludable=excludable,
order_bys=None,
limit_raw_sql=False,
)
@ -595,8 +583,7 @@ class PrefetchQuery:
target_field: Type["ForeignKeyField"],
parent_model: Type["Model"],
table_prefix: str,
fields: Union[Set[Any], Dict[Any, Any], None],
exclude_fields: Union[Set[Any], Dict[Any, Any], None],
excludable: ExcludableItems,
prefetch_dict: Dict,
orders_by: Dict,
) -> None:
@ -610,6 +597,8 @@ class PrefetchQuery:
already_extracted dictionary. Later those instances will be fetched by ids
and set on the parent model after sorting if needed.
:param excludable: structure of fields to include and exclude
:type excludable: ExcludableItems
:param rows: raw sql response from the prefetch query
:type rows: List[sqlalchemy.engine.result.RowProxy]
:param target_field: field with relation definition from parent model
@ -618,10 +607,6 @@ class PrefetchQuery:
:type parent_model: Type[Model]
:param table_prefix: prefix of the target table from current relation
:type table_prefix: str
:param fields: fields to include
:type fields: Union[Set[Any], Dict[Any, Any], None]
:param exclude_fields: fields to exclude
:type exclude_fields: Union[Set[Any], Dict[Any, Any], None]
:param prefetch_dict: dictionaries of related models to prefetch
:type prefetch_dict: Dict
:param orders_by: dictionary of order by clauses by model
@ -629,16 +614,16 @@ class PrefetchQuery:
"""
target_model = target_field.to
for row in rows:
# TODO Fix fields
field_name = parent_model.get_related_field_name(target_field=target_field)
item = target_model.extract_prefixed_table_columns(
item={},
row=row,
table_prefix=table_prefix,
fields=fields,
exclude_fields=exclude_fields,
excludable=excludable,
)
item["__excluded__"] = target_model.get_names_to_exclude(
fields=fields, exclude_fields=exclude_fields
excludable=excludable, alias=table_prefix
)
instance = target_model(**item)
instance = self._populate_nested_related(

View File

@ -6,6 +6,7 @@ import sqlalchemy
from sqlalchemy import text
import ormar # noqa I100
from ormar.models.excludable import ExcludableItems
from ormar.models.helpers.models import group_related_list
from ormar.queryset import FilterQuery, LimitQuery, OffsetQuery, OrderQuery
from ormar.queryset.actions.filter_action import FilterAction
@ -25,8 +26,7 @@ class Query:
select_related: List,
limit_count: Optional[int],
offset: Optional[int],
fields: Optional[Union[Dict, Set]],
exclude_fields: Optional[Union[Dict, Set]],
excludable: ExcludableItems,
order_bys: Optional[List["OrderAction"]],
limit_raw_sql: bool,
) -> None:
@ -35,8 +35,7 @@ class Query:
self._select_related = select_related[:]
self.filter_clauses = filter_clauses[:]
self.exclude_clauses = exclude_clauses[:]
self.fields = copy.deepcopy(fields) if fields else {}
self.exclude_fields = copy.deepcopy(exclude_fields) if exclude_fields else {}
self.excludable = excludable
self.model_cls = model_cls
self.table = self.model_cls.Meta.table
@ -105,8 +104,7 @@ class Query:
"""
self_related_fields = self.model_cls.own_table_columns(
model=self.model_cls,
fields=self.fields,
exclude_fields=self.exclude_fields,
excludable=self.excludable,
use_alias=True,
)
self.columns = self.model_cls.Meta.alias_manager.prefixed_columns(
@ -121,8 +119,6 @@ class Query:
related_models = group_related_list(self._select_related)
for related in related_models:
fields = self.model_cls.get_included(self.fields, related)
exclude_fields = self.model_cls.get_excluded(self.exclude_fields, related)
remainder = None
if isinstance(related_models, dict) and related_models[related]:
remainder = related_models[related]
@ -130,8 +126,7 @@ class Query:
used_aliases=self.used_aliases,
select_from=self.select_from,
columns=self.columns,
fields=fields,
exclude_fields=exclude_fields,
excludable=self.excludable,
order_columns=self.order_columns,
sorted_orders=self.sorted_orders,
main_model=self.model_cls,
@ -231,5 +226,3 @@ class Query:
self.select_from = []
self.columns = []
self.used_aliases = []
self.fields = {}
self.exclude_fields = {}

View File

@ -20,6 +20,7 @@ from sqlalchemy import bindparam
import ormar # noqa I100
from ormar import MultipleMatches, NoMatch
from ormar.exceptions import ModelError, ModelPersistenceError, QueryDefinitionError
from ormar.models.excludable import ExcludableItems
from ormar.queryset import FilterQuery
from ormar.queryset.actions.order_action import OrderAction
from ormar.queryset.clause import QueryClause
@ -48,8 +49,7 @@ class QuerySet(Generic[T]):
select_related: List = None,
limit_count: int = None,
offset: int = None,
columns: Dict = None,
exclude_columns: Dict = None,
excludable: ExcludableItems = None,
order_bys: List = None,
prefetch_related: List = None,
limit_raw_sql: bool = False,
@ -61,8 +61,7 @@ class QuerySet(Generic[T]):
self._prefetch_related = [] if prefetch_related is None else prefetch_related
self.limit_count = limit_count
self.query_offset = offset
self._columns = columns or {}
self._exclude_columns = exclude_columns or {}
self._excludable = excludable or ExcludableItems()
self.order_bys = order_bys or []
self.limit_sql_raw = limit_raw_sql
@ -121,8 +120,7 @@ class QuerySet(Generic[T]):
"""
query = PrefetchQuery(
model_cls=self.model,
fields=self._columns,
exclude_fields=self._exclude_columns,
excludable=self._excludable,
prefetch_related=self._prefetch_related,
select_related=self._select_related,
orders_by=self.order_bys,
@ -142,8 +140,7 @@ class QuerySet(Generic[T]):
self.model.from_row(
row=row,
select_related=self._select_related,
fields=self._columns,
exclude_fields=self._exclude_columns,
excludable=self._excludable,
source_model=self.model,
)
for row in rows
@ -208,8 +205,7 @@ class QuerySet(Generic[T]):
exclude_clauses=self.exclude_clauses,
offset=offset or self.query_offset,
limit_count=limit or self.limit_count,
fields=self._columns,
exclude_fields=self._exclude_columns,
excludable=self._excludable,
order_bys=order_bys or self.order_bys,
limit_raw_sql=self.limit_sql_raw,
)
@ -265,8 +261,7 @@ class QuerySet(Generic[T]):
select_related=select_related,
limit_count=self.limit_count,
offset=self.query_offset,
columns=self._columns,
exclude_columns=self._exclude_columns,
excludable=self._excludable,
order_bys=self.order_bys,
prefetch_related=self._prefetch_related,
limit_raw_sql=self.limit_sql_raw,
@ -321,8 +316,7 @@ class QuerySet(Generic[T]):
select_related=related,
limit_count=self.limit_count,
offset=self.query_offset,
columns=self._columns,
exclude_columns=self._exclude_columns,
excludable=self._excludable,
order_bys=self.order_bys,
prefetch_related=self._prefetch_related,
limit_raw_sql=self.limit_sql_raw,
@ -357,14 +351,14 @@ class QuerySet(Generic[T]):
select_related=self._select_related,
limit_count=self.limit_count,
offset=self.query_offset,
columns=self._columns,
exclude_columns=self._exclude_columns,
excludable=self._excludable,
order_bys=self.order_bys,
prefetch_related=related,
limit_raw_sql=self.limit_sql_raw,
)
def fields(self, columns: Union[List, str, Set, Dict]) -> "QuerySet":
def fields(self, columns: Union[List, str, Set, Dict],
_is_exclude: bool = False) -> "QuerySet":
"""
With `fields()` you can select subset of model columns to limit the data load.
@ -407,15 +401,10 @@ class QuerySet(Generic[T]):
:return: QuerySet
:rtype: QuerySet
"""
if isinstance(columns, str):
columns = [columns]
# TODO: Flatten all excludes into one dict-like structure with alias + model key
current_included = self._columns
if not isinstance(columns, dict):
current_included = update_dict_from_list(current_included, columns)
else:
current_included = update(current_included, columns)
excludable = ExcludableItems.from_excludable(self._excludable)
excludable.build(items=columns,
model_cls=self.model_cls,
is_exclude=_is_exclude)
return self.__class__(
model_cls=self.model,
@ -424,8 +413,7 @@ class QuerySet(Generic[T]):
select_related=self._select_related,
limit_count=self.limit_count,
offset=self.query_offset,
columns=current_included,
exclude_columns=self._exclude_columns,
excludable=excludable,
order_bys=self.order_bys,
prefetch_related=self._prefetch_related,
limit_raw_sql=self.limit_sql_raw,
@ -458,28 +446,7 @@ class QuerySet(Generic[T]):
:return: QuerySet
:rtype: QuerySet
"""
if isinstance(columns, str):
columns = [columns]
current_excluded = self._exclude_columns
if not isinstance(columns, dict):
current_excluded = update_dict_from_list(current_excluded, columns)
else:
current_excluded = update(current_excluded, columns)
return self.__class__(
model_cls=self.model,
filter_clauses=self.filter_clauses,
exclude_clauses=self.exclude_clauses,
select_related=self._select_related,
limit_count=self.limit_count,
offset=self.query_offset,
columns=self._columns,
exclude_columns=current_excluded,
order_bys=self.order_bys,
prefetch_related=self._prefetch_related,
limit_raw_sql=self.limit_sql_raw,
)
return self.fields(columns=columns, _is_exclude=True)
def order_by(self, columns: Union[List, str]) -> "QuerySet":
"""
@ -529,8 +496,7 @@ class QuerySet(Generic[T]):
select_related=self._select_related,
limit_count=self.limit_count,
offset=self.query_offset,
columns=self._columns,
exclude_columns=self._exclude_columns,
excludable=self._excludable,
order_bys=order_bys,
prefetch_related=self._prefetch_related,
limit_raw_sql=self.limit_sql_raw,
@ -642,8 +608,7 @@ class QuerySet(Generic[T]):
select_related=self._select_related,
limit_count=limit_count,
offset=query_offset,
columns=self._columns,
exclude_columns=self._exclude_columns,
excludable=self._excludable,
order_bys=self.order_bys,
prefetch_related=self._prefetch_related,
limit_raw_sql=self.limit_sql_raw,
@ -671,8 +636,7 @@ class QuerySet(Generic[T]):
select_related=self._select_related,
limit_count=limit_count,
offset=self.query_offset,
columns=self._columns,
exclude_columns=self._exclude_columns,
excludable=self._excludable,
order_bys=self.order_bys,
prefetch_related=self._prefetch_related,
limit_raw_sql=limit_raw_sql,
@ -700,8 +664,7 @@ class QuerySet(Generic[T]):
select_related=self._select_related,
limit_count=self.limit_count,
offset=offset,
columns=self._columns,
exclude_columns=self._exclude_columns,
excludable=self._excludable,
order_bys=self.order_bys,
prefetch_related=self._prefetch_related,
limit_raw_sql=limit_raw_sql,