Search code examples
pythonpython-3.xdjangodjango-3.1

Django renders a template that does not exist in specified directory


I have model 'Video' and I have a list view for this model, then in another app I'm trying to create a view 'UserVideosListView' that is viewable to the dedicated user. when I open the page in the browser before making any templates for this view I'm able to see current user's videos (not all videos).

# inside 'users' app
class UserVideosListView(ListView):
    model = Video
    template_name = "users/user_videos.html"
    context_object_name = 'videos'

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['videos'] = Video.objects.filter(author=self.request.user)
        return context

# inside 'videos' app
class VideoListView(ListView):
    model = Video
    paginate_by = 25
    template_name = 'videos/video_list.html'
    context_object_name = 'videos'

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['videos'] = Video.published_videos.all()
        return context

P.S. I'm sure that the URL I'm entering is current, and there is no template inside the 'users' directory


Solution

  • Django renders videos/video_list.html because ListView is based on MultipleObjectTemplateResponseMixin and BaseListView:

    class ListView(MultipleObjectTemplateResponseMixin, BaseListView):
        """
        Render some list of objects, set by `self.model` or `self.queryset`.
        `self.queryset` can actually be any iterable of items, not just a queryset.
        """
    

    Within MultipleObjectTemplateResponseMixin (GitHub), the template name is also generated based on the model.

    # If the list is a queryset, we'll invent a template name based on the
    # app and model name. This name gets put at the end of the template
    # name list so that user-supplied names override the automatically-
    # generated ones.
    if hasattr(self.object_list, 'model'):
        opts = self.object_list.model._meta
        names.append("%s/%s%s.html" % (opts.app_label, opts.model_name, self.template_name_suffix))
    

    Django docs says MultipleObjectTemplateResponseMixin:

    template_name_suffix

    The suffix to append to the auto-generated candidate template name. Default suffix is _list.

    get_template_names()

    Returns a list of candidate template names. Returns the following list:

    • the value of template_name on the view (if provided)
    • <app_label>/<model_name><template_name_suffix>.html

    I suggest you to override get_queryset() method instead of get_context_data()

    class UserVideosListView(ListView):
        model = Video
        context_object_name = 'videos'
        ...
    
        def get_queryset(self):
            return super().get_queryset().filter(author=self.request.user))