This is an automated email from the ASF dual-hosted git repository.

uranusjr pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/airflow.git


The following commit(s) were added to refs/heads/main by this push:
     new 28573c94ff Update docs re: template_fields typing and subclasses 
(#29725)
28573c94ff is described below

commit 28573c94ff9054aee76aee42dd1eec314e849016
Author: Josh Fell <48934154+josh-f...@users.noreply.github.com>
AuthorDate: Thu Feb 23 23:47:37 2023 -0500

    Update docs re: template_fields typing and subclasses (#29725)
---
 docs/apache-airflow/core-concepts/operators.rst |  3 +-
 docs/apache-airflow/howto/custom-operator.rst   | 72 +++++++++++++++++++++----
 2 files changed, 65 insertions(+), 10 deletions(-)

diff --git a/docs/apache-airflow/core-concepts/operators.rst 
b/docs/apache-airflow/core-concepts/operators.rst
index cb278402de..03da9893b7 100644
--- a/docs/apache-airflow/core-concepts/operators.rst
+++ b/docs/apache-airflow/core-concepts/operators.rst
@@ -109,7 +109,8 @@ You can also use Jinja templating with nested fields, as 
long as these nested fi
     )
 
 
-.. note:: The ``template_fields`` property can equally be a class variable or 
an instance variable.
+.. note:: The ``template_fields`` property is a class variable and guaranteed 
to be of a ``Sequence[str]``
+    type (i.e. a list or tuple of strings).
 
 Deep nested fields can also be substituted, as long as all intermediate fields 
are marked as template fields:
 
diff --git a/docs/apache-airflow/howto/custom-operator.rst 
b/docs/apache-airflow/howto/custom-operator.rst
index 33b1ffb357..bd80913a25 100644
--- a/docs/apache-airflow/howto/custom-operator.rst
+++ b/docs/apache-airflow/howto/custom-operator.rst
@@ -94,7 +94,7 @@ logic of an operation is in one place - in the operator.
 .. _custom-operator/hook:
 
 Hooks
-^^^^^
+-----
 Hooks act as an interface to communicate with the external shared resources in 
a DAG.
 For example, multiple tasks in a DAG can require access to a MySQL database. 
Instead of
 creating a connection per task, you can retrieve a connection from the hook 
and utilize it.
@@ -131,7 +131,7 @@ The ``execute`` gets called only during a DAG run.
 
 
 User interface
-^^^^^^^^^^^^^^^
+--------------
 Airflow also allows the developer to control how the operator shows up in the 
DAG UI.
 Override ``ui_color`` to change the background color of the operator in UI.
 Override ``ui_fgcolor`` to change the color of the label.
@@ -146,7 +146,7 @@ Override ``custom_operator_name`` to change the displayed 
name to something othe
             # ...
 
 Templating
-^^^^^^^^^^^
+----------
 You can use :ref:`Jinja templates <concepts:jinja-templating>` to parameterize 
your operator.
 Airflow considers the field names present in ``template_fields``  for 
templating while rendering
 the operator.
@@ -157,12 +157,13 @@ the operator.
 
             template_fields: Sequence[str] = ("name",)
 
-            def __init__(self, name: str, **kwargs) -> None:
+            def __init__(self, name: str, world: str, **kwargs) -> None:
                 super().__init__(**kwargs)
                 self.name = name
+                self.world = world
 
             def execute(self, context):
-                message = f"Hello from {self.name}"
+                message = f"Hello {self.world} it's {self.name}!"
                 print(message)
                 return message
 
@@ -171,7 +172,11 @@ You can use the template as follows:
 .. code-block:: python
 
         with dag:
-            hello_task = HelloOperator(task_id="task_id_1", dag=dag, name="{{ 
task_instance.task_id }}")
+            hello_task = HelloOperator(
+                task_id="task_id_1",
+                name="{{ task_instance.task_id }}",
+                world="Earth",
+            )
 
 In this example, Jinja looks for the ``name`` parameter and substitutes ``{{ 
task_instance.task_id }}`` with
 ``task_id_1``.
@@ -232,7 +237,6 @@ Then using this template as follows:
             config_task = MyConfigOperator(
                 task_id="task_id_1",
                 configuration={"query": {"job_id": "123", "sql": "select * 
from my_table"}},
-                dag=dag,
             )
 
 This will result in the UI rendering ``configuration`` as json in addition to 
the value contained in the
@@ -267,15 +271,65 @@ Currently available lexers:
 
 If you use a non-existing lexer then the value of the template field will be 
rendered as a pretty-printed object.
 
+Add template fields with subclassing
+^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+A common use case for creating a custom operator is for simply augmenting 
existing ``template_fields``.
+There might be a situation is which an operator you wish to use doesn't define 
certain parameters as
+templated, but you'd like to be able to dynamically pass an argument as a 
Jinja expression. This can easily be
+achieved with a quick subclassing of the existing operator.
+
+Let's assume you want to use the ``HelloOperator`` defined earlier:
+
+.. code-block:: python
+
+        class HelloOperator(BaseOperator):
+
+            template_fields: Sequence[str] = ("name",)
+
+            def __init__(self, name: str, world: str, **kwargs) -> None:
+                super().__init__(**kwargs)
+                self.name = name
+                self.world = world
+
+            def execute(self, context):
+                message = f"Hello {self.world} it's {self.name}!"
+                print(message)
+                return message
+
+However, you'd like to dynamically parameterize ``world`` arguments. Because 
the ``template_fields`` property
+is guaranteed to be a ``Sequence[str]`` type (i.e. a list or tuple of 
strings), you can subclass the
+``HelloOperator`` to modify the ``template_fields`` as desired easily.
+
+.. code-block:: python
+
+    class MyHelloOperator(HelloOperator):
+
+        template_fields: Sequence[str] = (*HelloOperator.template_fields, 
"world")
+
+Now you can use ``MyHelloOperator`` like this:
+
+.. code-block:: python
+
+    with dag:
+        hello_task = MyHelloOperator(
+            task_id="task_id_1",
+            name="{{ task_instance.task_id }}",
+            world="{{ var.value.my_world }}",
+        )
+
+In this example, the ``world`` argument will be dynamically set to the value 
of an Airflow Variable named
+"my_world" via a Jinja expression.
+
+
 Define an operator extra link
-^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+------------------------------
 
 For your operator, you can :doc:`Define an extra link <define-extra-link>` 
that can
 redirect users to external systems. For example, you can add a link that 
redirects
 the user to the operator's manual.
 
 Sensors
-^^^^^^^^
+-------
 Airflow provides a primitive for a special kind of operator, whose purpose is 
to
 poll some state (e.g. presence of a file) on a regular interval until a
 success criteria is met.

Reply via email to