Repository: apache/airflow

Issue:
Rendered Template raise general error if TaskInstance not exists
### Apache Airflow version

2.2.0b1 (beta snapshot)

### Operating System

Linux

### Versions of Apache Airflow Providers

n/a

### Deployment

Docker-Compose

### Deployment details

Docker Image: apache/airflow:2.2.0b1-python3.8

```shell
$ docker version
Client:
 Version:           20.10.8
 API version:       1.41
 Go version:        go1.16.6
 Git commit:        3967b7d28e
 Built:             Wed Aug  4 10:59:01 2021
 OS/Arch:           linux/amd64
 Context:           default
 Experimental:      true

Server:
 Engine:
  Version:          20.10.8
  API version:      1.41 (minimum version 1.12)
  Go version:       go1.16.6
  Git commit:       75249d88bc
  Built:            Wed Aug  4 10:58:48 2021
  OS/Arch:          linux/amd64
  Experimental:     false
 containerd:
  Version:          v1.5.5
  GitCommit:        72cec4be58a9eb6b2910f5d10f1c01ca47d231c0.m
 runc:
  Version:          1.0.2
  GitCommit:        v1.0.2-0-g52b36a2d
 docker-init:
  Version:          0.19.0
  GitCommit:        de40ad0
```

```shell
$ docker-compose version
docker-compose version 1.29.2, build unknown
docker-py version: 5.0.2
CPython version: 3.9.6
OpenSSL version: OpenSSL 1.1.1l  24 Aug 2021
```

### What happened

Rendered Template endpoint raise error if not Task Instances exists (e.g. fresh DAG)

```
Traceback (most recent call last):
  File "/home/airflow/.local/lib/python3.8/site-packages/flask/app.py", line 2447, in wsgi_app
    response = self.full_dispatch_request()
  File "/home/airflow/.local/lib/python3.8/site-packages/flask/app.py", line 1952, in full_dispatch_request
    rv = self.handle_user_exception(e)
  File "/home/airflow/.local/lib/python3.8/site-packages/flask/app.py", line 1821, in handle_user_exception
    reraise(exc_type, exc_value, tb)
  File "/home/airflow/.local/lib/python3.8/site-packages/flask/_compat.py", line 39, in reraise
    raise value
  File "/home/airflow/.local/lib/python3.8/site-packages/flask/app.py", line 1950, in full_dispatch_request
    rv = self.dispatch_request()
  File "/home/airflow/.local/lib/python3.8/site-packages/flask/app.py", line 1936, in dispatch_request
    return self.view_functions[rule.endpoint](**req.view_args)
  File "/home/airflow/.local/lib/python3.8/site-packages/airflow/www/auth.py", line 51, in decorated
    return func(*args, **kwargs)
  File "/home/airflow/.local/lib/python3.8/site-packages/airflow/www/decorators.py", line 72, in wrapper
    return f(*args, **kwargs)
  File "/home/airflow/.local/lib/python3.8/site-packages/airflow/utils/session.py", line 70, in wrapper
    return func(*args, session=session, **kwargs)
  File "/home/airflow/.local/lib/python3.8/site-packages/airflow/www/views.py", line 1055, in rendered_templates
    ti = dag_run.get_task_instance(task_id=task.task_id, session=session)
AttributeError: 'NoneType' object has no attribute 'get_task_instance'
```

### What you expected to happen

I understand that probably no way to render this value anymore by airflow webserver.

So basically it can be replaced by the same warning/error which throw when we tried to access to XCom for TaskInstance which not exists (Task Instance -> XCom -> redirect to /home -> In top of the page Task Not exists)

![image](https://user-images.githubusercontent.com/85952209/133264002-21c3ff4a-8273-4e24-b00e-cf91639c58b4.png)

However it would be nice if we have same as we have in 2.1 (and probably earlier in 2.x)


### How to reproduce

1. Create new DAG
2. Go to Graph View
3. Try to access Rendered Tab
4. Get an error

![image](https://user-images.githubusercontent.com/85952209/133263628-80e4a7f9-ac82-48d5-9aea-84df1c64a240.png)


### Anything else

Every time.

Sample url: `http://127.0.0.1:8081/rendered-templates?dag_id=sample_dag&task_id=sample_task&execution_date=2021-09-14T10%3A10%3A23.484252%2B00%3A00`

Discuss with @uranusjr 

### Are you willing to submit PR?

- [ ] Yes I am willing to submit a PR!

### Code of Conduct

- [X] I agree to follow this project's [Code of Conduct](https://github.com/apache/airflow/blob/main/CODE_OF_CONDUCT.md)

NoResultFound raises in UI when access to Task Instance Details
### Apache Airflow version

2.2.0b1 (beta snapshot)

### Operating System

Linux (`5.13.13-zen1-1-zen #1 ZEN SMP PREEMPT Thu, 26 Aug 2021 19:14:35 +0000 x86_64 GNU/Linux`)

### Versions of Apache Airflow Providers

<details><summary> pip freeze | grep apache-airflow-providers</summary> 
apache-airflow-providers-amazon==2.1.0<br>
apache-airflow-providers-celery==2.0.0<br>
apache-airflow-providers-cncf-kubernetes==2.0.1<br>
apache-airflow-providers-docker==2.1.0<br>
apache-airflow-providers-elasticsearch==2.0.2<br>
apache-airflow-providers-ftp==2.0.0<br>
apache-airflow-providers-google==5.0.0<br>
apache-airflow-providers-grpc==2.0.0<br>
apache-airflow-providers-hashicorp==2.0.0<br>
apache-airflow-providers-http==2.0.0<br>
apache-airflow-providers-imap==2.0.0<br>
apache-airflow-providers-microsoft-azure==3.1.0<br>
apache-airflow-providers-mysql==2.1.0<br>
apache-airflow-providers-postgres==2.0.0<br>
apache-airflow-providers-redis==2.0.0<br>
apache-airflow-providers-sendgrid==2.0.0<br>
apache-airflow-providers-sftp==2.1.0<br>
apache-airflow-providers-slack==4.0.0<br>
apache-airflow-providers-sqlite==2.0.0<br>
apache-airflow-providers-ssh==2.1.0<br>
</details>

### Deployment

Docker-Compose

### Deployment details

Docker Image: apache/airflow:2.2.0b1-python3.8

```shell
$ docker version
Client:
 Version:           20.10.8
 API version:       1.41
 Go version:        go1.16.6
 Git commit:        3967b7d28e
 Built:             Wed Aug  4 10:59:01 2021
 OS/Arch:           linux/amd64
 Context:           default
 Experimental:      true

Server:
 Engine:
  Version:          20.10.8
  API version:      1.41 (minimum version 1.12)
  Go version:       go1.16.6
  Git commit:       75249d88bc
  Built:            Wed Aug  4 10:58:48 2021
  OS/Arch:          linux/amd64
  Experimental:     false
 containerd:
  Version:          v1.5.5
  GitCommit:        72cec4be58a9eb6b2910f5d10f1c01ca47d231c0.m
 runc:
  Version:          1.0.2
  GitCommit:        v1.0.2-0-g52b36a2d
 docker-init:
  Version:          0.19.0
  GitCommit:        de40ad0
```

```shell
$ docker-compose version
docker-compose version 1.29.2, build unknown
docker-py version: 5.0.2
CPython version: 3.9.6
OpenSSL version: OpenSSL 1.1.1l  24 Aug 2021
```

### What happened

I've tested current project for compatibility to migrate to 2.2.x in the future.

As soon as i tried to access from UI to Task Instance Details or Rendered Template for DAG _which never started before_ I've got this error

```
Python version: 3.8.11
Airflow version: 2.2.0b1
Node: e70bca1d41d3
-------------------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/airflow/.local/lib/python3.8/site-packages/flask/app.py", line 2447, in wsgi_app
    response = self.full_dispatch_request()
  File "/home/airflow/.local/lib/python3.8/site-packages/flask/app.py", line 1952, in full_dispatch_request
    rv = self.handle_user_exception(e)
  File "/home/airflow/.local/lib/python3.8/site-packages/flask/app.py", line 1821, in handle_user_exception
    reraise(exc_type, exc_value, tb)
  File "/home/airflow/.local/lib/python3.8/site-packages/flask/_compat.py", line 39, in reraise
    raise value
  File "/home/airflow/.local/lib/python3.8/site-packages/flask/app.py", line 1950, in full_dispatch_request
    rv = self.dispatch_request()
  File "/home/airflow/.local/lib/python3.8/site-packages/flask/app.py", line 1936, in dispatch_request
    return self.view_functions[rule.endpoint](**req.view_args)
  File "/home/airflow/.local/lib/python3.8/site-packages/airflow/www/auth.py", line 51, in decorated
    return func(*args, **kwargs)
  File "/home/airflow/.local/lib/python3.8/site-packages/airflow/www/decorators.py", line 72, in wrapper
    return f(*args, **kwargs)
  File "/home/airflow/.local/lib/python3.8/site-packages/airflow/utils/session.py", line 70, in wrapper
    return func(*args, session=session, **kwargs)
  File "/home/airflow/.local/lib/python3.8/site-packages/airflow/www/views.py", line 1368, in task
    session.query(TaskInstance)
  File "/home/airflow/.local/lib/python3.8/site-packages/sqlalchemy/orm/query.py", line 3500, in one
    raise orm_exc.NoResultFound("No row was found for one()")
sqlalchemy.orm.exc.NoResultFound: No row was found for one()
```

### What you expected to happen

On previous version (Apache Airflow 2.1.2) it show information even if DAG never started.
If it's new behavior of Airflow for Task Instances in UI it would be nice get information specific to this error rather than generic error

### How to reproduce

1. Use Apache Airflow: 2.2.0b1
2. Create new DAG
3. In web server try to access to Task Instance Details (`/task` entrypoint) or Rendered Template (`rendered-templates` entrypoint)

### Anything else

As soon as DAG started at least once this kind of errors gone when access Task Instance Details or Rendered Template for this DAG Tasks

Seems like this kind of errors happen after #17719

### Are you willing to submit PR?

- [ ] Yes I am willing to submit a PR!

### Code of Conduct

- [X] I agree to follow this project's [Code of Conduct](https://github.com/apache/airflow/blob/main/CODE_OF_CONDUCT.md)



Discussion:

Thanks for opening your first issue here! Be sure to follow the issue template!

Could you describe what would show in the *Task Instance Attributes* field in that case? (I think I know, but it’d be nice to have someone to confirm this.)
It would be nice if it show the same information that it shows in current stable - `2.1.x`
In case if no DagRun created before it uses current date-time as execution date (my assumptions)

**Task Instance Attributes - 2.1.2**
![image](https://user-images.githubusercontent.com/3998685/133074323-db10a14e-e695-4cb7-882c-87fe551b66cc.png)

However probably it not possible any more and at least it would be nice get same error as we get on access to XCom

![image](https://user-images.githubusercontent.com/3998685/133075482-5f75e883-6626-4887-b3e7-4b4e8cc1d03d.png)


**Rendered Template - 2.1.2** 

Rendered Template also handy for if DagRun not exists, at least it help localize some errors in macros/variables

_Some configuration errors_:
![image](https://user-images.githubusercontent.com/3998685/133074947-cdf22fdf-db29-4f24-af0c-32ceb2be015c.png)

_Missing Variables_:
![image](https://user-images.githubusercontent.com/3998685/133075001-859f7143-67c9-4470-b024-3e79140c5418.png)

_An finally rendered template_
![image](https://user-images.githubusercontent.com/3998685/133075132-195c0971-747e-4ee4-8616-128c03ac4eea.png)

**Rendered Template - 2.2.0b1**

```
Python version: 3.8.11
Airflow version: 2.2.0b1
Node: e70bca1d41d3
-------------------------------------------------------------------------------
Traceback (most recent call last):
  File "/home/airflow/.local/lib/python3.8/site-packages/flask/app.py", line 2447, in wsgi_app
    response = self.full_dispatch_request()
  File "/home/airflow/.local/lib/python3.8/site-packages/flask/app.py", line 1952, in full_dispatch_request
    rv = self.handle_user_exception(e)
  File "/home/airflow/.local/lib/python3.8/site-packages/flask/app.py", line 1821, in handle_user_exception
    reraise(exc_type, exc_value, tb)
  File "/home/airflow/.local/lib/python3.8/site-packages/flask/_compat.py", line 39, in reraise
    raise value
  File "/home/airflow/.local/lib/python3.8/site-packages/flask/app.py", line 1950, in full_dispatch_request
    rv = self.dispatch_request()
  File "/home/airflow/.local/lib/python3.8/site-packages/flask/app.py", line 1936, in dispatch_request
    return self.view_functions[rule.endpoint](**req.view_args)
  File "/home/airflow/.local/lib/python3.8/site-packages/airflow/www/auth.py", line 51, in decorated
    return func(*args, **kwargs)
  File "/home/airflow/.local/lib/python3.8/site-packages/airflow/www/decorators.py", line 72, in wrapper
    return f(*args, **kwargs)
  File "/home/airflow/.local/lib/python3.8/site-packages/airflow/utils/session.py", line 70, in wrapper
    return func(*args, session=session, **kwargs)
  File "/home/airflow/.local/lib/python3.8/site-packages/airflow/www/views.py", line 1055, in rendered_templates
    ti = dag_run.get_task_instance(task_id=task.task_id, session=session)
AttributeError: 'NoneType' object has no attribute 'get_task_instance'
```
Again, what would show in the *Task Instance Attributes* fields? Only your first screenshot is related to the question, and unfortunately the fields are cropped so I can’t see what values are there exactly.
Also, what are the links that brought you to the page?

Resolving patch:
diff --git a/airflow/www/views.py b/airflow/www/views.py
--- a/airflow/www/views.py
+++ b/airflow/www/views.py
@@ -29,7 +29,7 @@
 from datetime import timedelta
 from json import JSONDecodeError
 from operator import itemgetter
-from typing import Iterable, List, Optional, Tuple
+from typing import Any, Iterable, List, Optional, Tuple
 from urllib.parse import parse_qsl, unquote, urlencode, urlparse
 
 import lazy_object_proxy
@@ -1050,10 +1050,20 @@ def rendered_templates(self, session):
         logging.info("Retrieving rendered templates.")
         dag = current_app.dag_bag.get_dag(dag_id)
         dag_run = dag.get_dagrun(execution_date=dttm, session=session)
-
         task = copy.copy(dag.get_task(task_id))
-        ti = dag_run.get_task_instance(task_id=task.task_id, session=session)
-        ti.refresh_from_task(task)
+
+        if dag_run is None:
+            # No DAG run matching given logical date. This usually means this
+            # DAG has never been run. Task instance rendering does not really
+            # make sense in this situation, but "works" prior to AIP-39. This
+            # "fakes" a temporary DagRun-TaskInstance association (not saved to
+            # database) for presentation only.
+            ti = TaskInstance(task)
+            ti.dag_run = DagRun(dag_id=dag_id, execution_date=dttm)
+        else:
+            ti = dag_run.get_task_instance(task_id=task.task_id, session=session)
+            ti.refresh_from_task(task)
+
         try:
             ti.get_rendered_template_fields(session=session)
         except AirflowException as e:
@@ -1063,6 +1073,7 @@ def rendered_templates(self, session):
             flash(msg, "error")
         except Exception as e:
             flash("Error rendering template: " + str(e), "error")
+
         title = "Rendered Template"
         html_dict = {}
         renderers = wwwutils.get_attr_renderer()
@@ -1364,7 +1375,7 @@ def task(self, session):
         task = copy.copy(dag.get_task(task_id))
         task.resolve_template_files()
 
-        ti = (
+        ti: Optional[TaskInstance] = (
             session.query(TaskInstance)
             .options(
                 # HACK: Eager-load relationships. This is needed because
@@ -1379,18 +1390,14 @@ def task(self, session):
                 TaskInstance.dag_id == dag_id,
                 TaskInstance.task_id == task_id,
             )
-            .one()
+            .one_or_none()
         )
-        ti.refresh_from_task(task)
-
-        ti_attrs = [
-            (attr_name, attr)
-            for attr_name, attr in (
-                (attr_name, getattr(ti, attr_name)) for attr_name in dir(ti) if not attr_name.startswith("_")
-            )
-            if not callable(attr)
-        ]
-        ti_attrs = sorted(ti_attrs)
+        if ti is None:
+            ti_attrs: Optional[List[Tuple[str, Any]]] = None
+        else:
+            ti.refresh_from_task(task)
+            all_ti_attrs = ((name, getattr(ti, name)) for name in dir(ti) if not name.startswith("_"))
+            ti_attrs = sorted((name, attr) for name, attr in all_ti_attrs if not callable(attr))
 
         attr_renderers = wwwutils.get_attr_renderer()
         task_attrs = [
@@ -1420,17 +1427,20 @@ def task(self, session):
                 "Airflow administrator for assistance.".format(
                     "- This task instance already ran and had it's state changed manually "
                     "(e.g. cleared in the UI)<br>"
-                    if ti.state == State.NONE
+                    if ti and ti.state == State.NONE
                     else ""
                 ),
             )
         ]
 
         # Use the scheduler's context to figure out which dependencies are not met
-        dep_context = DepContext(SCHEDULER_QUEUED_DEPS)
-        failed_dep_reasons = [
-            (dep.dep_name, dep.reason) for dep in ti.get_failed_dep_statuses(dep_context=dep_context)
-        ]
+        if ti is None:
+            failed_dep_reasons: List[Tuple[str, str]] = []
+        else:
+            dep_context = DepContext(SCHEDULER_QUEUED_DEPS)
+            failed_dep_reasons = [
+                (dep.dep_name, dep.reason) for dep in ti.get_failed_dep_statuses(dep_context=dep_context)
+            ]
 
         title = "Task Instance Details"
         return self.render_template(


Test patch:
diff --git a/tests/www/views/test_views_task_norun.py b/tests/www/views/test_views_task_norun.py
new file mode 100644
--- /dev/null
+++ b/tests/www/views/test_views_task_norun.py
@@ -0,0 +1,50 @@
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#   http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+import urllib.parse
+
+import pytest
+
+from airflow.utils import dates
+from tests.test_utils.db import clear_db_runs
+
+DEFAULT_DATE = dates.days_ago(2)
+
+DEFAULT_VAL = urllib.parse.quote_plus(str(DEFAULT_DATE))
+
+
+@pytest.fixture(scope="module", autouse=True)
+def reset_dagruns():
+    """Clean up stray garbage from other tests."""
+    clear_db_runs()
+
+
+def test_task_view_no_task_instance(admin_client):
+    url = f"/task?task_id=runme_0&dag_id=example_bash_operator&execution_date={DEFAULT_VAL}"
+    resp = admin_client.get(url, follow_redirects=True)
+    assert resp.status_code == 200
+    html = resp.data.decode("utf-8")
+    assert "<h5>No Task Instance Available</h5>" in html
+    assert "<h5>Task Instance Attributes</h5>" not in html
+
+
+def test_rendered_templates_view_no_task_instance(admin_client):
+    url = f"/rendered-templates?task_id=runme_0&dag_id=example_bash_operator&execution_date={DEFAULT_VAL}"
+    resp = admin_client.get(url, follow_redirects=True)
+    assert resp.status_code == 200
+    html = resp.data.decode("utf-8")
+    assert "Rendered Template" in html