Repository: TobikoData/sqlmesh
Pull request #2735 (2024-06-04 22:54:54)

Feat: Add option to render node definitions with default fields included


Patch:
diff --git a/sqlmesh/core/audit/definition.py b/sqlmesh/core/audit/definition.py
index 9d3e78b6..aa98f6e7 100644
--- a/sqlmesh/core/audit/definition.py
+++ b/sqlmesh/core/audit/definition.py
@@ -377,7 +377,9 @@ class StandaloneAudit(_Node, AuditMixin):
             self.render_definition(), other.render_definition(), self.dialect, other.dialect
         ).strip()
 
-    def render_definition(self, include_python: bool = True) -> t.List[exp.Expression]:
+    def render_definition(
+        self, include_python: bool = True, include_defaults: bool = False
+    ) -> t.List[exp.Expression]:
         """Returns the original list of sql expressions comprising the model definition.
 
         Args:
@@ -388,7 +390,11 @@ class StandaloneAudit(_Node, AuditMixin):
         for field_name in sorted(self.meta_fields):
             field_value = getattr(self, field_name)
             field_info = self.all_field_infos()[field_name]
-            if field_name == "standalone" or field_value != field_info.default:
+            if (
+                field_name == "standalone"
+                or (include_defaults and field_value)
+                or field_value != field_info.default
+            ):
                 if field_name == "description":
                     comment = field_value
                 else:
diff --git a/sqlmesh/core/model/definition.py b/sqlmesh/core/model/definition.py
index 06335f1b..8f077ae1 100644
--- a/sqlmesh/core/model/definition.py
+++ b/sqlmesh/core/model/definition.py
@@ -149,7 +149,9 @@ class _Model(ModelMeta, frozen=True):
             **kwargs,
         )
 
-    def render_definition(self, include_python: bool = True) -> t.List[exp.Expression]:
+    def render_definition(
+        self, include_python: bool = True, include_defaults: bool = False
+    ) -> t.List[exp.Expression]:
         """Returns the original list of sql expressions comprising the model definition.
 
         Args:
@@ -160,7 +162,7 @@ class _Model(ModelMeta, frozen=True):
         for field_name, field_info in ModelMeta.all_field_infos().items():
             field_value = getattr(self, field_name)
 
-            if field_value != field_info.default:
+            if (include_defaults and field_value) or field_value != field_info.default:
                 if field_name == "description":
                     comment = field_value
                 elif field_name == "kind":
@@ -968,8 +970,12 @@ class SqlModel(_SqlBasedModel):
         )
         return query
 
-    def render_definition(self, include_python: bool = True) -> t.List[exp.Expression]:
-        result = super().render_definition(include_python=include_python)
+    def render_definition(
+        self, include_python: bool = True, include_defaults: bool = False
+    ) -> t.List[exp.Expression]:
+        result = super().render_definition(
+            include_python=include_python, include_defaults=include_defaults
+        )
         result.extend(self.pre_statements)
         result.append(self.query)
         result.extend(self.post_statements)
@@ -1347,10 +1353,12 @@ class PythonModel(_Model):
             print_exception(e, self.python_env)
             raise SQLMeshError(f"Error executing Python model '{self.name}'")
 
-    def render_definition(self, include_python: bool = True) -> t.List[exp.Expression]:
+    def render_definition(
+        self, include_python: bool = True, include_defaults: bool = False
+    ) -> t.List[exp.Expression]:
         # Ignore the provided value for the include_python flag, since the Pyhon model's
         # definition without Python code is meaningless.
-        return super().render_definition(include_python=True)
+        return super().render_definition(include_python=True, include_defaults=include_defaults)
 
     @property
     def is_python(self) -> bool:


Test patch:
diff --git a/tests/core/test_model.py b/tests/core/test_model.py
index 9abeb697..4ee00428 100644
--- a/tests/core/test_model.py
+++ b/tests/core/test_model.py
@@ -1013,6 +1013,57 @@ def test_render_definition():
     assert "def test_macro(evaluator, v):" in d.format_model_expressions(model.render_definition())
 
 
+def test_render_definition_with_defaults():
+    query = """
+        SELECT
+            1::int AS a,
+            CAST(2 AS double) AS b,
+        FROM
+            db.other_table t1
+            LEFT JOIN
+            db.table t2
+            ON
+                t1.a = t2.a
+        """
+
+    expressions = d.parse(
+        f"""
+        MODEL (
+            name db.table,
+            owner owner_name,
+            dialect spark,
+            kind VIEW,
+        );
+
+        {query}
+        """
+    )
+
+    model = load_sql_based_model(
+        expressions,
+        default_catalog="catalog",
+    )
+
+    expected_expressions = d.parse(
+        f"""
+        MODEL (
+            name db.table,
+            owner owner_name,
+            cron '@daily',
+            dialect spark,
+            kind VIEW,
+        );
+
+        {query}
+        """
+    )
+
+    # Should not include the macro implementation.
+    assert d.format_model_expressions(
+        model.render_definition(include_python=False, include_defaults=True)
+    ) == d.format_model_expressions(expected_expressions)
+
+
 def test_cron():
     daily = _Node(name="x", cron="@daily")
     assert to_datetime(daily.cron_prev("2020-01-01")) == to_datetime("2019-12-31")