We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Name Django Admin Views clearer
Currently, Django Admin views are captured with confusingly internal names. We should capture something clearer

![_django_object_actions_utils_change_view](https://user-images.githubusercontent.com/6119/41299370-829159f6-6e20-11e8-8bc1-a6a41fba324d.png)


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `src/scout_apm/django/middleware.py`
Content:
```
1 # coding=utf-8
2 from __future__ import absolute_import, division, print_function, unicode_literals
3 
4 import logging
5 
6 from scout_apm.core.ignore import ignore_path
7 from scout_apm.core.queue_time import track_request_queue_time
8 from scout_apm.core.tracked_request import TrackedRequest
9 
10 logger = logging.getLogger(__name__)
11 
12 
13 def get_operation_name(request):
14     view_name = request.resolver_match._func_path
15     return "Controller/" + view_name
16 
17 
18 def track_request_view_data(request, tracked_request):
19     tracked_request.tag("path", request.path)
20     if ignore_path(request.path):
21         tracked_request.tag("ignore_transaction", True)
22 
23     try:
24         # Determine a remote IP to associate with the request. The value is
25         # spoofable by the requester so this is not suitable to use in any
26         # security sensitive context.
27         user_ip = (
28             request.META.get("HTTP_X_FORWARDED_FOR", "").split(",")[0]
29             or request.META.get("HTTP_CLIENT_IP", "").split(",")[0]
30             or request.META.get("REMOTE_ADDR", None)
31         )
32         tracked_request.tag("user_ip", user_ip)
33     except Exception:
34         pass
35 
36     user = getattr(request, "user", None)
37     if user is not None:
38         try:
39             tracked_request.tag("username", user.get_username())
40         except Exception:
41             pass
42 
43 
44 class MiddlewareTimingMiddleware(object):
45     """
46     Insert as early into the Middleware stack as possible (outermost layers),
47     so that other middlewares called after can be timed.
48     """
49 
50     def __init__(self, get_response):
51         self.get_response = get_response
52 
53     def __call__(self, request):
54         tracked_request = TrackedRequest.instance()
55 
56         tracked_request.start_span(operation="Middleware")
57         queue_time = request.META.get("HTTP_X_QUEUE_START") or request.META.get(
58             "HTTP_X_REQUEST_START", ""
59         )
60         track_request_queue_time(queue_time, tracked_request)
61 
62         try:
63             return self.get_response(request)
64         finally:
65             TrackedRequest.instance().stop_span()
66 
67 
68 class ViewTimingMiddleware(object):
69     """
70     Insert as deep into the middleware stack as possible, ideally wrapping no
71     other middleware. Designed to time the View itself
72     """
73 
74     def __init__(self, get_response):
75         self.get_response = get_response
76 
77     def __call__(self, request):
78         """
79         Wrap a single incoming request with start and stop calls.
80         This will start timing, but relies on the process_view callback to
81         capture more details about what view was really called, and other
82         similar info.
83 
84         If process_view isn't called, then the request will not
85         be recorded.  This can happen if a middleware further along the stack
86         doesn't call onward, and instead returns a response directly.
87         """
88         tracked_request = TrackedRequest.instance()
89 
90         # This operation name won't be recorded unless changed later in
91         # process_view
92         tracked_request.start_span(operation="Unknown")
93         try:
94             response = self.get_response(request)
95         finally:
96             tracked_request.stop_span()
97         return response
98 
99     def process_view(self, request, view_func, view_args, view_kwargs):
100         """
101         Capture details about the view_func that is about to execute
102         """
103         tracked_request = TrackedRequest.instance()
104         tracked_request.mark_real_request()
105 
106         track_request_view_data(request, tracked_request)
107 
108         span = tracked_request.current_span()
109         if span is not None:
110             span.operation = get_operation_name(request)
111 
112     def process_exception(self, request, exception):
113         """
114         Mark this request as having errored out
115 
116         Does not modify or catch or otherwise change the exception thrown
117         """
118         TrackedRequest.instance().tag("error", "true")
119 
120 
121 class OldStyleMiddlewareTimingMiddleware(object):
122     """
123     Insert as early into the Middleware stack as possible (outermost layers),
124     so that other middlewares called after can be timed.
125     """
126 
127     def process_request(self, request):
128         tracked_request = TrackedRequest.instance()
129         request._scout_tracked_request = tracked_request
130 
131         queue_time = request.META.get("HTTP_X_QUEUE_START") or request.META.get(
132             "HTTP_X_REQUEST_START", ""
133         )
134         track_request_queue_time(queue_time, tracked_request)
135 
136         tracked_request.start_span(operation="Middleware")
137 
138     def process_response(self, request, response):
139         # Only stop span if there's a request, but presume we are balanced,
140         # i.e. that custom instrumentation within the application is not
141         # causing errors
142         tracked_request = getattr(request, "_scout_tracked_request", None)
143         if tracked_request is not None:
144             tracked_request.stop_span()
145         return response
146 
147 
148 class OldStyleViewMiddleware(object):
149     def process_view(self, request, view_func, view_func_args, view_func_kwargs):
150         tracked_request = getattr(request, "_scout_tracked_request", None)
151         if tracked_request is None:
152             # Looks like OldStyleMiddlewareTimingMiddleware didn't run, so
153             # don't do anything
154             return
155 
156         tracked_request.mark_real_request()
157 
158         track_request_view_data(request, tracked_request)
159 
160         span = tracked_request.start_span(operation=get_operation_name(request))
161         # Save the span into the request, so we can check
162         # if we're matched up when stopping
163         request._scout_view_span = span
164 
165     def process_response(self, request, response):
166         tracked_request = getattr(request, "_scout_tracked_request", None)
167         if tracked_request is None:
168             # Looks like OldStyleMiddlewareTimingMiddleware didn't run, so
169             # don't do anything
170             return response
171 
172         # Only stop span if we started, but presume we are balanced, i.e. that
173         # custom instrumentation within the application is not causing errors
174         span = getattr(request, "_scout_view_span", None)
175         if span is not None:
176             tracked_request.stop_span()
177         return response
178 
179     def process_exception(self, request, exception):
180         tracked_request = getattr(request, "_scout_tracked_request", None)
181         if tracked_request is None:
182             # Looks like OldStyleMiddlewareTimingMiddleware didn't run, so
183             # don't do anything
184             return
185 
186         tracked_request.tag("error", "true")
187
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/src/scout_apm/django/middleware.py b/src/scout_apm/django/middleware.py
--- a/src/scout_apm/django/middleware.py
+++ b/src/scout_apm/django/middleware.py
@@ -11,7 +11,20 @@
 
 
 def get_operation_name(request):
+    view_func = request.resolver_match.func
     view_name = request.resolver_match._func_path
+
+    if hasattr(view_func, "model_admin"):
+        # Seems to comes from Django admin (attribute only set on Django 1.9+)
+        admin_class = view_func.model_admin.__class__
+        view_name = (
+            admin_class.__module__
+            + "."
+            + admin_class.__name__
+            + "."
+            + view_func.__name__
+        )
+
     return "Controller/" + view_name

{"golden_diff": "diff --git a/src/scout_apm/django/middleware.py b/src/scout_apm/django/middleware.py\n--- a/src/scout_apm/django/middleware.py\n+++ b/src/scout_apm/django/middleware.py\n@@ -11,7 +11,20 @@\n \n \n def get_operation_name(request):\n+    view_func = request.resolver_match.func\n     view_name = request.resolver_match._func_path\n+\n+    if hasattr(view_func, \"model_admin\"):\n+        # Seems to comes from Django admin (attribute only set on Django 1.9+)\n+        admin_class = view_func.model_admin.__class__\n+        view_name = (\n+            admin_class.__module__\n+            + \".\"\n+            + admin_class.__name__\n+            + \".\"\n+            + view_func.__name__\n+        )\n+\n     return \"Controller/\" + view_name\n", "issue": "Name Django Admin Views clearer\nCurrently, Django Admin views are captured with confusingly internal names. We should capture something clearer\r\n\r\n![_django_object_actions_utils_change_view](https://user-images.githubusercontent.com/6119/41299370-829159f6-6e20-11e8-8bc1-a6a41fba324d.png)\r\n\n", "before_files": [{"content": "# coding=utf-8\nfrom __future__ import absolute_import, division, print_function, unicode_literals\n\nimport logging\n\nfrom scout_apm.core.ignore import ignore_path\nfrom scout_apm.core.queue_time import track_request_queue_time\nfrom scout_apm.core.tracked_request import TrackedRequest\n\nlogger = logging.getLogger(__name__)\n\n\ndef get_operation_name(request):\n    view_name = request.resolver_match._func_path\n    return \"Controller/\" + view_name\n\n\ndef track_request_view_data(request, tracked_request):\n    tracked_request.tag(\"path\", request.path)\n    if ignore_path(request.path):\n        tracked_request.tag(\"ignore_transaction\", True)\n\n    try:\n        # Determine a remote IP to associate with the request. The value is\n        # spoofable by the requester so this is not suitable to use in any\n        # security sensitive context.\n        user_ip = (\n            request.META.get(\"HTTP_X_FORWARDED_FOR\", \"\").split(\",\")[0]\n            or request.META.get(\"HTTP_CLIENT_IP\", \"\").split(\",\")[0]\n            or request.META.get(\"REMOTE_ADDR\", None)\n        )\n        tracked_request.tag(\"user_ip\", user_ip)\n    except Exception:\n        pass\n\n    user = getattr(request, \"user\", None)\n    if user is not None:\n        try:\n            tracked_request.tag(\"username\", user.get_username())\n        except Exception:\n            pass\n\n\nclass MiddlewareTimingMiddleware(object):\n    \"\"\"\n    Insert as early into the Middleware stack as possible (outermost layers),\n    so that other middlewares called after can be timed.\n    \"\"\"\n\n    def __init__(self, get_response):\n        self.get_response = get_response\n\n    def __call__(self, request):\n        tracked_request = TrackedRequest.instance()\n\n        tracked_request.start_span(operation=\"Middleware\")\n        queue_time = request.META.get(\"HTTP_X_QUEUE_START\") or request.META.get(\n            \"HTTP_X_REQUEST_START\", \"\"\n        )\n        track_request_queue_time(queue_time, tracked_request)\n\n        try:\n            return self.get_response(request)\n        finally:\n            TrackedRequest.instance().stop_span()\n\n\nclass ViewTimingMiddleware(object):\n    \"\"\"\n    Insert as deep into the middleware stack as possible, ideally wrapping no\n    other middleware. Designed to time the View itself\n    \"\"\"\n\n    def __init__(self, get_response):\n        self.get_response = get_response\n\n    def __call__(self, request):\n        \"\"\"\n        Wrap a single incoming request with start and stop calls.\n        This will start timing, but relies on the process_view callback to\n        capture more details about what view was really called, and other\n        similar info.\n\n        If process_view isn't called, then the request will not\n        be recorded.  This can happen if a middleware further along the stack\n        doesn't call onward, and instead returns a response directly.\n        \"\"\"\n        tracked_request = TrackedRequest.instance()\n\n        # This operation name won't be recorded unless changed later in\n        # process_view\n        tracked_request.start_span(operation=\"Unknown\")\n        try:\n            response = self.get_response(request)\n        finally:\n            tracked_request.stop_span()\n        return response\n\n    def process_view(self, request, view_func, view_args, view_kwargs):\n        \"\"\"\n        Capture details about the view_func that is about to execute\n        \"\"\"\n        tracked_request = TrackedRequest.instance()\n        tracked_request.mark_real_request()\n\n        track_request_view_data(request, tracked_request)\n\n        span = tracked_request.current_span()\n        if span is not None:\n            span.operation = get_operation_name(request)\n\n    def process_exception(self, request, exception):\n        \"\"\"\n        Mark this request as having errored out\n\n        Does not modify or catch or otherwise change the exception thrown\n        \"\"\"\n        TrackedRequest.instance().tag(\"error\", \"true\")\n\n\nclass OldStyleMiddlewareTimingMiddleware(object):\n    \"\"\"\n    Insert as early into the Middleware stack as possible (outermost layers),\n    so that other middlewares called after can be timed.\n    \"\"\"\n\n    def process_request(self, request):\n        tracked_request = TrackedRequest.instance()\n        request._scout_tracked_request = tracked_request\n\n        queue_time = request.META.get(\"HTTP_X_QUEUE_START\") or request.META.get(\n            \"HTTP_X_REQUEST_START\", \"\"\n        )\n        track_request_queue_time(queue_time, tracked_request)\n\n        tracked_request.start_span(operation=\"Middleware\")\n\n    def process_response(self, request, response):\n        # Only stop span if there's a request, but presume we are balanced,\n        # i.e. that custom instrumentation within the application is not\n        # causing errors\n        tracked_request = getattr(request, \"_scout_tracked_request\", None)\n        if tracked_request is not None:\n            tracked_request.stop_span()\n        return response\n\n\nclass OldStyleViewMiddleware(object):\n    def process_view(self, request, view_func, view_func_args, view_func_kwargs):\n        tracked_request = getattr(request, \"_scout_tracked_request\", None)\n        if tracked_request is None:\n            # Looks like OldStyleMiddlewareTimingMiddleware didn't run, so\n            # don't do anything\n            return\n\n        tracked_request.mark_real_request()\n\n        track_request_view_data(request, tracked_request)\n\n        span = tracked_request.start_span(operation=get_operation_name(request))\n        # Save the span into the request, so we can check\n        # if we're matched up when stopping\n        request._scout_view_span = span\n\n    def process_response(self, request, response):\n        tracked_request = getattr(request, \"_scout_tracked_request\", None)\n        if tracked_request is None:\n            # Looks like OldStyleMiddlewareTimingMiddleware didn't run, so\n            # don't do anything\n            return response\n\n        # Only stop span if we started, but presume we are balanced, i.e. that\n        # custom instrumentation within the application is not causing errors\n        span = getattr(request, \"_scout_view_span\", None)\n        if span is not None:\n            tracked_request.stop_span()\n        return response\n\n    def process_exception(self, request, exception):\n        tracked_request = getattr(request, \"_scout_tracked_request\", None)\n        if tracked_request is None:\n            # Looks like OldStyleMiddlewareTimingMiddleware didn't run, so\n            # don't do anything\n            return\n\n        tracked_request.tag(\"error\", \"true\")\n", "path": "src/scout_apm/django/middleware.py"}], "after_files": [{"content": "# coding=utf-8\nfrom __future__ import absolute_import, division, print_function, unicode_literals\n\nimport logging\n\nfrom scout_apm.core.ignore import ignore_path\nfrom scout_apm.core.queue_time import track_request_queue_time\nfrom scout_apm.core.tracked_request import TrackedRequest\n\nlogger = logging.getLogger(__name__)\n\n\ndef get_operation_name(request):\n    view_func = request.resolver_match.func\n    view_name = request.resolver_match._func_path\n\n    if hasattr(view_func, \"model_admin\"):\n        # Seems to comes from Django admin (attribute only set on Django 1.9+)\n        admin_class = view_func.model_admin.__class__\n        view_name = (\n            admin_class.__module__\n            + \".\"\n            + admin_class.__name__\n            + \".\"\n            + view_func.__name__\n        )\n\n    return \"Controller/\" + view_name\n\n\ndef track_request_view_data(request, tracked_request):\n    tracked_request.tag(\"path\", request.path)\n    if ignore_path(request.path):\n        tracked_request.tag(\"ignore_transaction\", True)\n\n    try:\n        # Determine a remote IP to associate with the request. The value is\n        # spoofable by the requester so this is not suitable to use in any\n        # security sensitive context.\n        user_ip = (\n            request.META.get(\"HTTP_X_FORWARDED_FOR\", \"\").split(\",\")[0]\n            or request.META.get(\"HTTP_CLIENT_IP\", \"\").split(\",\")[0]\n            or request.META.get(\"REMOTE_ADDR\", None)\n        )\n        tracked_request.tag(\"user_ip\", user_ip)\n    except Exception:\n        pass\n\n    user = getattr(request, \"user\", None)\n    if user is not None:\n        try:\n            tracked_request.tag(\"username\", user.get_username())\n        except Exception:\n            pass\n\n\nclass MiddlewareTimingMiddleware(object):\n    \"\"\"\n    Insert as early into the Middleware stack as possible (outermost layers),\n    so that other middlewares called after can be timed.\n    \"\"\"\n\n    def __init__(self, get_response):\n        self.get_response = get_response\n\n    def __call__(self, request):\n        tracked_request = TrackedRequest.instance()\n\n        tracked_request.start_span(operation=\"Middleware\")\n        queue_time = request.META.get(\"HTTP_X_QUEUE_START\") or request.META.get(\n            \"HTTP_X_REQUEST_START\", \"\"\n        )\n        track_request_queue_time(queue_time, tracked_request)\n\n        try:\n            return self.get_response(request)\n        finally:\n            TrackedRequest.instance().stop_span()\n\n\nclass ViewTimingMiddleware(object):\n    \"\"\"\n    Insert as deep into the middleware stack as possible, ideally wrapping no\n    other middleware. Designed to time the View itself\n    \"\"\"\n\n    def __init__(self, get_response):\n        self.get_response = get_response\n\n    def __call__(self, request):\n        \"\"\"\n        Wrap a single incoming request with start and stop calls.\n        This will start timing, but relies on the process_view callback to\n        capture more details about what view was really called, and other\n        similar info.\n\n        If process_view isn't called, then the request will not\n        be recorded.  This can happen if a middleware further along the stack\n        doesn't call onward, and instead returns a response directly.\n        \"\"\"\n        tracked_request = TrackedRequest.instance()\n\n        # This operation name won't be recorded unless changed later in\n        # process_view\n        tracked_request.start_span(operation=\"Unknown\")\n        try:\n            response = self.get_response(request)\n        finally:\n            tracked_request.stop_span()\n        return response\n\n    def process_view(self, request, view_func, view_args, view_kwargs):\n        \"\"\"\n        Capture details about the view_func that is about to execute\n        \"\"\"\n        tracked_request = TrackedRequest.instance()\n        tracked_request.mark_real_request()\n\n        track_request_view_data(request, tracked_request)\n\n        span = tracked_request.current_span()\n        if span is not None:\n            span.operation = get_operation_name(request)\n\n    def process_exception(self, request, exception):\n        \"\"\"\n        Mark this request as having errored out\n\n        Does not modify or catch or otherwise change the exception thrown\n        \"\"\"\n        TrackedRequest.instance().tag(\"error\", \"true\")\n\n\nclass OldStyleMiddlewareTimingMiddleware(object):\n    \"\"\"\n    Insert as early into the Middleware stack as possible (outermost layers),\n    so that other middlewares called after can be timed.\n    \"\"\"\n\n    def process_request(self, request):\n        tracked_request = TrackedRequest.instance()\n        request._scout_tracked_request = tracked_request\n\n        queue_time = request.META.get(\"HTTP_X_QUEUE_START\") or request.META.get(\n            \"HTTP_X_REQUEST_START\", \"\"\n        )\n        track_request_queue_time(queue_time, tracked_request)\n\n        tracked_request.start_span(operation=\"Middleware\")\n\n    def process_response(self, request, response):\n        # Only stop span if there's a request, but presume we are balanced,\n        # i.e. that custom instrumentation within the application is not\n        # causing errors\n        tracked_request = getattr(request, \"_scout_tracked_request\", None)\n        if tracked_request is not None:\n            tracked_request.stop_span()\n        return response\n\n\nclass OldStyleViewMiddleware(object):\n    def process_view(self, request, view_func, view_func_args, view_func_kwargs):\n        tracked_request = getattr(request, \"_scout_tracked_request\", None)\n        if tracked_request is None:\n            # Looks like OldStyleMiddlewareTimingMiddleware didn't run, so\n            # don't do anything\n            return\n\n        tracked_request.mark_real_request()\n\n        track_request_view_data(request, tracked_request)\n\n        span = tracked_request.start_span(operation=get_operation_name(request))\n        # Save the span into the request, so we can check\n        # if we're matched up when stopping\n        request._scout_view_span = span\n\n    def process_response(self, request, response):\n        tracked_request = getattr(request, \"_scout_tracked_request\", None)\n        if tracked_request is None:\n            # Looks like OldStyleMiddlewareTimingMiddleware didn't run, so\n            # don't do anything\n            return response\n\n        # Only stop span if we started, but presume we are balanced, i.e. that\n        # custom instrumentation within the application is not causing errors\n        span = getattr(request, \"_scout_view_span\", None)\n        if span is not None:\n            tracked_request.stop_span()\n        return response\n\n    def process_exception(self, request, exception):\n        tracked_request = getattr(request, \"_scout_tracked_request\", None)\n        if tracked_request is None:\n            # Looks like OldStyleMiddlewareTimingMiddleware didn't run, so\n            # don't do anything\n            return\n\n        tracked_request.tag(\"error\", \"true\")\n", "path": "src/scout_apm/django/middleware.py"}]}