MCPcopy
hub / github.com/ormar-orm/ormar / with_projection_exclusions

Method with_projection_exclusions

ormar/models/excludable.py:740–808  ·  view source on GitHub ↗

Return a copy with relations not referenced by ``fields()`` removed from a flat values projection. A filter like ``filter(project__id=...)`` auto-joins ``project``; without this, a flat ``values()`` call leaks every Project column even though only one main-m

(
        self,
        source_model: type["Model"],
        select_related: list[str],
    )

Source from the content-addressed store, hash-verified

738 )
739
740 def with_projection_exclusions(
741 self,
742 source_model: type["Model"],
743 select_related: list[str],
744 ) -> "ExcludableItems":
745 """
746 Return a copy with relations not referenced by ``fields()`` removed
747 from a flat values projection.
748
749 A filter like ``filter(project__id=...)`` auto-joins ``project``;
750 without this, a flat ``values()`` call leaks every Project column
751 even though only one main-model field was requested. Returns the
752 original instance unchanged when ``fields()`` was never called -
753 the leak is a values-projection problem, not an ORM-load problem.
754
755 :param source_model: model from which relation paths are rooted
756 :type source_model: type[Model]
757 :param select_related: paths joined into the query (dunder strings)
758 :type select_related: list[str]
759 :return: new :class:`ExcludableItems` with implicit excludes added,
760 or ``self`` when no ``fields()`` call had any effect
761 :rtype: ExcludableItems
762 """
763 if self.include_entry_count() == 0:
764 return self
765
766 excludable = ExcludableItems.from_excludable(self)
767
768 # If fields() never names the source model, project only its PK.
769 if not excludable._referenced_at(source_model, ()):
770 excludable.get(source_model).pk_only = True
771
772 for path in select_related:
773 parts = tuple(path.split("__"))
774 # Snapshot which prefixes carry a fields() reference before any
775 # exclusions are added below - the exclude additions would
776 # otherwise show up as "referenced" on the next iteration.
777 referenced = [
778 excludable._referenced_at(source_model, parts[: d + 1])
779 for d in range(len(parts))
780 ]
781 # Walk deepest-first so kept_deeper accumulates inward. Each
782 # segment still needs its own exclude when not kept, because
783 # ReverseAliasResolver only consults the immediate parent.
784 kept_deeper = False
785 for i in reversed(range(len(parts))):
786 kept_deeper = kept_deeper or referenced[i]
787 segment = parts[i]
788 parent_alias, parent_model = excludable._resolve_path(
789 source_model, parts[:i]
790 )
791 parent_exc = excludable.get(parent_model, alias=parent_alias)
792 if parent_exc.is_explicitly_included(segment):
793 continue
794 if kept_deeper:
795 # Intermediate relation: keep the join but project
796 # PK-only when this segment was never explicitly named.
797 if not referenced[i]:

Callers 1

valuesMethod · 0.80

Calls 8

include_entry_countMethod · 0.95
from_excludableMethod · 0.80
_referenced_atMethod · 0.80
_resolve_pathMethod · 0.80
get_nameMethod · 0.80
getMethod · 0.45
addMethod · 0.45

Tested by

no test coverage detected