2

I am working with Django 3.1 ORM and am running (with pytest) a test involving a complex nested query. I get this failure:

self = <django.db.models.expressions.Subquery object at 0x0000027CE184D2B0>
lookup = 'lte'

    def get_lookup(self, lookup):
>       return self.output_field.get_lookup(lookup)
E       TypeError: get_lookup() missing 1 required positional argument: 'lookup_name'

That get_lookup(lookup) to be called is defined (as far as I can see) in django.db.models.query_utils.RegisterLookupMixin as

def get_lookup(self, lookup_name):
    ...

The corresponding source statement of my test involves something like

value = now() - OuterRef(OuterRef('myattr2')) * timedelta(days=1)
queryset2.annotate(has_x=Exists(queryset.filter(myattr__lte=value)))

and there is more query construction code around it.
My debugger tells me that self.output_field is a DateTimeField object.

So overall:

  • The context is an Exists subquery.
  • The lookup is 'lte' (as intended).
  • The call provides a DateTimeField as self (from output_field) and 'lte' as lookup_name.
  • The method-to-be-called expects self and lookup_name.

The call should work, shouldn't it?
Where is the TypeError?

Brian Destura
  • 11,487
  • 3
  • 18
  • 34
Lutz Prechelt
  • 36,608
  • 11
  • 63
  • 88
  • 2
    What are `myattr` and `myattr2`? I was able to reproduce similar error, the issue is that `self.output_field` is a class, not an instance as it should be, thus missing default `self` and as a result, missing `lookup_name` – Alexandr Tatarinov Jul 26 '21 at 18:23
  • Just curious, why the double `OuterRef` here? `OuterRef(OuterRef('myattr2'))` – Brian Destura Jul 27 '21 at 01:14
  • @bdbd The inner `OuterRef` escapes the `Exists` shown in the question. The outer `OuterRef` escapes another `Exists` _not_ shown in the question that is part of the "more query construction code around it" that I mentioned. – Lutz Prechelt Jul 27 '21 at 10:19

1 Answers1

1

The comment by @AlexandrTatarinov is right on; that was my problem as well; myattr is a computed attribute created thusly:

annotate(myattr=Subquery(queryset3,
                         output_field=DateTimeField))

But for some reason, Django firmly wants a field instance for the output field, not a field class. So output_field=DateTimeField() works, but my output_field=DateTimeField does not. Add the parentheses and my problem is solved.

Ironically, The output_field= is not even required in this case! Removing it works as well as adding the parens.

To reflect on my work process, these were my mistakes:

  1. I checked in the debugger that a proper self would be present -- but obviously not carefully enough, mistaking the DateTimeField class for a DateTimeField instance.
  2. I had inserted the output_field=DateTimeField clause while I debugged a previous problem, but did not take it out again when it did not help. Bad idea.
  3. When I wrote my question, I hesitated when I wrote "My debugger tells me that self.output_field is a DateTimeField object." and asked myself "Do I know it is a DateTimeField object and not the DateTimeField class?", but I did not go and look in the debugger again, I only recalled that I had checked it and had been satisfied.

So my take-home lessons are:

  • (from number 2): Don't do half-baked things.
  • (from numbers 1&3): Trust yourself as little as possible.
Lutz Prechelt
  • 36,608
  • 11
  • 63
  • 88