In a Django project I have tests that check that a URL uses a specific class-based view. e.g. I have this view:
from django.views.generic import TemplateView
class HomeView(TemplateView):
template_name = "home.html"
And this test:
from django.test import TestCase
from django.urls import resolve
from myapp import views
class UrlsTestCase(TestCase):
def test_home_view(self):
self.assertEqual(resolve("/").func.__name__, views.HomeView.__name__)
This test passes in Django 3.x but once I try it with Django 4.0 the test fails with:
self.assertEqual(resolve("/").func.__name__, views.HomeView.__name__)
AssertionError: 'view' != 'HomeView'
- view
HomeView
Obviously something has changed in Django 4.0 but I can't see anything related in the release notes.
So, what's changed and how can I make this test work again (or how can I test this in a better way)?
CodePudding user response:
You use .as_view()
, it thus does not return a class object, but a function that will handle requests by creating a new HomeView
and trigger the corresponding method based on the HTTP method.
You can check if it is a method of the HomeView
by checking the .view_class
attribute. Furthermore it is probably better to check if the two classes are the same, not the names of these classes:
# ↓ no .__name__ ↓
self.assertEqual(resolve('/').func.view_class, views.HomeView)
I did some research why comparing the __name__
s no longer works. Apparently in django-3.0 they used the update_wrapper(…)
function which sets the __name__
, __qualname__
, etc. of one object (in this case the class) to the function. Indeed, in the source code [GitHub], we can see:
@classonlymethod def as_view(cls, **initkwargs): # … def view(request, *args, **kwargs): # … # … # take name and docstring from class update_wrapper(view, cls, updated=()) # …
this has been changed in django-4.0 where they also explained in a comment why they do no longer do that [GitHub]:
@classonlymethod def as_view(cls, **initkwargs): # … # __name__ and __qualname__ are intentionally left unchanged as # view_class should be used to robustly determine the name of the view # instead. # …