I have a little news app with a DetailView class like the following:
class DetailView(LoginRequiredMixin,generic.DetailView):
model = NewsItem
template_name = 'news/detail.html'
def get_object(self):
obj = super().get_object()
if self.request.user.is_superuser or obj.published:
return obj
and an urls.py config like this:
urlpatterns = [
path('', views.IndexView.as_view(), name='index'),
path('<int:pk>/', views.DetailView.as_view(), name='detail'),
path('<int:itemId>/publish', views.publish, name='publish'),
]
Now when i pass an invalid ID to the detail view it automatically redirects to 404. I wanted to know if it's possible to just pass an empty object to the DetailView instead. The template then would handle the 404 on its own.
Also:Even though it works so far I feel like the way i handled the permission requirements (overriding the get_object method) isn't the correct way/Django way to do things
Solution: i changed the overriding of get_object like this:
class DetailView(LoginRequiredMixin,generic.DetailView):
model = NewsItem
template_name = 'news/detail.html'
def get_object(self):
queryset = self.get_queryset()
pk = self.kwargs.get(self.pk_url_kwarg)
if pk is not None:
queryset = queryset.filter(pk=pk)
else:
raise AttributeError("No article id provided")
try:
return queryset.get()
except queryset.model.DoesNotExist:
return None
it is basically the same get_object method as the original just without the check for a slug value, and if the queryset.get() call catches the queryset.model.DoesNotExist Exception it returns None
Classy CBVs, as usual, makes all clear.
If you don't like 404 for a bad id, you need to override get_object to a greater extent than you have done, because it raises the exception Http404 for a bad id. The code in question is
try:
# Get the single item from the filtered queryset
obj = queryset.get()
except queryset.model.DoesNotExist:
raise Http404(_("No %(verbose_name)s found matching the query") %
{'verbose_name': queryset.model._meta.verbose_name})
return obj
You could catch the exception when you call obj = super().get_object()
from django.http import Http404
def get_object(self, queryset=None):
try:
obj = super().get_object(queryset)
except Http404:
obj = NewsItem( ... ) # a dummy empty NewsItem
return obj
Or you could simply not call super() at all, and supply your own code to return an appropriate object. The empty object might exist in the database as a special entity (best created by a data migration), or it might (as here) be created on the fly and never saved. In either case it would have some distinguishing characteristic that the template can easily test.