11from __future__ import annotations
22
33import typing as t
4+ from pathlib import Path
45
56from fastapi import APIRouter , Depends , HTTPException
67from sqlglot import exp
1213from sqlmesh .utils .date import now , to_datetime
1314from web .server import models
1415from web .server .settings import get_loaded_context
16+ from web .server .utils import is_relative_to
1517
1618router = APIRouter ()
1719
@@ -125,7 +127,7 @@ def serialize_model(context: Context, model: Model, render_query: bool = False)
125127 return models .Model (
126128 name = model .name ,
127129 fqn = model .fqn ,
128- path = str ( path . absolute (). relative_to ( context . path ). as_posix () ) if path else None ,
130+ path = _path_relative_to_project ( context , path ) if path else None ,
129131 full_path = str (path .absolute ().as_posix ()) if path else None ,
130132 dialect = dialect ,
131133 columns = columns ,
@@ -138,6 +140,23 @@ def serialize_model(context: Context, model: Model, render_query: bool = False)
138140 )
139141
140142
143+ def _path_relative_to_project (context : Context , path : Path ) -> str :
144+ """Returns a model's path relative to the project root that defines it.
145+
146+ `context.path` is only the first of the configured projects, so it isn't necessarily an
147+ ancestor of every model when the context is loaded with more than one of them.
148+ """
149+ absolute_path = path .absolute ()
150+ project_roots = [root for root in context .configs if is_relative_to (absolute_path , root )]
151+ if not project_roots :
152+ # The model lives outside of every configured project, so there is nothing to be
153+ # relative to.
154+ return absolute_path .as_posix ()
155+ # The deepest root wins so that nested projects report the closest one.
156+ closest_root = max (project_roots , key = lambda root : len (root .parts ))
157+ return absolute_path .relative_to (closest_root ).as_posix ()
158+
159+
141160def _get_model_type (model : Model ) -> str :
142161 if model .is_sql :
143162 return models .ModelType .SQL
0 commit comments