2

Can someone please explain why this doesn't work

    class MansionDesign(Listview):
        sequence = Design.objects.filter(design_type__name='maisonette').order_by('created_at').reverse()
        queryset = [sequence[i:i + 3] for i in range(0, len(sequence), 3)]
        template_name = 'designs/mansions.html'

but when i change the above class to a subclass of view like below,

    class MansionDesign(View):
        def get(self, request):
            sequence = Design.objects.filter(design_type__name='maisonette').order_by('created_at').reverse()
            queryset = [sequence[i:i + 3] for i in range(0, len(sequence), 3)]
            return render(request, 'designs/mansions.html', {'object_list': queryset})

the code seems to work just fine. The error I get from the above class is 'name sequence is not defined'. I would appreciate some clarification on this. Thanks in advance.

Arpit Solanki
  • 9,567
  • 3
  • 41
  • 57
Joshua Karanja
  • 770
  • 1
  • 5
  • 9
  • Please add the full traceback to questions seeking debugging help. [mcve] – Håken Lid Jun 28 '17 at 11:28
  • The actual reason for this error is explained in the duplicate question. It's not anything specific to django, but a consequence of how scoping in class definitions work in python 3. – Håken Lid Jun 28 '17 at 11:57
  • This makes much more sense. After checking the error logs again i realized that 'sequence' was undefined in the scope of the function implementing the list comprehension. It doesn't receive the instance of the class in which the comprehension is created and thus cannot access its properties in this case 'sequence'. – Joshua Karanja Jun 28 '17 at 13:09

1 Answers1

2

Quotes from the docs:

ListView takes two params, one as queryset and second and template name.

Now if you try to define something else in the subclass of ListView you will get the error. If you do something like below:

queryset =[list(Design.objects.filter(design_type__name='maisonette').order_by('created_at').reverse())[i:i + 3] for i in range(0, len(list(Design.objects.filter(design_type__name='maisonette').order_by('created_at').reverse())), 3)]

in short - i removed the temperory variable you used as sequence. The above one will definitely won't work because its a list. The example was just to convey the point

Now the above one does not seem to be neat so if you have complex queries that you want to execute and show using listview then follow the examples below. You have to define a get_queryset method which will return the desired queryset object

class PublisherBookList(ListView):

    template_name = 'books/books_by_publisher.html'

    def get_queryset(self):
        self.publisher = get_object_or_404(Publisher, name=self.args[0])
        return Book.objects.filter(publisher=self.publisher)

Links:

https://docs.djangoproject.com/en/1.11/topics/class-based-views/generic-display/#generic-views-of-objects

Arpit Solanki
  • 9,567
  • 3
  • 41
  • 57