Repository: getsentry/sentry-python

Issue:
DjangoIntegration conflict with MiddlewareMixin & TemplateView
django version: 3.0
sentry-sdk version: 0.14.3

My project has a middleware using Django's `django.utils.deprecation.MiddlewareMixin`. Visiting a view which subclasses `django.views.generic.TemplateView` while Sentry is active results in the following exception:
```
AttributeError: 'function' object has no attribute '__self__'
  File "django/core/handlers/exception.py", line 47, in inner
    response = get_response(request)
  File "django/core/handlers/base.py", line 196, in _get_response
    middleware_method.__self__.__class__.__name__,
```

The following classes & url config should be sufficient to demonstrate the problem:

```
from django.utils.deprecation import MiddlewareMixin
from django.views.generic import TemplateView

# ...in myapp.middleware.py
class DemonstratesConflictMiddleware(MiddlewareMixin):
    def process_template_response(self, request, response):
        return response

# ...in myapp.views.py
class DemonstratesConflictView(TemplateView): 
    template_name = "index.html"


# ...in urls.py
import myapp.views
urlpatterns += [
    path('/', myapp.views.DemonstratesConflictView.as_view(), name='throws-exception'),
]

# ... in settings.py

MIDDLEWARE += ['myapp.middleware.DemonstratesConflictMiddleware']

sentry_sdk.init(
    dsn="OMITTED",
    integrations=[DjangoIntegration()],
)

```


Discussion:
Sorry for the slow response, I will look into this now
Sorry, can't reproduce. Can you provide a template that exhibits this issue?
Closing due to lack of activity, please respond so we can continue investigating
@untitaker I've got the same error. With custom middleware with process_template_response and sentry enabled

Can you provide a minimal self-contained example so I can reproduce this? I
already tried it once and clearly there is more to it than what is
described in the issue.

On Fri, Jul 31, 2020, 06:15 Mikhail Podgurskiy <notifications@github.com>
wrote:

> @untitaker <https://github.com/untitaker> I've got the same error. With
> custom middleware with process_template_response and sentry enabled
>
> —
> You are receiving this because you were mentioned.
> Reply to this email directly, view it on GitHub
> <https://github.com/getsentry/sentry-python/issues/661#issuecomment-666906913>,
> or unsubscribe
> <https://github.com/notifications/unsubscribe-auth/AAGMPRJ7MOBCF2YUJISBQ2TR6JANDANCNFSM4LW27BKA>
> .
>

Here is the simple [gist](https://gist.github.com/kmmbvnr/8760daf908e58a600af4b5ad623fd3e5). I've added only new style custom middleware with process_template_response method

```
git clone https://gist.github.com/8760daf908e58a600af4b5ad623fd3e5.git sentry-bug
cd sentry-bug
poetry install
poetry run python ./manage.py runserver
```
Access to http://127.0.0.1:8000/

```
Django version 3.1rc1, using settings 'settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.
Internal Server Error: /login/
Traceback (most recent call last):
  File "/tmp/sentry-bug/.venv/lib/python3.6/site-packages/django/core/handlers/exception.py", line 47, in inner
    response = get_response(request)
  File "/tmp/sentry-bug/.venv/lib/python3.6/site-packages/django/core/handlers/base.py", line 198, in _get_response
    middleware_method.__self__.__class__.__name__,
AttributeError: 'function' object has no attribute '__self__'
```

Without `sentry.init` in settings.py the sample works correctly and shows login page
excellent, thank you 

Resolving patch:
diff --git a/sentry_sdk/integrations/django/middleware.py b/sentry_sdk/integrations/django/middleware.py
--- a/sentry_sdk/integrations/django/middleware.py
+++ b/sentry_sdk/integrations/django/middleware.py
@@ -91,9 +91,14 @@ def sentry_wrapped_method(*args, **kwargs):
 
             try:
                 # fails for __call__ of function on Python 2 (see py2.7-django-1.11)
-                return wraps(old_method)(sentry_wrapped_method)  # type: ignore
+                sentry_wrapped_method = wraps(old_method)(sentry_wrapped_method)
+
+                # Necessary for Django 3.1
+                sentry_wrapped_method.__self__ = old_method.__self__  # type: ignore
             except Exception:
-                return sentry_wrapped_method  # type: ignore
+                pass
+
+            return sentry_wrapped_method  # type: ignore
 
         return old_method
 


Test patch: