Common pattern for prefetched and filtered related objects.

Hi everyone,

I’ve a question about best practices around prefetching. Let’s assume a basic model like the following (untested so there might be typos):

class A(models.Model):
    name = models.CharField(max_length=30)
    included = models.BooleanField(default=False)
    bees = models.ForeignKey("B", on_delete=models.CASCADE, related_name="reverse_as")

class B(models.Model):
    name = models.CharField(max_length=50)
    
   def get_included_as(self):
         return self.reverse_as.filter(included=True)

For reference I’m using DRF, so the “problem” happens at serialization time.

Now in my views I want to prefetch As to avoid cascading queries for each Bs something akin to:

B.objects.filter(...).prefetch_related(Prefetch("reverse_as", A.objects.all().filter(included=True)))

Now if my serializer (which I want to be generic as much as possible) does just B.get_included_as django will hit again the DB despite the query being prefetched, that’s expected and documented behavior, because I do filtering on the resulting queryset.

My solution is to use to_attr on the Prefetch object and have my serializer access the field conditionnally something like:

B.objects.filter(...).prefetch_related(Prefetch("reverse_as", A.objects.all().filter(included=True), to_attr="prefetched_as"))

if has_attr(obj, 'prefetched_as'):
  return obj.prefetched_as
else:
   # defer to the generic accessor :
   return obj.get_included_as()

Now my question is, the pattern is quite simple and easy to follow, but is there a better / more canonical way of dealing with this?

Thanks in advance.