I read through that as best as I could, but the most I found was that it was suggested and then not replied to.
I don’t personally buy the argument of keeping the path() contract simple. There is already a check for isinstance(view, View), that throws an error instead of doing the right thing. I do think that the check of isinstance instead of hasattr() is a bad idea though as it limits the helpfulness of the error to just subtypes of View.
I think we can have a new discussion and not be stuck in the old decision. That was a very different time, when CBVs were just proposed. We now have a situation where CBVs are recommended as the standard way to write views.
CBVs are not recommended as the standard way to write views. In fact I personally recommend the opposite as it’s more “Pythonic” – simple is better than complex
I am +1 for the proposal, despite being in camp FBV. I have always found typing .as_view() confusing, like: “Why am I converting my View to a view? I thought it already was one?” I believe beginners also find this confusing, and even long-time users, at least myself, sometimes forget to type it.
Using a protocol as proposed is good for other classy view things and has a similar consistency to the ORM’s as_sql protocol.
The code in path() would be simpler actually. We delete:
elif isinstance(view, View):
view_cls_name = view.__class__.__name__
raise TypeError(
f"view must be a callable, pass {view_cls_name}.as_view(), not "
f"{view_cls_name}()."
)
and instead replace it with:
if hasattr(view, 'as_view'):
view = view.as_view()
from django.views import View
class Foo(View):
pass
Foo().as_view()
If you run this:
File "/[...]/venv/lib/python3.11/site-packages/django/utils/decorators.py", line 11, in __get__
raise AttributeError(
AttributeError: This method is available only on the class, not on instances.
hmm… actually, the single use @classonlymethod makes my suggestion of if hasattr(...) not work, because the hasattr call will cause a raise and then return false. Classic foot gun of hasattr.
I am all for keeping the specific and helpful error message in there instead of relying on the more generic error message from classonlymethod.
I also like the idea of auto-handling the default case of MyView.as_view() within urlpatterns, while still allowing to call it explicitly e.g. to pass custom initkwargs if needed or to use it in e.g. tests to convert the CBV to a callable outside of urlpatterns.
On a quick glance, there are 29 uses of .as_view() in our code base, 27 of those are the normal no-args case within a urlpattern. I’d find it easier to read and understand if passing a CBV directly would work.
As someone who rarely uses django CBV’s, I’m all for what @boxed proposes. I remember last time I had to use it, it was like:
I passed FooView
→ error
→ “Ah, I still need that classmethod, what was the name… ah as_view”
→ error
→ “hm, I need to call it… why is it so complicated”
So, if people don’t need it most of the times, why not make it easier?