Peter Marek
Peter Marek

Reputation: 31

What is "output_field" in django

Django doc says for aggregate functions:

output_field - An optional argument that represents the model field of the return value

So I expected that the code below returns "True".

MyModel.objects.annotate(foo=Max(Value("yes"), output_field=BooleanField())).first().foo

or

MyModel.objects.annotate(foo=Max("id", output_field=BooleanField())).first().foo

But string "yes" is returned in the first case and some integer in the second one.

I know I can use "Cast", but can somebody tell me, what is "output_field" good for in this case?

Upvotes: 3

Views: 3163

Answers (2)

NeilG
NeilG

Reputation: 4160

Search through the doc page on expressions to see many examples: https://docs.djangoproject.com/en/3.2/ref/models/expressions/

  • The output_field parameter is to resolve conflicting or ambiguous types in an expression, if Django does not have sufficient heuristics to determine by itself.
  • Django offers the output_field as a route for you to tell it what output type you actually want.
  • The output_field parameter is not so much to allow you to cast the type of your values, it's to help Django resolve the output type amongst competing options.
  • The output_field parameter will not always be required, but sometimes it will resolve ambiguity which would otherwise cause Django to raise an exception.

Looking at your examples, I can only presume that Django disregards your output_field in these cases.

With respect to the different output between the two, Django treats the bare string "id" as a field name. If you wrapped the "id" string with Value("id") like you did for "yes", you would see a return of "id" instead. And if you removed the Value wrapper from your "yes" example you would see: FieldError: Cannot resolve keyword 'yes' into field. Choices are: ...

As a more involved example to indicate how the output_field parameter can be useful, consider MS SQL Server, which does not have a proper Boolean type, and uses bit fields for the purpose instead.

Attempting queryset = queryset.annotate( foo=Case(When(name__icontains='XYZ', then=Value(True)), default=Value(False)) ) gives django.core.exceptions.FieldError: Cannot resolve expression type, unknown output_field Even though you've submitted two, unambiguously Boolean values, Django cannot find the right type to return.

This is what the output_field is for. You can resolve the ambiguity for Django by providing the type you want to use: queryset = queryset.annotate( foo=Case(When(name__icontains='XYZ', then=Value(True)), default=Value(False), output_field=BooleanField()) ) Apparently Django is happy to try that but can't complete it under MS SQL Server: django.db.utils.ProgrammingError: ('42000', '[42000] [FreeTDS][SQL Server]Statement(s) could not be prepared. (8180) (SQLExecDirectW)')

So to really help Django out you need to know a bit [sic] more than Django and supply: queryset = queryset.annotate( foo=Case(When(name__icontains='XYZ', then=Value(True)), default=Value(False), output_field=BinaryField()) ) (Works).

To apply this to your example: MyModel.objects.annotate(foo=Max(False, output_field=BooleanField())).first().foo returns False.

And even using a numeric value, Django is able to interpret the value according to your supplied output_field: MyModel.objects.annotate(foo=Max(0, output_field=BooleanField())).first().foo returns False.

But as we saw, although Django can handle this, MS SQL Server will not tolerate a boolean value, because it doesn't have them; so you may find you need to do this in the particular case of MS SQL Server: MyModel.objects.annotate(foo=Max(0, output_field=BinaryField())).first().foo which returns 0.

So the output_field parameter provides this additional control.

Upvotes: 1

Ihor Pomaranskyy
Ihor Pomaranskyy

Reputation: 5611

In some cases Django needs to know the type of data in some field explicitly.

For example, in case you'll try to feed results of such queryset into some serializer (from Django REST Framework), you'll get an error and will be forced to provide output field.

Upvotes: 0

Related Questions