Xar
Xar

Reputation: 7940

Django ORM: customize a ListView, and add more information to its queryset

I have a view to list a certain model (lets call it class A), like this:

class BaseListView(ListView, MultipleObjectMixin):
    http_method_names = ['get']
    order_field = None

    def get_paginate_by(self, queryset):
        session_manager = SessionManager(self.request.session)
        return session_manager.paginate_by.get()

    def get_context_data(self, **kwargs):
        context = super(BaseListView, self).get_context_data(**kwargs)

        session_manager = SessionManager(self.request.session)
        session_manager.paginate_by.set_to(context)

        return context

This view did just what was needed, till now. Now I have to compare the list of objects it retrieves with another list of objects (class B).

The objects of class A and B both have a primary key with their name.

I want to check if any of the objects from class A has the same name (primary key) as any of the objects in class B. In case there is an instance of A in B I would like to add a certain parameter or something like is_in_B=True.

I need this so that I can represent these instances of A in a different way in the template.

How could I do this?

This is what I have come up with by myself for the moment:

class AListView(BaseListView):
    model = "A"

    def get_queryset(self):
        queryset = super(AListView, self). get_query_set()

        all_objects_A = A.objects.all()
        all_objects_B = B.objects.all()

        # modify queryset to indicate which instances of A are present in B
        # No idea how to do this

        return queryset

I'm not really sure this is an appropiate approach.

Also, how am I supposed to modify the queryset returned by my class so that I can indicate which instances of class A share the same name as any of the instances of class B?

Upvotes: 2

Views: 607

Answers (1)

solarissmoke
solarissmoke

Reputation: 31474

You can annotate your queryset with a conditional expression to achieve this:

from django.db.models import Case, When, Value

def get_queryset(self):
    # This gives you the queryset of A objects
    queryset = super(AListView, self).get_queryset()
    # List of primary keys of B objects
    all_objects_B = B.objects.all().values_list('pk',flat=True)

    # modify queryset to indicate which instances of A are present in B
    return queryset.annotate(
        is_in_b=Case(When(pk__in=all_objects_B, then=Value(True)),
                       default=Value(False))
        )
    )

Your queryset objects will now have an is_in_b property.

This will work fine if your list of B objects is small. If it is large then I am not sure it is very efficient, and you may need to develop this further to see whether the check (is A in B) can be done directly in the database (possibly requiring raw SQL).

Upvotes: 1

Related Questions