Repository: encode/django-rest-framework

Issue:
Consider deprecation of `.model` attribute on views.
The `.model` attribute on views is a shortcut for `.serializer_class` and/or `.queryset`.

It's usage is currently discouraged in favor of the more explicit style, but we should probably deprecate it entirely. It creates undesirable implicit behaviour, and is a potential point of confusion.

Removing it would create a simpler very explicit style in the generic views that's feels like a further step in the right direction to me.

This would also allow us to remove the `DEFAULT_MODEL_SERIALIZER_CLASS` setting.



Discussion:
To be more clear - I think it's problematic because it's unclear to the user _exactly_ what behaviour they  should expect if `.model` is set.  The `.queryset` and `.serializer_class` have a very specific set of concerns (object lookup and validation/serialisation respectively)

Marking for 2.4 consideration.

Note that doing this would force us to change the homepage/README example to use an explicit serializer, and fix a long outstanding bug that the example contains, as noted in #1249.


Resolving patch:
diff --git a/rest_framework/generics.py b/rest_framework/generics.py
--- a/rest_framework/generics.py
+++ b/rest_framework/generics.py
@@ -252,6 +252,12 @@ def get_serializer_class(self):
         if serializer_class is not None:
             return serializer_class
 
+        warnings.warn(
+            'The `.model` attribute on view classes is now deprecated in favor '
+            'of the more explicit `serializer_class` and `queryset` attributes.',
+            DeprecationWarning, stacklevel=2
+        )
+
         assert self.model is not None, \
             "'%s' should either include a 'serializer_class' attribute, " \
             "or use the 'model' attribute as a shortcut for " \
@@ -282,6 +288,11 @@ def get_queryset(self):
             return self.queryset._clone()
 
         if self.model is not None:
+            warnings.warn(
+                'The `.model` attribute on view classes is now deprecated in favor '
+                'of the more explicit `serializer_class` and `queryset` attributes.',
+                DeprecationWarning, stacklevel=2
+            )
             return self.model._default_manager.all()
 
         error_format = "'%s' must define 'queryset' or 'model'"
diff --git a/rest_framework/permissions.py b/rest_framework/permissions.py
--- a/rest_framework/permissions.py
+++ b/rest_framework/permissions.py
@@ -108,6 +108,9 @@ def get_required_permissions(self, method, model_cls):
         return [perm % kwargs for perm in self.perms_map[method]]
 
     def has_permission(self, request, view):
+        # Note that `.model` attribute on views is deprecated, although we
+        # enforce the deprecation on the view `get_serializer_class()` and
+        # `get_queryset()` methods, rather than here.
         model_cls = getattr(view, 'model', None)
         queryset = getattr(view, 'queryset', None)
 
diff --git a/rest_framework/routers.py b/rest_framework/routers.py
--- a/rest_framework/routers.py
+++ b/rest_framework/routers.py
@@ -128,6 +128,9 @@ def get_default_base_name(self, viewset):
         If `base_name` is not specified, attempt to automatically determine
         it from the viewset.
         """
+        # Note that `.model` attribute on views is deprecated, although we
+        # enforce the deprecation on the view `get_serializer_class()` and
+        # `get_queryset()` methods, rather than here.
         model_cls = getattr(viewset, 'model', None)
         queryset = getattr(viewset, 'queryset', None)
         if model_cls is None and queryset is not None:
@@ -135,7 +138,7 @@ def get_default_base_name(self, viewset):
 
         assert model_cls, '`base_name` argument not specified, and could ' \
             'not automatically determine the name from the viewset, as ' \
-            'it does not have a `.model` or `.queryset` attribute.'
+            'it does not have a `.queryset` attribute.'
 
         return model_cls._meta.object_name.lower()
 


Test patch: