vincbeck commented on code in PR #70312:
URL: https://github.com/apache/airflow/pull/70312#discussion_r3684864628


##########
airflow-core/src/airflow/models/team.py:
##########
@@ -78,3 +79,48 @@ def get_all_team_names(cls, *, session: Session = 
NEW_SESSION) -> set[str]:
         :return: Set of all team names
         """
         return set(session.scalars(select(Team.name)).all())
+
+
+class TeamOwnedMixin:
+    """
+    Exposes ``team_name`` on models that reach a team through a relationship 
path.
+
+    Teams only exist in multi-team mode, so the config is checked before any 
hop is
+    walked: single-team deployments answer ``None`` without loading a 
relationship, and
+    endpoints there pay for no extra join.  In multi-team mode the value comes 
from the
+    already-loaded relationships when the query applied
+    :func:`~airflow.api_fastapi.common.db.dags.eager_load_teams`, and falls 
back to the
+    per-Dag cached resolver otherwise — so the attribute stays correct on 
paths that
+    cannot eager load (an in-memory Dag run, a callback re-fetching by primary 
key)
+    instead of tripping the ``lazy="raise"`` guard that keeps N+1 loads out.
+    """
+
+    #: Attribute names to walk from ``self`` to the owning :class:`DagModel`.
+    _team_path: ClassVar[tuple[str, ...]] = ()
+
+    if TYPE_CHECKING:
+        # Every model mixing this in carries ``dag_id`` (it is the fallback 
lookup key).
+        dag_id: str
+
+    @property
+    def team_name(self) -> str | None:
+        """Name of the team owning this entity, or ``None`` when it is not 
team-owned."""
+        if not conf.getboolean("core", "multi_team"):
+            return None
+
+        from airflow.models.dag import DagModel
+
+        entity: Any = self
+        for attribute in (*self._team_path, "bundle", "teams"):
+            state = sa_inspect(entity)
+            if attribute in state.unloaded:
+                # Reuse this entity's own session. ``get_team_name`` is 
``@provide_session``,
+                # and the session it would open is the *same* scoped session 
the caller is
+                # using, so closing it on exit detaches every object the 
caller still holds.
+                if state.session is not None:
+                    return DagModel.get_team_name(self.dag_id, 
session=state.session)
+                return DagModel.get_team_name(self.dag_id)
+            if (entity := getattr(entity, attribute)) is None:
+                return None
+        # A bundle maps to at most one team (unique index on 
dag_bundle_team.dag_bundle_name).
+        return entity[0].name if entity else None

Review Comment:
   We did that on purpose so that later if we want to have N-N relationship 
between dag bundles and teams, it is easier



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to